Hi!

I'm currently migrating a project to VC8. The same project compiles and runs
using VC7.1 and eVC4. Now, doing so I found something strange:

I have a library (a DLL) and that lib exports a type. This type is derived
publicly from std::pair<>. Now, what I found during the research was that
this DLL also exports functions for std::pair, in this case constructor,
destructor and assignment operator, but only when compiled with VC7.1 and
later, not with eVC4. Why? All parts of std::pair are inline template code,
any code that uses my library will have these definitions in scope when it
can also use my exported type, so there shouldn't be need for that.

The problems arise with another library (this time a static library, which
was built for convenience to share code between two projects) which also
includes these very symbols that are exported from the DLL. This leads to
linker errors. The two interesting parts are
1. The exact same setup compiles and runs under VC7.1. This means either the
static lib doesn't export these symbols or they are marked as 'weak
symbols' so that duplicate definitions are silently ignored.
2. As a workaround, I included the header of the DLL in the files that cause
symbol clashes. This is really strange, it seems as if the additional
header somehow changed the declaration of std::pair (i.e. suddenly it is
imported from said DLL instead of creating inline instances) - I don't
think that's a good idea.

I'm currently trying to figure out a way to remove the std::pair from the
interface of the DLL without breaking the interface (which would be my next
option, and not necessarily the worst since the interface has assembled
some dust already and a new version is called for).

However, I'm mostly trying to understand what is going on and why, so any
pointers are welcome.

cheers

Uli

Re: Inconsistency in exported symbols between different compiler versions by Eugene

Eugene
Tue Jul 25 03:23:45 CDT 2006

Ulrich Eckhardt wrote:
>
> I have a library (a DLL) and that lib exports a type. This type is
> derived publicly from std::pair<>.

Does the std::pair<> instanciation depend on your derived type? If yes the
answer to your question is provided at
http://msdn2.microsoft.com/en-us/library/twa2aw10.aspx

<quote>
class __declspec(dllexport) D : public B<D> {
// ...Because this is common pattern with templates, the compiler changed
the semantics of dllexport when it is applied to a class that has one or
more base-classes and when one or more of the base classes is a
specialization of a class template. In this case, the compiler implicitly
applies dllexport to the specializations of class templates. In Visual C++
.NET, a user can do the following and not get a warning:

class __declspec(dllexport) D : public B<D> {
// ...</quote>

> Now, what I found during the
> research was that this DLL also exports functions for std::pair, in
> this case constructor, destructor and assignment operator, but only
> when compiled with VC7.1 and later, not with eVC4. Why?

Looks like the case above. eVC4 behaves more or less like VC6 that didn't
have this feature.

> I'm currently trying to figure out a way to remove the std::pair from
> the interface of the DLL without breaking the interface (which would
> be my next option, and not necessarily the worst since the interface
> has assembled some dust already and a new version is called for).

As a general rule don't export classes derived from something you don't
control. You can use explicit dllexport on each public member to achieve the
same effect (but you will have to write down the implicitly generated ones).


--
Eugene
http://www.gershnik.com



Re: Inconsistency in exported symbols between different compiler versions by Ulrich

Ulrich
Tue Jul 25 04:23:29 CDT 2006

Eugene Gershnik wrote:
> Ulrich Eckhardt wrote:
>>
>> I have a library (a DLL) and that lib exports a type. This type is
>> derived publicly from std::pair<>.
>
> Does the std::pair<> instanciation depend on your derived type? If yes the
> answer to your question is provided at
> http://msdn2.microsoft.com/en-us/library/twa2aw10.aspx
>
> <quote>
> class __declspec(dllexport) D : public B<D> {
> // ...Because this is common pattern with templates, the compiler changed
> the semantics of dllexport when it is applied to a class that has one or
> more base-classes and when one or more of the base classes is a
> specialization of a class template. In this case, the compiler implicitly
> applies dllexport to the specializations of class templates. In Visual C++
> .NET, a user can do the following and not get a warning:
>
> class __declspec(dllexport) D : public B<D> {
> // ...</quote>

Thanks for that info, but the std::pair<> instantiation doesn't depend on my
type, it is a simple pair<unsigned long, unsigned long> holding a version
number.

>> Now, what I found during the
>> research was that this DLL also exports functions for std::pair, in
>> this case constructor, destructor and assignment operator, but only
>> when compiled with VC7.1 and later, not with eVC4. Why?
>
> Looks like the case above. eVC4 behaves more or less like VC6 that didn't
> have this feature.

Yes, just for the record the compilers that come with eVC4 report versions
12.00-12.02, the one from VC6 reports 12.00.

>> I'm currently trying to figure out a way to remove the std::pair from
>> the interface of the DLL without breaking the interface (which would
>> be my next option, and not necessarily the worst since the interface
>> has assembled some dust already and a new version is called for).
>
> As a general rule don't export classes derived from something you don't
> control. You can use explicit dllexport on each public member to achieve
> the same effect (but you will have to write down the implicitly generated
> ones).

Hmmm, I don't think that is a good advise. There are two reasons why not:

1. It shouldn't matter.
Yes, I don't have control over std::pair<>, but I assume that for every user
of my class it will be the same. With this assumption, everything stands
and falls, and nothing (not even explicitly dllexporting the instantiation)
can prevent failure if someone tries to use my library with a different
std::pair implementation.
This also means that if the visible std::pair<> template is the same for my
library and the one using it, any symbols that were not resolved internally
can be generated from the template so they are available without being
exported from my lib.

2. It could lead to symbol clashes.
If two libraries both used the explicit dllexport to export the same
template instantiation, I couldn't link both libraries to the same project
without resolving to linker hacks (I think, I didn't really verify that).


Lastly, and that is another thing that puzzles me, I don't understand why
the compiler would handle baseclasses and members differently. I understand
that it has to embed code inline for both, but I don't understand why it
suddenly needs to export its functions when it derives from a template
class.

Uli


Re: Inconsistency in exported symbols between different compiler versions by Eugene

Eugene
Tue Jul 25 11:30:34 CDT 2006

Ulrich Eckhardt wrote:
> Eugene Gershnik wrote:
>> Ulrich Eckhardt wrote:
>>>
>>> I have a library (a DLL) and that lib exports a type. This type is
>>> derived publicly from std::pair<>.
>>
>> Does the std::pair<> instanciation depend on your derived type? If
>> yes the answer to your question is provided at
>> http://msdn2.microsoft.com/en-us/library/twa2aw10.aspx
>
> Thanks for that info, but the std::pair<> instantiation doesn't
> depend on my type, it is a simple pair<unsigned long, unsigned long>
> holding a version number.

Hmmm. Then silently exporting it should be classified as a bug ;-)

>> As a general rule don't export classes derived from something you
>> don't control. You can use explicit dllexport on each public member
>> to achieve the same effect (but you will have to write down the
>> implicitly generated ones).
>
> Hmmm, I don't think that is a good advise. There are two reasons why
> not:
>
> 1. It shouldn't matter.
> Yes, I don't have control over std::pair<>, but I assume that for
> every user of my class it will be the same. With this assumption,
> everything stands and falls, and nothing (not even explicitly
> dllexporting the instantiation) can prevent failure if someone tries
> to use my library with a different std::pair implementation.
> This also means that if the visible std::pair<> template is the same
> for my library and the one using it, any symbols that were not
> resolved internally can be generated from the template so they are
> available without being exported from my lib.

std::pair is a simple case but think about some project specific templates
that might have explicit instanciations etc. Yes you can make it work but
following the rue above will make your life simpler.

> 2. It could lead to symbol clashes.
> If two libraries both used the explicit dllexport to export the same
> template instantiation,

Which is covered by the general version of the above rule: don't export what
you don't control ;-) The only one who has any business exporting template
instanciations is the library that provides them.

I also think you misunderstood what I posted about not exporting classes.
The idea is to use something like

class mine : public not_mine
{
public:
__declspec(dllexport) mine();
__declspec(dllexport) void foo();
private:
void bar();

some_type m_member;
};

IOW you export the functions not the "class". This says clearly what you
expect your clients to see and what you don't.
Also if you know Java or C# you might want to compare dllexport semantics
with the notion of package level public and private that they have.
Logically it is very similar and the rules (enforced by the compiler there)
make sense.

--
Eugene
http://www.gershnik.com