Correct import/export of symbols from library

Hi,

This question is related to building a shared library NMAKE : fatal error U1077 I think.

In order to properly declare my libraries symbol, I’m currently obliged to do something like that:

#ifdef UNIQUELIBRARYIDENTIFIER_MSVC_SHARED
#ifdef EXPORT_MSVC_SHARED
#define DLLAPI __declspec(dllexport)
#elif IMPORT_MSVC_SHARED
#define DLLAPI __declspec(dllimport)
#endif
#else
#define DLLAPI
#endif

DLLAPI void foo();

When I’m compiling my library with msvc and if it is set to be shared, I “target_compile_define” publicly UNIQUELIBRARYIDENTIFIER_MSVC_SHARED. I also privately define EXPORT_MSVC_SHARED.
When I’m compiling a client on MSVC which is not a dll, I privately define IMPORT_MSVC_SHARED.

Thus on non-msvc compiler or if the library is set to static, DLLAPI is empty. Otherwise it’s __declspec(something).

This seems to be far stretched, it obliges to generate an identifier per library and to use it properly in its headers which is error prone.

Besides, so far, if the client target is itself a shared library, then DLLAPI will be erroneously set to export for dependencies.

How can it be done correctly? It seems that it should be better if the client set import on the dependencies on a per dependency basis?

So far, I’m failing to find a proper documentation on how to compile/link a static/shared library for multiple compilers.
Thanks
A.

More generally is there a way to do something like that in A.h, header of some library A

#ifdef <some symbol defined only when building A>
#ifdef <some symbol defined only if A is shared>
<do something>
#else // A is static
<do stg else>
#else // I'm not building A, I'm building another target that uses A
#ifdef <some symbol defined only if A is shared>
<do something>
#else // A is static
<do stg else>
#endif
#endif

How can it be achieved, without collision between the different targets involved (a symbol defined for a target must not be confused with one of another target).

Thanks

I strongly recommend you take a look at the GenerateExportHeader module. It automates this sort of logic and it supports a wide range of toolchains.

1 Like

Hi,

It seems indeed close to what I need but I’m failing to use it correctly.
First of all, I think that the documentation could be improved by replacing:

install(FILES ${PROJECT_BINARY_DIR}/${TARGET_NAME}_export.h DESTINATION <my include directory path>)
FILE(COPY ${PROJECT_BINARY_DIR}/${TARGET_NAME}_export.h DESTINATION <my include directory path>)

It probably depends on each one file organisation…

Yet my current issues are not that one.

First of all, the ${TARGET_NAME}_export.h contents depend on the used compiler. It might not be an issue as it is automatically modified when I switch compiler. Perhaps a mere file header that explains that this is a generated file that must not be edited and that is valid only for the current compiler would be clearer? I think it would be more pragmatic than trying to generate one file for all situations, which will not be feasible in fine…
My second issue, which is currently blocking me, is that if I switch my target to STATIC, I would have expected <TARGET_NAME>_STATIC_DEFINE to be defined and usable when building or using the library but it does not seem to…
for instance the following code in the definition of an exported function:

#ifdef MYLIB_STATIC_DEFINE
  std::cout << "Static library mylib says hello" << std::endl;
#else
  std::cout << "Shared library mylib says hello" << std::endl;
#endif

always displays Shared library mylib says hello when I use the function from another target.

Thanks
A.