Developing ICL for Windows

ICL Macros

Because in all header files dllexport attributes must be replaced with dllimport to create application using the ICL and to prevent delivering different files some useful macros are defined in CompatMacros.h:

// Extract from CompatMacros.h
#ifdef WIN32
  #ifdef ICLUtils_EXPORTS
    ICLUtils_API __declspec(dllexport)
  #else
    ICLUtils_API __declspec(dllimport)
  #endif
#endif

For every library project Visual Studio creates a preprocessor definition PROJECTNAME_EXPORTS. That is why the previous extract guarantees the usage of the attribute dllexport only for our current library and dllimport for all others. Obviously this file must be included by all ICL header files to achieve this effect.

Export/Import Functions and Variables

With the previously defined macros only all new functions, variables and objects, that should get an entry in the created library with the corresponding definition, must be marked.

// This function will be exported if ICLUtils is created
// and imported if another project wants to use it
ICLUtils_API void func();

This should be done for at least all functions and variables that are declared in header files but defined in source files. Private declarations cannot be used by other classes, but the protected ones should be marked as well because classes outside our library could derive from our classes. Template functions cannot be exported in a library without an instantiation. Because of this reason the macros for dllexport must be added to the definitions of the instantiations. Furthermore it should also be added to the declaration of the template function to show other projects that some definitions of this function can be found in the corresponding library. The dllexport attribute in the declaration will be ignored while compiling the library because only instantiations can be exported.

// Header file
template<class A> ICLUtils_API void func(A arg);


// Source file
template<class A> void func(A arg) {
  // Some code
}
// Explicit instantiations
template ICLUtils_API void func(int   arg);
template ICLUtils_API void func(float arg);

While declaring a global function with the keyword friend inside of a class the pattern of the real declaration must be considered. Even private functions must be written using this pattern or the compiler may interpret it as a redefinition of this function which leads to different linkage error.

class Example {
private:
  friend ICLUtils_API void func(void);
}

ICLUtils_API void func(void);

All above assumptions apply to functions and variables of template classes.

Export/Import Classes

Using the macros on the entire class or structure is a faster way to mark functions and variables for export in a library. This method marks everything that can be exported in a class but the following parts:

  • template functions are using not the same template attributes as the class/struct

  • friend functions

  • inline functions

  • classes and structures defined in the class/struct

To prevent linking errors it must be ensured, that the listed parts are using the defined macros. Furthermore the explicit instantiations of template classes or structures must contain the dllexport attribute.

// Header file
template<class A> class ICLUtils_API Example {
  void func(A arg);
}


// Source file
template<class A> void func(A arg) {
  // Some code
}
// Explicit instantiations
template class ICLUtils_API Example<int>;
template class ICLUtils_API Example<float>;

Some problems may occur using the macros on classes or structures that derive from other classes/structures. The parent class/struct will also be exported in the library if it wasn’t before. This may lead to more than one definitions with the same names and resolve in an linker error. The most common reason is a template class which was not instantiated with a specific type and now is derived two or more times.

Example

The following example shows most of the previously discussed cases:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Header file

#ifdef TESTLIBRARY_EXPORTS
#define TESTLIBRARY_API __declspec(dllexport)
#else
#define TESTLIBRARY_API __declspec(dllimport)
#endif

template<class T> class TESTLIBRARY_API TemplateClass {
public:
  class InClass {
  public:
    TESTLIBRARY_API InClass(void);
  };
  int var;
  T tVar;
  TemplateClass(T arg);
  int func(void);
  template<class A> TESTLIBRARY_API A templateFunc(A arg);
  int callGlobalFriendFunc(void);
  friend TESTLIBRARY_API int friendFunc(void);
protected:
  int protectedFunc(void);
private:
  int privateFunc(void);
  friend TESTLIBRARY_API int gloabalFriendFunc(void);
};

extern TESTLIBRARY_API int globalVar;
TESTLIBRARY_API int globalFunc(void);


// Source file

template<class T>
TemplateClass<T>::InClass::InClass() {
  // some code
}

template<class T>
TemplateClass<T>::TemplateClass(T arg) {
  var = protectedFunc();
  tVar = arg;
}

template<class T>
int TemplateClass<T>::func() {
  return privateFunc();
}

template<class T>
int TemplateClass<T>::callGlobalFriendFunc() {
  return globalFunc();
}

template<class T>
int TemplateClass<T>::protectedFunc() {
  return 10;
}

template<class T>
int TemplateClass<T>::privateFunc() {
  return 55;
}

template<class T> template<class A>
A TemplateClass<T>::templateFunc(A arg) {
  return arg + 3;
}

// Explicit instantiations
template TESTLIBRARY_API int   TemplateClass<int>::templateFunc(int   arg);
template TESTLIBRARY_API float TemplateClass<int>::templateFunc(float arg);

// Explicit instantiation of the class
template class TESTLIBRARY_API TemplateClass<int>;

int globalVar = 1337;

int globalFunc(void)
{
  return 42;
}

int friendFunc(void) {
  return 555;
}