exporting a C++ object in a DLL

G

Grey Alien

I have a class that contains a std::map variable. I need to export the
class via a DLL. the class looks something like this:

class MyClass
{
public:
MyClass();
MyClass(const MyClass&);

private:
MyClass& operator=(const MyClass&);

typedef std::map<SomeKey, SomethingElse> TreasureChest ;

TreasureChest m_treasures;
};
 
G

Grey Alien

QbProg said:
you can either __declspec(dllexport) every member of the class that
you want to export, or __declspec(dllexport) the class definition.

In the "dll user" code you should declare the class with
__declspec(dllimport). There are standard ways to do that using
macros. See
http://www.codeproject.com/dll/SimpleDll2.asp

or
http://msdn2.microsoft.com/en-US/library/a90k134d(VS.80).aspx

Good Bye
QbProg

See: http://support.microsoft.com/kb/168958

Relevant text: The only STL container that can currently be exported is
vector. The other containers (that is, map, set, queue, list, deque) all
contain nested classes and cannot be exported.

The article was last reviewed in September 2005 - I wanted to know if it
is now possible to export std::map from a DLL
 
B

Ben Voigt [C++ MVP]

Grey Alien said:
I have a class that contains a std::map variable. I need to export the
class via a DLL. the class looks something like this:

No you don't. Create an interface (class with pure virtual pointers),
derive the implementation from it, and share only the interface. You do
that by putting the interface definition in a public header file. No
__declspec(dllexport) statement is needed.

Exporting C++ classes is very bad news. __declspec(dllexport) should be
used only for 'extern "C"' functions.
 
G

Grey Alien

Ben said:
No you don't.

Yes I do. I know what I want.

Create an interface (class with pure virtual pointers),
derive the implementation from it, and share only the interface. You do
that by putting the interface definition in a public header file. No
__declspec(dllexport) statement is needed.

You are assuming that the DLL will be consumed by a C++ client. That is
not the case. Besides, how can you possibly use code in another
compilation unit if you don't link into it (either statically or
dynamically).?
Exporting C++ classes is very bad news. __declspec(dllexport) should be
used only for 'extern "C"' functions.

Not necessarily true. In my case, I am taking care of the C++ "name
mangling" - through various policies and procedures (didn't include info
because it is orthogonal to my original question).
 
G

Grey Alien

David said:
Grey:

You want to export a class containing an std::map and it will not be
consumed by a C++ client?

Ben's advice might have seemed a little fierce, but basically I agree
with it. You will save yourself a lot of future headaches if you design
your class with a pure virtual interface (and methods using simple types).

Dave:

That may be the case, but the fact remains that the class whose methods
are invoked needs to contain a map member variable. At the moment, I'm
getting this annoying warning:

warning C4251: 'theManager::m_signalMap' : class 'std::map<_Kty,_Ty>'
needs to have dll-interface to be used by clients of class 'theManager'

- which seems to imply that std::map can be exported - which contradicts
the (outdated) article on the MSN site. So do I heed the warning and
export the data type (preferred) or do ignore it (with potentially
disastrous consequences)?
 
D

David Wilkinson

Grey said:
You are assuming that the DLL will be consumed by a C++ client. That is
not the case. Besides, how can you possibly use code in another
compilation unit if you don't link into it (either statically or
dynamically).?

Grey:

You want to export a class containing an std::map and it will not be
consumed by a C++ client?

Ben's advice might have seemed a little fierce, but basically I agree
with it. You will save yourself a lot of future headaches if you design
your class with a pure virtual interface (and methods using simple types).
 
B

Ben Voigt [C++ MVP]

Grey Alien said:
Yes I do. I know what I want.

Create an interface (class with pure virtual pointers),

You are assuming that the DLL will be consumed by a C++ client. That is
not the case. Besides, how can you possibly use code in another
compilation unit if you don't link into it (either statically or
dynamically).?

If your client isn't C++, the prohibition on __declspec(dllexport) of
classes becomes absolutely instead of just a really good idea.

What do you mean by "don't link into it"? You are creating a DLL, right?
"dynamically linked library" If you create an interface, then the compiler
links the interface v-table to the implementations. The client only needs
the interface definition.
 
B

Ben Voigt [C++ MVP]

Grey Alien said:
Dave:

That may be the case, but the fact remains that the class whose methods
are invoked needs to contain a map member variable. At the moment, I'm
getting this annoying warning:

warning C4251: 'theManager::m_signalMap' : class 'std::map<_Kty,_Ty>'
needs to have dll-interface to be used by clients of class 'theManager'
- which seems to imply that std::map can be exported - which contradicts
the (outdated) article on the MSN site. So do I heed the warning and
export the data type (preferred) or do ignore it (with potentially
disastrous consequences)?

There is no contradiction, both are correct. std::map should not be
exported, and because of that, it should not be used by clients of
"theManager". Since it is a private implementation detail, why would that
present a problem? Clients aren't using it.
 
G

Grey Alien

Sounds like you're describing the Pimpl pattern. I haven't used it
myself before, though it does sound like a good idea (I can't use it in
my current project though - because legacy code already exports classes).

However, as a matter of interest, if only the interface is specified
(via pure virtuals in a header) - surely, it means that EACH child class
will have to implement the same functionality allover again. The idea
for exporting classes was for code reuse - which you seem to be losing,
using the Pimpl pattern you describe. Am I missing something?
 
D

David Wilkinson

Grey said:
Sounds like you're describing the Pimpl pattern. I haven't used it
myself before, though it does sound like a good idea (I can't use it in
my current project though - because legacy code already exports classes).

Grey:

Pimpl and Abstract Base Class (ABC) are not the same thing. A pimpl
class is not a pure interface.
 
B

Ben Voigt [C++ MVP]

Grey Alien said:
Ben Voigt [C++ MVP] wrote:


I have a class that contains a std::map variable. I need to export the
class via a DLL. the class looks something like this:



No you don't.


Yes I do. I know what I want.

Create an interface (class with pure virtual pointers),

derive the implementation from it, and share only the interface. You
do that by putting the interface definition in a public header file.
No __declspec(dllexport) statement is needed.

Sounds like you're describing the Pimpl pattern. I haven't used it myself
before, though it does sound like a good idea (I can't use it in my
current project though - because legacy code already exports classes).

However, as a matter of interest, if only the interface is specified (via
pure virtuals in a header) - surely, it means that EACH child class will
have to implement the same functionality allover again. The idea for
exporting classes was for code reuse - which you seem to be losing, using
the Pimpl pattern you describe. Am I missing something?

Yup. The interface can be implemented in a base class which provides the
implementation, and each derived (or child if you prefer) class inherits.
Only the interface cannot have any implementation, but it can participate in
a full hierarchy.
 
G

Grey Alien

Ben said:
Yup. The interface can be implemented in a base class which provides
the implementation, and each derived (or child if you prefer) class
inherits. Only the interface cannot have any implementation, but it can
participate in a full hierarchy.

But if the interface is implemented in a base class, then that base
class needs to be exported, so that its derived classes can correctly
link to the approriate compilation units - so we end up in the same
situation, i.e having to export the (base) class.

Unless I am misunderstanding you, your approach ensures that if we have
a Widget class (say), we will make its interface available as an ABC,
and then every module that needs to use a Widget class implement the
interface. This is not scaleable - as N different classes (in different
library modules) that delegate to the Widget class will have to write N
implementations for the Widget class. The duplicity of effort that
entails (not to mention the increased scope for errors/nightmare code
maintenance etc) is so ridiculous that I have to assume that I have
misunderstood you - please clarify.
 
B

Ben Voigt [C++ MVP]

Grey Alien said:
But if the interface is implemented in a base class, then that base class
needs to be exported, so that its derived classes can correctly link to
the approriate compilation units - so we end up in the same situation, i.e
having to export the (base) class.

Unless I am misunderstanding you, your approach ensures that if we have a
Widget class (say), we will make its interface available as an ABC, and
then every module that needs to use a Widget class implement the
interface. This is not scaleable - as N different classes (in different
library modules) that delegate to the Widget class will have to write N
implementations for the Widget class. The duplicity of effort that entails
(not to mention the increased scope for errors/nightmare code maintenance
etc) is so ridiculous that I have to assume that I have misunderstood
you - please clarify.

The module that you think you want to dllexport a class from...

Instead you declare an interface (class with only pure virtual functions) in
its public header file. Include this from the clients. Since the members
are pure virtual, the header has the complete definition and no
declspec(dllexport) is needed.

Now in that module providing the class, you create a declspec(dllexport)
extern "C" function (or several) for creating instances, called a factory
method. The instances are of a class that implements the interface. If you
want polymorphism between multiple implementations, multiple classes can
provide an implementation by inheriting the interface. These multiple
classes can each implement the interface independently, or they can inherit
from each other to share an interface.
 
B

Ben Voigt [C++ MVP]

Ben Voigt said:
The module that you think you want to dllexport a class from...

Instead you declare an interface (class with only pure virtual functions)
in its public header file. Include this from the clients. Since the
members are pure virtual, the header has the complete definition and no
declspec(dllexport) is needed.

Now in that module providing the class, you create a declspec(dllexport)
extern "C" function (or several) for creating instances, called a factory
method. The instances are of a class that implements the interface. If
you want polymorphism between multiple implementations, multiple classes
can provide an implementation by inheriting the interface. These multiple
classes can each implement the interface independently, or they can
inherit from each other to share an interface.

Moreover:

The clients need no implementation at all. Instead of consuming the
implementation via dllimport of the class, they simply include the header
and call the factory method. The implementation is linked in because the
factory function sets up the v-table to point to the actual functions (done
automatically by the compiler when you construct an object with virtual
functions).

If the client needs to extend the class, use aggregation instead of C++
inheritance. See the documentation for using CoCreateInstance with
aggregation for the way COM does this. Of course the aggregation logic can
be encapsulated in a C++ class that can be distributed as source code so
that the clients can incorporate it via inheritance without regard to binary
layout compatibility.
 
G

Grey Alien

Ben said:
Ben Voigt [C++ MVP] wrote:



However, as a matter of interest, if only the interface is
specified (via pure virtuals in a header) - surely, it means that
EACH child class will have to implement the same functionality
allover again. The idea for exporting classes was for code reuse -
which you seem to be losing, using the Pimpl pattern you describe.
Am I missing something?



Yup. The interface can be implemented in a base class which
provides the implementation, and each derived (or child if you
prefer) class inherits. Only the interface cannot have any
implementation, but it can participate in a full hierarchy.


But if the interface is implemented in a base class, then that base
class needs to be exported, so that its derived classes can correctly
link to the approriate compilation units - so we end up in the same
situation, i.e having to export the (base) class.

Unless I am misunderstanding you, your approach ensures that if we
have a Widget class (say), we will make its interface available as an
ABC, and then every module that needs to use a Widget class implement
the interface. This is not scaleable - as N different classes (in
different library modules) that delegate to the Widget class will
have to write N implementations for the Widget class. The duplicity
of effort that entails (not to mention the increased scope for
errors/nightmare code maintenance etc) is so ridiculous that I have
to assume that I have misunderstood you - please clarify.


The module that you think you want to dllexport a class from...

Instead you declare an interface (class with only pure virtual
functions) in its public header file. Include this from the clients.
Since the members are pure virtual, the header has the complete
definition and no declspec(dllexport) is needed.

Now in that module providing the class, you create a
declspec(dllexport) extern "C" function (or several) for creating
instances, called a factory method. The instances are of a class that
implements the interface. If you want polymorphism between multiple
implementations, multiple classes can provide an implementation by
inheriting the interface. These multiple classes can each implement
the interface independently, or they can inherit from each other to
share an interface.


Moreover:

The clients need no implementation at all. Instead of consuming the
implementation via dllimport of the class, they simply include the
header and call the factory method. The implementation is linked in
because the factory function sets up the v-table to point to the actual
functions (done automatically by the compiler when you construct an
object with virtual functions).

If the client needs to extend the class, use aggregation instead of C++
inheritance. See the documentation for using CoCreateInstance with
aggregation for the way COM does this. Of course the aggregation logic
can be encapsulated in a C++ class that can be distributed as source
code so that the clients can incorporate it via inheritance without
regard to binary layout compatibility.


This is without doubt, far superior to what I have been doing all along.
Could you possibly write a few lines showing a "dummy" class and how it
may be consumed by another module using the mchanism you describe?

Additionally, is there a name for the mechanism/methodolgy you describe
?. I would be grateful for any links on where to read up more about this
- and maybe convince myself to refactor the existing code to use this
methodology. Tks
 
D

David Wilkinson

Grey said:
Additionally, is there a name for the mechanism/methodolgy you describe
?. I would be grateful for any links on where to read up more about this
- and maybe convince myself to refactor the existing code to use this
methodology. Tks

Grey:

I call this method "Do It Yourself COM."
 
B

Ben Voigt [C++ MVP]

David Wilkinson said:
Grey:

I call this method "Do It Yourself COM."

Maintaining actual compatibility with COM is a good thing too. You can have
real COM objects without messing with a lot of the complications of DCOM,
like marshalling and compound document support.
 

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