Подтвердить что ты не робот

Создание переносимой библиотеки для работы как в 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?

Большое спасибо заранее,

4b9b3361

Ответ 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;
};