LNK2019:unresolved external symbol with my own dll

Started by
5 comments, last by harshman_chris 11 years, 3 months ago

So this is the error I am getting when using part of dll. There is quite a bit of code involved in this one.

I know this is a messy error mesage, sorry.


Error	175	error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall ParticlesEngine::Event<class ParticlesEngine::EventData>::operator+=(class ParticlesEngine::Delegate<class ParticlesEngine::EventData> *)" (__imp_??Y?$Event@VEventData@ParticlesEngine@@@ParticlesEngine@@QAEXPAV?$Delegate@VEventData@ParticlesEngine@@@1@@Z) referenced in function "public: virtual void __thiscall TestScene::initialize(void)" (?initialize@TestScene@@UAEXXZ)	C:\Users\100312531\Desktop\Project-Seventeen\ParticleEngineProject\EngineTest\TestScene.obj	EngineTest

Here is all the code segements, if I am missing anything, or I need to answer a question let me know.



//TestScene.h #include <Delegate.h> #include <EventData.h> #include <Event.h> class TestScene : Scene { void OnButtonClick(EventData data); void OnButtonEnter(EventData data); void OnButtonLeave(EventData data); EventHandler(TestScene, OnButtonClick, EventData); EventHandler(TestScene, OnButtonEnter, EventData); EventHandler(TestScene, OnButtonLeave, EventData);

}


//TestScene.cpp

void TestScene::initialize()
{
button->OnClick += &EOnButtonClick;
button->OnEnter += &EOnButtonEnter;
button->OnLeave += &EOnButtonLeave;
}

void TestScene::OnButtonClick(EventData data)
{
}

void TestScene::OnButtonEnter(EventData data)
{
}

void TestScene::OnButtonLeave(EventData data)
{
}

The Code from the dll, the custom event system I am using, it is pretty straight forward.


#ifndef _EVENT_H
#define _EVENT_H

#include <vector>
#include <algorithm>
#include "Delegate.h"

#ifdef PARTICLESENGINE_DLL 
#define PARTICLESENGINE_API __declspec( dllexport )
#else
#define PARTICLESENGINE_API __declspec( dllimport )
#endif

namespace ParticlesEngine
{

	template <typename T>
	class PARTICLESENGINE_API Event
	{
		public:
			inline void operator+=(Delegate<T>* delegate)
			{
				// An object can only subscribe once
				if (find(_delegates.begin(), _delegates.end(), delegate) == _delegates.end())
				{
					_delegates.push_back(delegate);
				}
			}

			inline void operator-=(Delegate<T>* delegate)
			{
				typedef typename std::vector< Delegate<T>* >::iterator iter;
				iter i = _delegates.begin();
				while (i != _delegates.end())
				{
					if (*i == delegate)
					{
						i = _delegates.erase(i);
					}
					else
					{
						++i;
					}
				}
			}

			inline void operator()(T param)
			{
				typedef typename std::vector< Delegate<T>* >::iterator iter;
				for (iter i = _delegates.begin(); i != _delegates.end(); ++i)
				{
					(*i)->operator()(param);
				}
			}

		private:
			std::vector< Delegate<T>* > _delegates;
	};

}

#endif

#ifndef _EVENTDATA_H
#define _EVENTDATA_H

#ifdef PARTICLESENGINE_DLL 
#define PARTICLESENGINE_API __declspec( dllexport )
#else
#define PARTICLESENGINE_API __declspec( dllimport )
#endif

namespace ParticlesEngine
{

	class EventData
	{
	public:
		PARTICLESENGINE_API EventData()
		{
		}
	};

}

#endif

#ifndef _DELEGATE_H
#define _DELEGATE_H

#ifdef PARTICLESENGINE_DLL 
#define PARTICLESENGINE_API __declspec( dllexport )
#else
#define PARTICLESENGINE_API __declspec( dllimport )
#endif

namespace ParticlesEngine
{

	#define EventHandler(thisType, handler, type)\
		class __E##handler##__ : public Delegate< type >\
		{\
			public:\
				__E##handler##__ ( thisType * obj )\
				: _obj(obj) {}\
				inline void operator()( type param )\
				{\
					_obj-> handler (param);\
				}\
				thisType * _obj;\
		};\
		__E##handler##__ E##handler;

	template <typename T>
	class PARTICLESENGINE_API Delegate
	{
	public:
		virtual void operator()(T param) = 0;
	};

}

#endif
Advertisement

You are still exporting too much from the DLL.

The DLL boundary is well-defined. It doesn't support templates well. It doesn't support managed memory.

It is designed around a hybrid Pascal based and C based interface.

Export individual global functions, not functions from a class and definitely not templates, and only use raw memory blocks if they need to transfer memory.

I am not sure I fully understand what you are saying. Are there any good tutorials that could walk me through this or is there a way I fix this problem.

When you say export only global functions how does that work in an OO structure?

I come from a heavy C# background, I have not been using C++ for 8 months.

Edit:

I removed the PARTICLESENGINE_API from the Event class and that solved my linker issue, I know I have a long way to go with dll's any advice or tutorials would be great.

This article looks fairly in-depth. There are a lot of nuanced details, so don't gloss over them.

The concepts behind a DLL come from the early 1980s. You really need to understand how and why they did things back then in order to leverage DLL interfaces.

This article looks fairly in-depth. There are a lot of nuanced details, so don't gloss over them.

The concepts behind a DLL come from the early 1980s. You really need to understand how and why they did things back then in order to leverage DLL interfaces.

Thanks

Export the specialisation.....


template class PARTICLESENGINE_API Event<EventData>;

or export the specialised funcs manually,


template PARTICLESENGINE_API Event<EventData>::Event();
template PARTICLESENGINE_API Event<EventData>::operator () (EventData p);

.... but make sure you always export the ctor/copy ctor/dtor if you go down that route.

You probably don't want to be exporting template classes like this though. Those inline operators cannot be inline, they access internal data. Pure virtual classes do not need to be exported. Exporting an empty constructor is just plain silly. I somewhat suspect you may find your design to be a little bit annoying when it comes to using it with a DLL though. I'd probably be inclined to suggest nice simple, clean, fully encaspulated C++ objects at the core DLL layer, with templates as a way to quickly derive new classes from a concrete base type if needed.

frob is talking about using dlsym/GetProcAddress to extract the DLL symbols manually, which will need C (or C++ name mangling and some ASM). If you are using an import library (i.e. linking to a .lib), then the name unmangling is handled for you. Understanding the C aspects is vital to fully understanding the C++ nuances.

Generally templates are much easier to deal with if they're in a static library. As part of a DLL interface, they tend to cause lots of annoying linking problems (as you are finding out)

Thanks, I may consider a static library, its just a little foreign to me from my C# background where all of this stuff was entirely handled for me.

I am not two worried about getting it perfect right now, next year in school I have a Game Engine course which should help clear up all this stuff.

A side question, how do I step into my dll in visual studio 2010, is there a way?

There is a pdb file generated, along with the dll, but my output says it cannot find it.

This topic is closed to new replies.

Advertisement