Thursday, March 08, 2007

C++ Linkages...


Well there are many things in development that one has to learn the hard way. By hard way, I mean one gets stuck in some problem and then fight over it or days. Then when you come to understand the problem, you realize why your peers had implemented a certain thing in a certain way. And when you realize this, you feel through respect for them and never forget the problem ever again.

I went through a same problem. This time I was migrating certain applications from one product which used VC 6.0 to another product which used VC.Net. The newer product was based on the older product, meaning the older product acted as a platform and newer stuff was developed on it. So plugins developed on older product used to work on newer product. The problem used to arise at the time of migration. Since the build system was different (newer product used scons) many modifications where made to #define.

I will briefly talk about problems I faced and reason for them. For most experienced developers, this will be child’s play.



1) LINK 2001:- unresolved external symbol error

a) The .h or .cpp file containing the symbol is not getting included. Meaning the lib which contains the symbol is not getting loaded. Check the Makefile or the Build script files to see that the lib path is include.

b) The Lib might be getting included. In that case, open the lib in notepad and search of the symbol. Check if the symbol is present in the lib file. If it is not, then one has forgotten to do dllexport. ( This is case of implicit linking )

c) In the case of one file including .h file present in another dll. Meaning building one dll but internally there is linking to another dl. Now the second dll, as per __declspec(dllimport) gets imported but some where down it is again getting exported because of __declspec(dllexport). This error will occur mostly in case for static variables of a class. Refer below of method to avoid this problem.



2) LINK 2005:- already defined in obj ….

a) The called dll is getting imported and exported as in case of issue 1c.

b) If the above is not a problem, check whether the functions for which problems exist are inline functions. If they are inline functions then make them non inline. Functions are made inline by either using keyword Inline or by defining them in the class declaration itself.



How to avoid problem of multiple import and export

1) Setup a common environment variable which one can use and forget about the import and export headache

Example:-
Suppose we want to build a dll called TAR and it internally contains number of .h and .cpp file. In one of .h file, usually where one puts all #define or the one being include by all .h and .cpp files we put the following definition

#if defined (_WIN32)
#ifdef DASH_WINEXPORT
#define DASH _WINEXPORT __declspec(dllexport)
#else
#define DASH _WINEXPORT __declspec(dllimport)
#endif
#else
#define DASH _WINEXPORT
#endif



Now in the build script file or the makefile or pro file for the TAR।dll,
make sure to define the variable TAR_WINEXPORT

So now when the TAR dll is being built, the variable TAR_WINEXPORT will get set via the build script. This results in TAR_WINEXPORT being set the value __declspec(dllexport) and all the symbols will get exported.

Now suppose one is building a dll called DASH and one of the files is including a .h file who’s symbolic reference is present in the TAR library. So now when one builds the DASH library, the TAR library will get only imported as in the DASH library building script, we have not set the TAR_WINEXPORT variable. So the value of TAR_WINEXPORT becomes __declspec(dllimport) and hence during building of DASH library, the TAR library will get only imported and not exported out again.

In the DASH library the exporting variable will be as follows

#if defined (_WIN32)
#ifdef DASH_WINEXPORT
#define DASH _WINEXPORT __declspec(dllexport)
#else
#define DASH _WINEXPORT __declspec(dllimport)
#endif
#else
#define DASH _WINEXPORT
#endif



2) In the above case, we have defined two export variables for two
libraries/dll’s. So if number of dll’s increase, number of variables increases.

To avoid that one can simply set one single variable for the whole. Consider the following case.

We have are going to set only one variable for purpose of exporting individually in each file.

Now consider that we have a tar\t.h file which is used for creating TAR library.
Let’s have a dash\d.h file which is internally doing #include<../tar/t.h>. The d.h file is used for creating DASH library. So the layout of both files will be as follows.

d.h

#ifdef _WIN32
#undef GLOBAL_WINEXPORT
#define GLOBAL_WINEXPORT __declspec(dllimport)
#endif



#define DLLIMP
#include "../tar/t.h”
#define undef DLLIMP



#ifndef DLLIMP
#ifdef _WIN32
#undef GLOBAL_WINEXPORT
#define GLOBAL_WINEXPORT __declspec(dllexport)
#endif
#endif


t.h

#ifdef _WIN32
#undef GLOBAL_WINEXPORT
#define GLOBAL_WINEXPORT __declspec(dllimport)
#endif



#ifndef DLLIMP
#ifdef _WIN32
#undef GLOBAL_WINEXPORT
#define GLOBAL_WINEXPORT __declspec(dllexport)
#endif
#endif


So now while linking, when t.h is called, the variable DLLIMP is set. As per the
definition in t.h file, since DLLIMP is set, the __declspec(dllexport) part won’t be called and the dll will only get imported.

When building of TAR dll, since the variable DLLIMP is not set, the dll will get exported out, which is as required.

Generally use solution one as it is easier to manage but it’s all up to developers choice



Programming Ring - New Post
Programming Ring - Old Post

No comments: