LNK2019 unresolved external symbol / how to ignore dllimport?

F

Frederiek

Hi,

I'm using MS Visual C++ 2005 (MSVC8.0).

I am experiencing some kind of linker dilemma. While building a program
I am -in a way- forced to use following construct.


// aaa_ssf.h - some 3rd party open source library header file

class DBP_EXPORT AAA_SSF
{
public:

virtual ~AAA_SSF() { }

// other members

protected:

AAA_SSF() { }

// other members
};


DBP_EXPORT is a define that depends on whether the header file is used
to build a library, or in a program using the library. In case the
header is used to build a library, DBP_EXPORT is defined as

#define DBP_EXPORT __declspec (dllexport)

In case it is used to build a program based on the library, it is
defined as

#define DBP_EXPORT __declspec (dllimport)

I am using the header file to create a program based on the library. So
DBP_EXPORT is defined as __declspec (dllimport). But in case of the
above class, this causes a problem when building. The dllimport
specifier in front of the class name, seems to instruct the compiler to
put the class members that are called from the program, in the object
file as external defined functions. But they are not defined external,
they are defined inside the class definition itself.
When the linker processes the object file, it probably looks for some
library containing the class member definitions, but ofcourse it can't
find such a library, and it reports back a LNK2019 unresolved external
symbol error.

Is there a way to instruct the compiler that it should not indicate the
members as defined external if they are in fact defined in the source
itself, even if the dllimport specifier is used?

If this is not possible, I will have to start messing with the 3rd
party open source code, an activity which I'd like to postpone as long
as possible.

Regards,

Frederiek
 
U

Ulrich Eckhardt

Frederiek said:
I'm using MS Visual C++ 2005 (MSVC8.0).

I'm currently also porting to that target and worked around some weird
things concerning linking...
class DBP_EXPORT AAA_SSF
{
public:
virtual ~AAA_SSF() { }
AAA_SSF() { }
};

The point is, I guess, that these members are inline.
I am using the header file to create a program based on the library. So
DBP_EXPORT is defined as __declspec (dllimport). But in case of the
above class, this causes a problem when building. The dllimport
specifier in front of the class name, seems to instruct the compiler to
put the class members that are called from the program, in the object
file as external defined functions.

Yes, even though they are inline, it should import them from the DLL when it
doesn't inline them. Note that it doesn't have to use inlining, that is
still just a suggestion to the compiler.
But they are not defined external, they are defined inside the class
definition itself.

Just one guess: the library is built wrong. The thing is that even if a
function is defined inline, the compiler should still emit and export an
out-of-line definition. I couldn't say how to do it wrong...
When the linker processes the object file, it probably looks for some
library containing the class member definitions, but ofcourse it can't
find such a library, and it reports back a LNK2019 unresolved external
symbol error.

Just to make sure, but you do in fact link with the DLL (or, rather,
it's .lib file), right?
Is there a way to instruct the compiler that it should not indicate the
members as defined external if they are in fact defined in the source
itself, even if the dllimport specifier is used?

You could always write a linker file giving explicit instructions what to
export and how, but that shouldn't be necessary IMHO.

Uli
 
T

Tim Roberts

Frederiek said:
I'm using MS Visual C++ 2005 (MSVC8.0).

I am experiencing some kind of linker dilemma. While building a program
I am -in a way- forced to use following construct.


// aaa_ssf.h - some 3rd party open source library header file

class DBP_EXPORT AAA_SSF
{
public:

virtual ~AAA_SSF() { }

// other members

protected:

AAA_SSF() { }

// other members
};


DBP_EXPORT is a define that depends on whether the header file is used
to build a library, or in a program using the library.

We seem to be seeing more and more examples of the technique of exporting
an entire C++ class from a DLL. This is a dramatically bad idea.

Such a DLL is tightly bound to the version of C++ you are using. The C++
name mangling scheme has changed at least once in the history of Visual
C++, and changes to the C++ language will probably require it to change
again. In that case, your DLL will be useless.

A much better design is to separate the interface and the implementation.
Create a pure virtual base class which defines the functions and methods
you expect to support. Create an implementation which derives from your
interface, that is hidden to the outside world inside the DLL. Create a
NON-METHOD object factory which returns an instance of your class.

Example:

class AAA_SSF
{
// Pure virtual base class -- an interface.
// This declaration is exposed to user applications.
}

class AAA_SSF_impl : pubic AAA_SSF
{
// Class which implements the AAA_SSF functions. This
// declaration is not exporsed to user applications.
}

extern "C" AAA_SSF* __declspec(dllexport)
Create_AAA_SSF()
{
return new AAA_SSF_impl;
}

Now, your DLL has exactly one export (Create_AAA_SSF). The import library
doesn't have to change at all when you add or remove members from the
interface or the implementation. Your name has standard C mangling, and
can even be called from C, just like a COM interface.
 
U

Ulrich Eckhardt

Tim said:
We seem to be seeing more and more examples of the technique of exporting
an entire C++ class from a DLL. This is a dramatically bad idea.

Such a DLL is tightly bound to the version of C++ you are using. The C++
name mangling scheme has changed at least once in the history of Visual
C++, and changes to the C++ language will probably require it to change
again. In that case, your DLL will be useless.

It's not just the name mangling, but the whole compiler ABI. The name
mangling is only a way to ensure that incompatible ABIs don't cause runtime
errors but linker errors. Another thing is dependent libs, in particular
the C++ standardlibrary. Anyhow, smart people encode the compiler-ABI
(which is basically the name of the compiler) into the DLL, just like they
add e.g. a 'd' for the debug versions. Using #pragma comment(lib,...) and
some preprocessor use this then works without problems.
A much better design is to separate the interface and the implementation.
Create a pure virtual base class which defines the functions and methods
you expect to support. Create an implementation which derives from your
interface, that is hidden to the outside world inside the DLL. Create a
NON-METHOD object factory which returns an instance of your class.

Example:

class AAA_SSF
{
// Pure virtual base class -- an interface.
// This declaration is exposed to user applications.
}

class AAA_SSF_impl : pubic AAA_SSF
{
// Class which implements the AAA_SSF functions. This
// declaration is not exporsed to user applications.
}

extern "C" AAA_SSF* __declspec(dllexport)
Create_AAA_SSF()
{
return new AAA_SSF_impl;
}

Now, your DLL has exactly one export (Create_AAA_SSF). The import library
doesn't have to change at all when you add or remove members from the
interface or the implementation. Your name has standard C mangling, and
can even be called from C, just like a COM interface.

Okay, but unless you also export every memberfunction in a C-style way you
don't gain anything, you can call the factory function, but you can't call
any methods because their mangled name or the implementation of the vptr
may be different. Also, you lose things like overloading and simple value
semantics. IMHO, exporting things via a C interface makes most sense when
you use explicit loading of plugins.

Uli
 
T

Tamas Demjen

Ulrich said:
Okay, but unless you also export every memberfunction in a C-style way you
don't gain anything, you can call the factory function, but you can't call
any methods because their mangled name or the implementation of the vptr
may be different.

The name mangling doesn't matter here. These member functions are not
called by name, but rather by the vtable. How common is that the vtables
are incompatible? I've used this technique not only across different
versions of VC++, but even to interface Borland tools with VC++. The
only big problem I've ever had was that by default enums are treated as
8-bit with Borland tools, and 32-bit with Microsoft tools, which causes
a crash if they're passed by pointer or by reference. But this problem
exists with C-style calls as well.

Of course, one has to be careful with these vtable calls. We still can't
pass STL classes, and memory allocated by the DLL must be freed there
too. I sometimes prefer flattening out classes into a C-style
interface, but I've found the vtable method nearly as portable.

Tom
 
T

Tim Roberts

Ulrich Eckhardt said:
Okay, but unless you also export every memberfunction in a C-style way you
don't gain anything, you can call the factory function, but you can't call
any methods because their mangled name or the implementation of the vptr
may be different.

I don't think that is a real concern. The ubiquity of COM support amongst
x86 C and C++ compilers has demonstrated rather conclusively that vptrs are
always implemented the same way, and no one would think of doing it
differently now, if it meant giving up COM compatibility.
 

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