Dealing with "needs to have dll-interface to be used by clients of class"

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

I like warning free code, especially for a dll, these warnings are the only left right now I have to deal with. I am not sure how to deal with them however. If someone could point me in the right direction that would be great.


std::basic_string<_Elem,_Traits,_Ax>
std::map<_Kty,_Ty>
std::vector<_Ty>
std::list<_Ty>'
std::_List_iterator<_Mylist>

Update:

So to compound this, I get this error when setting up my .exe project:


Error 49 error C2491: 'ParticlesEngine::Singleton<T>::instance' : definition of dllimport static data member not allowed

The Code:


#ifndef _SINGLETON_H
#define _SINGLETON_H

#define NULL 0


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

namespace ParticlesEngine
{

	template <typename T>
	class PARTICLESENGINE_API Singleton
	{
	public:
		Singleton<T>(void)
		{    
		}
 
		static inline T* getInstance(void) 
		{ 
			if(instance == NULL) 
			{
				instance = new T();
			}
			return instance;
		} 

		static inline bool exists( void ) { return instance != 0; }
   
		virtual ~Singleton( void ) { instance = 0; }

	protected:
		static T* instance;
	};

	template <typename T>
	T* Singleton<T>::instance = 0;

}

#endif

Advertisement

Warning C4251. You have a DLL interface that depends on code that is compiled externally, is statically linked into the DLL & EXE, and as a result you may have a version mis-match between the data structure used in the DLL, vs the 'same' (but possibly not) data structure used in the exe.

In a nutshell, if your DLL links to the release CRT, and then returns a std::string. That string will be allocated in the DLL, and may end up being freed/reallocated in the EXE. Even worse, the data sizes of the STL containers may change size (i.e. iterator debugging info), which can cause all manner of buffer overruns and other niceties.

To avoid this warning, you have two choices:

a) Don't use STL,

b) Don't use STL.

There is a third option, which is to simply ignore the warning. The warning is only a problem when trying to link a debug build DLL with a release build app (or visa-versa). If both EXE & DSO are linking to the same CRT, then there is no problem.

As for you other problem, stop putting the class implementations as non-inlined parts of a header file. This won't work with DLLs. The second you attempt to link to the DSO, the static variable is declared as a new global variable in the executable, and it's declared as being "dllimport", which is WRONG. It needs to be declared once in your DLL as "dllexport". Source files are there for a reason! Use them more! :p

Alright I kinda though that, I will go edit my headers to correct that issue.

About stl, I can switch to char instead of string for most of it, it will take a bunch of work, but how can I replace vector and map and list which I use alot without having to write my own.

Thanks

[Actually there is another way to avoid the STL problem..... Don't expose in in the DSO interface]

class Foo { public: EXPORT Foo(); EXPORT Foo(const Foo&); EXPORT ~Foo(); EXPORT const char* name() const; EXPORT void setName(const char* n); private: std::string m_name; };

And just to keep the pedants at bay, you can finalise the class if you really want to avoid the problem of 'Foo' being of an unknown size (although disabling SECURE_CSL and building the DSO in release normally avoids all but the most extreme edge cases).

class Foo { Foo(const Foo&); public: EXPORT static Foo* create(); EXPORT ~Foo(); EXPORT const char* name() const; EXPORT void setName(const char* n); private: std::string m_name; };

Alright I kinda though that, I will go edit my headers to correct that issue.

About stl, I can switch to char instead of string for most of it, it will take a bunch of work, but how can I replace vector and map and list which I use alot without having to write my own.

Thanks

Stop exporting the entire f***ing class! Exporting the entire class is really bad practice. It will attempt to export ALL of your member variables, private functions, inline functions, etc. Just export the public functions you need, and be done with it (but remember to always export the ctor, copy ctor, dtor; and also make sure that your ctor is virtual if you intend to use it as a base class. This isn't 'good practice', it's required).

You can pass simpler forms of container across the boundary provided their layout and size remain constant. Pass this class by value across the DLL boundary, AND DO NOT EXPORT IT!! (It's inline for a reason!)

template< typename T >
class ConstVector
{
public:

inline ConstVector( const std::vector<T>& data )
: m_begin(0), m_end(0)
{
if(data.size())
{
m_begin = &data[0];
m_end = m_begin + data.size();
}
}

inline ConstVector( const ConstVector<T>& data )
: m_begin(0), m_end(0)
{
}

inline ~ConstVector()
{
}

inline size_t size() const
{
return m_end - m_begin;
}

inline const T* begin() const
{
return m_begin;
}

inline const T* end() const
{
return m_end;
}

inline operator std::vector<T> () const
{
return m_begin ? std::vector<T>(m_begin, m_end) : std::vector<T>();
}

private:
const T* m_begin;
const T* m_end;
};

(I've not actually tested that code, but it should be about right..... )

[quote name='RobTheBloke' timestamp='1358442286' post='5022571']
Stop exporting the entire f***ing class! Exporting the entire class is really bad practice. It will attempt to export ALL of your member variables, private functions, inline functions, etc. Just export the public functions you need, and be done with it
[/quote]

Specify your interface to the library. This is true any time you cross a boundary (such as a dll or even a networking protocol).

[quote name='RobTheBloke' timestamp='1358441477' post='5022564']
a) Don't use STL,
b) Don't use STL.
[/quote]

Icky.

Use the standard library. Use it and embrace it. The library is incredibly useful.

DLLs have a very clear type of interface. That interface passes buffers of data, not c++ objects.

It isn't that much different than any other boundary, such as a network boundary or file system boundary; you don't just dump the entire class but instead serialize the data into a boundary-crossing interface.

a) Don't use STL,
b) Don't use STL.

Icky.

Use the standard library. Use it and embrace it. The library is incredibly useful.


15 years ago I had that opinion, but after a far too many years maintaining the plug-in architectures for a number of 3D apps, I've developed a lot of contempt for the design of STL allocators & containers (the algorithms are fine, if a little inefficient in places). Yes you can approach everything with a serialise/deserialise mindset, but it just leads to performance hot spots in my experience (or more accurately, it just makes everything a little bit warm all over). If at some point down the line you need to resolve the performance issues, it becomes a real PITA. For any DSO heavy application, it's going to be far easier to simply restrict yoruself to using your own containers (which are of a known size & implementation that you can easily pass across the boundary directly). STL just gets in the way of that imho. I've yet to see a single DLL whose quality is improved by the presence of STL..... Most of the time, it simply encourages junior developers to make silly mistakes in the API (for example, forgetting that returning const reference to a std::vector is bad news). My 2 cents.

Alright there is a lot of information above I am working on understanding, thank you all for your help so far.

So to not export the entire class I need to add "EXPORT" in front of everything I want to export in the header files. However I shouldn't add EXPORT to functions that are Inline? In addition I should change all my functions that take std::string as a parameter to char, and then keep the std::string's internal or get rid of them all together.

To the other issue with static data members, I moved the Singleton<T>::instance to the c++ file, now I get Linker errors for each class which is a singleton, which then causes more issues.

I am not sure how to solve this:

My Singleton c++ just to make sure I did make any errors.


#include "Singleton.h"

using namespace ParticlesEngine;

template <typename T>
T* Singleton<T>::instance = 0;

You need to export EACH specialisation to the DLL. You cannot export a template to a DLL.

template class DLL_EXPORT Singleton<FooType1>;

You need to export EACH specialisation to the DLL. You cannot export a template to a DLL.

template class DLL_EXPORT Singleton<FooType1>;

Something like this, but this doesn't work.


#include "Singleton.h"

#include "GameInfo.h"
#include "Logger.h"
#include "SoundManager.h"
#include "SceneManager.h"
#include "ResourceManager.h"

using namespace ParticlesEngine;

template class DLL_EXPORT GameInfo* Singleton<GameInfo>::instance = 0;
template class DLL_EXPORT Logger* Singleton<Logger>::instance = 0;
template class DLL_EXPORT SoundManager* Singleton<SoundManager>::instance = 0;
template class DLL_EXPORT SceneManager* Singleton<SceneManager>::instance = 0;
template class DLL_EXPORT ResourceManager* Singleton<ResourceManager>::instance = 0;

This topic is closed to new replies.

Advertisement