Создание переносимой библиотеки для работы как в Linux, так и в Windows
gcc (GCC) 4.7.2
Здравствуйте,
Я создаю общую библиотеку, которая будет компилироваться на linux и dll, которая будет компилироваться в Windows с использованием того же исходного кода. Поэтому я создаю портативную библиотеку для Linux и Windows.
В моем файле заголовка для библиотеки это i.e. module.h
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type
#else
#define LIB_INTERFACE(type) type
#endif
LIB_INTERFACE(int) module_init();
#ifdef __cplusplus
}
#endif
В источнике у меня есть следующий i.e. module.c
#include "module.h"
LIB_INTERFACE(int) module_init()
{
/* do something useful
return 0;
}
И в моем тестовом приложении, которое будет связывать и использовать этот модуль .so у меня есть это:
#include "module.h"
int main(void)
{
if(module_init() != 0) {
return -1;
}
return 0;
}
1) Является ли то, что я сделал выше, это правильная реализация создания переносимой библиотеки для linux и windows?
2) Мне просто интересно, как я завернул функции в extern "C"
, чтобы эта библиотека могла быть вызвана из программы, которая была скомпилирована в С++. Мне все еще нужен этот EXTERN_C
в следующем:
#define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type
3) Какова цель EXTERN_C
?
Большое спасибо заранее,
Ответы
Ответ 1
Это типичный способ экспорта DLL-API для Windows и поддержки Linux:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
# ifdef MODULE_API_EXPORTS
# define MODULE_API __declspec(dllexport)
# else
# define MODULE_API __declspec(dllimport)
# endif
#else
# define MODULE_API
#endif
MODULE_API int module_init();
#ifdef __cplusplus
}
#endif
В источнике DLL:
#define MODULE_API_EXPORTS
#include "module.h"
MODULE_API int module_init()
{
/* do something useful */
return 0;
}
Ваш источник приложения правильный.
Используя приведенную выше модель, в Windows DLL будет экспортировать API, пока приложение будет импортировать его. Если не на Win32, украшение __declspec
будет удалено.
Поскольку заголовок обертывает весь интерфейс в extern "C"
, использование макроса EXTERN_C
на каждом интерфейсе не требуется. extern "C"
используется, чтобы сообщить компоновщику ссылку C
вместо C++
. C является стандартным для компиляторов, тогда как С++ не ограничивает использование библиотеки DLL для приложения, построенного с помощью того же компилятора.
Нет необходимости интегрировать возвращаемый тип в макрос API.
Ответ 2
extern "C" в основном означает, что вы говорите компилятору, чтобы он не исказил ваше имя функции.
Mangling - это процесс "кодирования" имен функций для последующего выполнения и совершенно другой в C и С++, поскольку С++ может иметь разные функции с тем же именем (через перегрузку и т.д.).
В источнике С++, каков эффект extern "C" ?
После компиляции эти функции можно вызывать из любого места, но вы можете быть уверены, какую библиотеку вы создаете (статическую или динамическую), прежде чем начать.
Также я рекомендую вам не использовать DEFINES, например, вы делаете в одном файле для удобства переноски из-за проблем с обслуживанием или чтения, которые могут возникнуть позже в разработке.
Я бы создал базовый файл, определяющий интерфейс, полностью переносимый для WIN и UNIX, а затем создать две другие библиотеки, реализующие интерфейс, но для разных платформ.
Например, вы можете:
AbstractInterface.h, WinInterface.h, UnixInterface.h
Затем компилируйте только те, которые вам нужны, в зависимости от платформы.
Ответ 3
Для Linux gcc без -fvisibility = hidden будет выполнять функции, экспортированные по умолчанию, кроме статических функций.
С -fvisibility = hidden gcc не будет выполнять функции, экспортированные по умолчанию, за исключением того, что функции, украшенные
__attribute__ ((visibility ("default")))
Для Windows экспортированные функции, украшенные
__attribute__ ((dllexport))
при использовании экспортированных функций они должны быть украшены
__attribute__ ((dllimport))
Макросы в ваших сообщениях
__declspec(dllexport)
поддерживаются MSVC.
Таким образом, скрещенные макросы linux и windows выглядят следующим образом:
#if defined _WIN32 || defined __CYGWIN__ || defined __MINGW32__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif
- Убедитесь, что общие объекты или проекты DLL должны быть скомпилированы
с -DBUILDING_DLL.
- Проект, который зависит от вашего общего объекта или библиотеки DLL, должен быть скомпилирован без -DBUILDING_DLL
Подробнее см. http://gcc.gnu.org/wiki/Visibility
Ответ 4
Из-за концепции полиморфизма, специфичного для языка С++, все функции, определенные в С++, искажены. т.е. для создания уникальных имен для каждой переопределенной функции, "компилятор" украшает имена функций.
Поскольку манипуляция имени обрабатывается "компилятором", и нет спецификации для строгого определения правил определения имени, каждый компилятор украшает имена разными способами. Проще говоря, компиляторы gcc и msvc создают разные сигнатуры функций для одного и того же кода. вы можете прочитать далее о mangling имени в статье wiki здесь.
Ваш файл module.h просто сообщает компилятору использовать c style name mangling или вообще не манипулировать. из-за этой директивы библиотека, которая скомпилирована gcc, может использоваться для связи с бинарным, который написан в visual studio. Это поможет вам распространять двоичные файлы вашей библиотеки вместо исходного кода.
С другой стороны, если вы не используете директиву EXTERN_C, библиотека и проект, который ссылается на библиотеку, должны быть скомпилированы с использованием того же самого компилятора. например, вы должны использовать gcc для компиляции linux и msvc для компиляции Windows как для библиотеки, так и для проекта, связанного с этой библиотекой.
Ответ 5
Вместо того, чтобы писать файл заголовка самостоятельно, вы также можете позволить CMake сгенерировать его для компилятора здания, используя CMake generate_export_header, как это (примеры взяты со страницы со ссылками):
add_library(libfoo foo.cpp)
generate_export_header(libfoo)
#include "libfoo_export.h"
class LIBFOO_EXPORT FooClass {
int bar;
};