TL; DR: 이런 설정이 필요합니다!
my_library를 만든다고 하면, 이 라이브러리를 동적 라이브러리로 쓸 때는 MY_LIBRARY_SHARED를 1로 정의해야 합니다.
우선 헤더에 이런 내용이 필요합니다. 헤더가 여러 개라면 별도의 파일로 빼는 것도 좋습니다.
// 유효성 검사 (없어도 됨)
#ifndef MY_LIBRARY_SHARED
# error MY_LIBRARY_SHARED must be specified
#elif MY_LIBRARY_SHARED == 0
#elif MY_LIBRARY_SHARED == 1
#else
# error MY_LIBRARY_SHARED must be specified as 0 or 1
#endif
#undef MY_LIBRARY_API
#ifdef MY_LIBRARY_EXPORT
# if defined(_WIN32) && MY_LIBRARY_SHARED == 1
# define MY_LIBRARY_API __declspec(dllexport)
# else
# define MY_LIBRARY_API
# endif
#else
# if defined(_WIN32) && MY_LIBRARY_SHARED == 1
# define MY_LIBRARY_API __declspec(dllimport)
# else
# define MY_LIBRARY_API
# endif
#endif
C
복사
해당 라이브러리의 헤더에서는 내보내려는 모든 심볼에 MY_LIBRARY_API를 붙여야 합니다.
#ifndef MY_LIBRARY_H
#define MY_LIBRARY_H
// ... includes
MY_LIBRARY_API void my_library_function(void);
#endif
C
복사
추가로 해당 라이브러리를 구현하는 c 파일에서는 해당 헤더를 include하기 전에 MY_LIBRARY_EXPORT를 정의해야 합니다.
#define MY_LIBRARY_EXPORT
#include "my_library.h"
// ... other includes
MY_LIBRARY_API void my_library_function(void) {
puts("Hello world!");
}
C
복사
동적 라이브러리, 정적 라이브러리
동적 라이브러리는 실행 시점에 링크되며 실행 파일에 포함되지 않는 라이브러리고,
정적 라이브러리는 빌드 타임에 링크되어 실행 파일에 포함되는 라이브러리입니다.
모종의 이유로 라이브러리를 동적으로도 배포하고 정적으로도 배포하고 싶을 수 있습니다.
이 글에서는 그럴 때 헤더에서 어떤 설정을 해야 하는지에 대해 다룹니다.
특별한 설정이 필요한 이유
헤더에 설정을 해야 될 이유가 딱히 없을 것 같긴 한데요,
실제로 macOS와 Linux에서는 헤더에 별다른 설정을 하지 않아도 문제 없습니다.
하지만 Windows에서는 동적 라이브러리에서 불러올 함수 및 동적 라이브러리로 내보낼 함수에 각각 __declspec(dllimport), __declspec(dllexport)를 불여야 합니다.
그리고 이는 Windows의 MSVC용 플래그이므로, 다른 빌드 환경에서는 붙이지 말아야 합니다.
Windows용 동적 라이브러리 함수 정의 예시
hello.dll 동적 라이브러리에 다음 함수가 있다고 합시다.
#include <stdio.h>
void hello(void) {
puts("Hello world!");
}
C
복사
Windows에서는 이 함수를 동적 라이브러리에서 내보내려면, 즉 외부에서 불러와서 쓸 수 있게 하려면 다음과 같은 코드가 필요합니다.
#include <stdio.h>
__declspec(dllexport) void hello(void) {
puts("Hello world!");
}
C
복사
그리고 사용할 때는 이런 함수 선언이 필요합니다.
__declspec(dllimport) void hello(void);
C
복사
필요한 설정
크로스 플랫폼
Windows와 POSIX 환경을 모두 지원하는 크로스 플랫폼 라이브러리라면 __declspec을 바로 사용해서는 안 됩니다.
_WIN32가 정의되어 있을 때만, 그리고 동적 라이브러리일 때만 붙이도록 해야 합니다.
#if defined(_WIN32) && MY_LIBRARY_SHARED == 1
__declspec(dllimport)
#endif
void my_library_function(void);
C
복사
이걸 매번 하긴 귀찮으니 MY_LIBRARY_API라는 매크로 상수로 정의해 두면 좋을 것입니다.
#if defined(_WIN32) && MY_LIBRARY_SHARED == 1
# define MY_LIBRARY_API __declspec(dllimport)
#else
# define MY_LIBRARY_API
#endif
MY_LIBRARY_API void my_library_function(void);
C
복사
물론 dllimport 부분은 라이브러리를 빌드할 떄는 dllexport로 적절히 바뀌어야 합니다.
헤더 공유
MY_LIBRARY_API의 내용이 다르다고 외부 공개용 헤더와 별개로 같은 내용의 내부용 헤더를 작성하는 것은 바람직하지 않습니다.
#ifdef MY_LIBRARY_EXPORT
# if defined(_WIN32) && MY_LIBRARY_SHARED == 1
# define MY_LIBRARY_API __declspec(dllexport)
# else
# define MY_LIBRARY_API
# endif
#else
# if defined(_WIN32) && MY_LIBRARY_SHARED == 1
# define MY_LIBRARY_API __declspec(dllimport)
# else
# define MY_LIBRARY_API
# endif
#endif
C
복사
MY_LIBRARY_EXPORT라는 매크로 상수가 있는지에 따라 적절히 바뀌도록 하는 것이 좋습니다.