Issues with maps and TR1::function in DLLs

Started by
8 comments, last by XaserIII 12 years, 9 months ago
Hi!

I'm currently working on a DLL which uses std::map container and TR1::Function. Somehow however, I get loads of errors when i try to compile it.

It basically looks like

#include <map>
#include <functional>

class __declspec(dllexport) Game : public Singleton<Game>
{
private:
std::map<int, std::tr1::function<void (void)>> HandleMap;

public:
Game()
{
HandleMap[QUESTCOMPLETE] = std::tr1::bind(&MyGame::Game::OnAddMoney, this, std::tr1::placeholders::_1);
//....
}
}


So. when i try to compile this, first of all i get the warning C4251 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' for the std::map declaration.
An then load of errors in the xxresult file, which obviously I have to cause somehow. Does anyone have an idea why?

Xaser
Advertisement
Passing standard library classes across DLL boundaries is evil.

The reason for this is that you may well have another DLL, or EXE, or whatever, which uses a different version of the standard library. This can happen even with the "same" compiler - different service packs for Visual Studio, for instance, introduce changes to the standard library implementation.

Suppose I pass a Version X std::map into a DLL. It's defined to accept a std::map, so all compiles cleanly and all appears to be well.

Then I update my compiler, or give my code to someone else, or whatever. The DLL gets compiled again, this time expecting std::map Version Y. I run my program, and Version Y tries to do something new that Version X didn't support. In the absolute best case my program will crash. Far more likely, and far more deadly, is the possibility that the program will continue to run but silently do things incorrectly - leak memory, corrupt data, summon cat-demons from the underworld using a portal generated by the rotations of the hard drive... you name it. The behaviour at this point is what is technically referred to as "undefined" - which is a euphemism for "ohhhh shit."


As the documentation says, don't do this. Expose minimal interfaces between your DLLs which rely only on guaranteed things like integers, char*s, and so on. Then re-wrap those back into standard library classes internally, where appropriate.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

pewwww.. okay^^... thanks

So what that basically means is never export anything that contains std library classes or at least "make it safe" as you said, wrap it etc.
So what I did was I rewrote some parts and don't export the whole class now. In fact I have only one function left that is exportet, which is a install hook function. Yes this function again calls a function from that class i posted above but this should be fine shouldn't it?
Suprisingly I still get these xxresult.cpp errors?! They are definitely related to the tr1::function use but I don't see any problems with that.
Anymore Ideas?

Xaser
The same caveat applies to anything exported across DLL boundaries; I used std::map as an example.

For instance, you can't pass a std::string across boundaries safely. Instead, pass a const char* and use the std::string::c_str() function at the call site, then re-wrap to a string internally in the DLL.

Function objects are analogous. You can't pass a tr1::function across boundaries safely. In this case, the corresponding way to be safe is to use a raw function pointer.

If you need state attached to the function call (i.e. you're using a genuine functor instead of a free function as the callback/hook function) then pass a raw pointer to the state as a parameter of the function. Win32 APIs typically do this with something like an LPVOID, for instance; however, don't use void* if you can help it. Pass a HookState* struct or something.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Yea of course, but as I said, the std::TR1::functions are not exported directly


extern "C"
__declspec(dllexport) LRESULT HookProc(int code, WPARAM key, LPARAM info)
{
CallNextHookEx(NULL, code, key, info); //Immediately pass on

gGameDLL = GetModuleHandle("Game.dll");
if(gGameDLL == 0x00)
return 0x01;

sGame.HandleKey(key);

return 0;
}


Its just the call. And I just tried it out: I removed the __declspec and extern "C" and the error was still there! Which means that this error has nothing to to with exporting.. :/

Xaser
If a member variable is part of a class definition that you use __declspec(dllexport) then it becomes part of the exported definition - even if it's a private member variable. My advice is not to export concrete types in DLL interfaces at all. Just expose an abstract base class.
errr right... as I said it can't be a problem of exporting, because nothing is exported at the moment, i have removed all export declines and the error is still there?

Xaser
At this point I've lost track of what changes you've made to the code and what the actual error even is; in the absence of this information, there's really nothing we can do but speculate wildly.

If you can post the current code you have (preferably a minimal example that shows the error) along with the actual, exact copy of all the error messages you're getting, then we'll see what we can do :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Yea I know, I was just lazy translating from german to english :P

okay here we go thats the two lines where the errors occur, they are in the xxresult, which are I think included with "functional"


template<class _Fty
_C_CLASS_ARG0>
struct _RESULT_TYPE<false, _Fty _C_ARG0_ARG1>
{ // define return type for UDT without nested type named result_type
#if _NARGS == 0
typedef void _Type;

#else /* _NARGS */
typedef typename _Fty::template result<_Fty(_ARG0_ARG1)>::type _Type;
#endif /* _NARGS */
};

// TEMPLATE CLASS _Result_of
template<class _Fty
_C_CLASS_FARG0>
struct _RESULT_OF
{ // UDT
static const bool _Value = _HAS_RESULT_TYPE(_Fty);
typedef typename _RESULT_TYPE<
_RESULT_OF<_Fty _C_FARG0_FARG1>::_Value,
_Fty _C_FARG0_FARG1>::_Type _Type;
};


and this is what I used of functional

HandleMap[QUESTCOMPLETE] = std::tr1::bind(&MyGame::Game::OnAddMoney, this, std::tr1::placeholders::_1);

Yes it is in a class, but nothing is exported or anything, just a normal DLL

and these are the errors


1>error C2825: "_Fty": must be a class or namespace when followed by '::'
1>error C2903: "result": symbol is neither a class template nor a function template
1>error C2039: 'result': is not a member of '`global namespace''
1>error C2143: Syntaxerror: missing token ';' before '<'
1>error C2039: 'type': is not a member of '`global namespace''
1> error C2238: Unexpected token before ';'
1>error C2039: '_Type': is not a member of 'std::tr1::_Result_type2<__formal,_Fty,_Arg0,_Arg1>'
1> Singleton.cpp[/quote]

Roughly translated. Really need to get an english language pack, why did microsoft remove it!?

Thanks, Xaser
Finally managed to fix the problem myself.

The problem was located in the std::bind call:

HandleMap[QUESTCOMPLETE] = std::tr1::bind(&MyGame::Game::OnAddMoney, this, std::tr1::placeholders::_1);

Everytime I used it I had a function which had one parameter. Therefore I got used to always ad a placeholder, which in this case led to the error as the Function OnAddMoney has no parameter.


Hope this helps also in other cases!
Xaser

This topic is closed to new replies.

Advertisement