Warning C4251 - Chicken & egg problem

B

Bob Altman

I have a class that is exported from a DLL. This class includes a private
std::vector containing pointers to instances of the class, like this:

class MYCLASS_API MyClass {
private:
vector<MyClass*> m_list;
}

When I compile I get the dreaded Warning C4251: ... needs to have
dll-interface to be used by clients of class...

If m_list was a vector of standard types (such as int) then the solution
would be to modify the code like this (stolen from
http://support.microsoft.com/kb/q168958/):

#ifdef EXP_STL
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif

// Instantiate vector<int>
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<int>;

class MYCLASS_API MyClass {
private:
// Note: Changed from preceding example
vector<int> m_list;
}

My problem is that m_list is a vector of MyClass*, and I don't know to
forward-reference it in the vector instantiation. In other words:

#ifdef EXP_STL
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif

// This doesn't compile!
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<MyClass*>;

class MYCLASS_API MyClass {
private:
vector<MyClass*> m_list;
}

Help!

TIA - Bob
 
D

Doug Harrison [MVP]

I have a class that is exported from a DLL. This class includes a private
std::vector containing pointers to instances of the class, like this:

class MYCLASS_API MyClass {
private:
vector<MyClass*> m_list;
}

When I compile I get the dreaded Warning C4251: ... needs to have
dll-interface to be used by clients of class...

If m_list was a vector of standard types (such as int) then the solution
would be to modify the code like this (stolen from
http://support.microsoft.com/kb/q168958/):

#ifdef EXP_STL
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif

// Instantiate vector<int>
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<int>;

class MYCLASS_API MyClass {
private:
// Note: Changed from preceding example
vector<int> m_list;
}

My problem is that m_list is a vector of MyClass*, and I don't know to
forward-reference it in the vector instantiation. In other words:

#ifdef EXP_STL
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif

// This doesn't compile!
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<MyClass*>;

class MYCLASS_API MyClass {
private:
vector<MyClass*> m_list;
}

Help!

In general, I'd forget about that approach to silencing the warning. The
only time it's ever necessary is when the class template has static data.
In that situation, it will help ensure there is only one instance of the
static data throughout the process, to the extent that everyone who uses
the class #includes the header that contains the explicit instantiation
statement and links to the DLL that contains the instantiation. That can be
hard to enforce, but fortunately it isn't an issue for you, as std::vector
has no static data.

For all other cases, the best approach to silencing the warning is #pragma
warning(disable). Here's why. First, there is the aforementioned difficulty
of enforcing the notion that there are a number of specializations that are
explicitly instantiated and exported, which must be obtained only by
#including a certain header file and linking to (hopefully) a single DLL;
if you have multiple DLLs trying to implement this, you are really in
trouble. Second, if you are doing everything you need to successfully share
classes by exporting them in this way[*], then for templates without static
data, it hardly matters that there are different instantiations in
different modules. They will all work the same, and you would have to write
a very contrived program to detect that you had done this. Offhand, it
would have to depend on the address of function or type_info objects being
the same across the process; the former would be a pretty bizarre
dependency to create, and I've avoided the latter by textually comparing
raw_name()'s, which is a VC extension to std::type_info. Ironically, I've
done the latter exactly once, while developing a solution to the template
static data problem. There is also the potential for code bloat and
performance degradation, but you would have to get into a truly
pathological situation for them to become significant issues.

[*] To export whole classes, the module that defines it and all the ones
who use it must link to the same CRT DLL and more generally use the same
compiler and compiler options to the extent that it matters. If you treat
it as equivalent to static linking WRT these things, then you'll be OK. The
only real thorn is template static data, and the "recommended" solution to
that problem is fragile as previously discussed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top