On Windows, function symbols are not exported by default from dynamic libraries. You must explicitly annotate the functions:
- Use
__declspec(dllexport)for functions to be exported when building the library. - Use
__declspec(dllimport)for functions to be imported when consuming the library.
A common approach is to use a macro that switches between these two declarations depending on whether the code is building the library or using it.
Here is an example header:
// mylib.h
#pragma once
#ifdef MYLIB_EXPORTS // building the library
#define MYLIB_API __declspec(dllexport)
#else // consuming the library
#define MYLIB_API __declspec(dllimport)
#endif
MYLIB_API void hello();
When building the library, the MYLIB_EXPORTS macro must be defined in CMakeLists.txt. When consuming the library, it must not be defined:
# Define MYLIB_EXPORTS macro for symbol export
target_compile_definitions(MyLibrary
PRIVATE MYLIB_EXPORTS
)
cmake_minimum_required(VERSION 3.10.0)
project(mylib VERSION 0.1.0 LANGUAGES C CXX)
add_library(mylib SHARED mylib.cpp mylib.h)
# Define MYLIB_EXPORTS for symbol export
target_compile_definitions(mylib
PRIVATE MYLIB_EXPORTS
)
set_target_properties(mylib PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
Cross-Platform Hendling
On Linux, all symbols are exported by default, so no explicit export declaration is needed. To make the code portable, define a no-op macro for Linux:
// mylib.h
#pragma once
#if defined(_WIN32) || defined(_WIN64)
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API // empty macro
#endif
MYLIB_API void hello();
Supporting Static Libray Builds
When generating a static library, neither Windows nor Linux requires import/export declarations. To handle this, add a MYLIB_STATIC macro that disables those macros during static build:
// mylib.h
#pragma once
#ifdef MYLIB_STATIC
#define MYLIB_API
#else
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#endif
MYLIB_API void hello();
In CMakeLists.txt, define MYLIB_STATIC when building the static library:
# Define MYLIB_STATIC for static library build
target_compile_definitions(mylib
PRIVATE MYLIB_STATIC
)
This is the same technique used by GLFW, which requires defining
GLFW_STATICwhen using its static library.
Static vs Dynamic Dependency Propagation
The type (static or dynamic) of a libray refers only to how its own code is linked. It does not affect the linkage of its dependencies. If a static library links dynamically to a third-party library, the resulting static library will still require that third-party library to be available at runtime. The distinction between static and dynamic libraries applies solely to the library being built, not to the libraries it references.