33
gcc (GCC) 4.7.2

Hello,

I am creating a shared library that will compile on linux and a dll that will compile on windows using the same source code. So i am creating an portable library for both linux and windows.

In my header file for the library is this 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

In the source I have the following i.e. module.c

#include "module.h"

LIB_INTERFACE(int) module_init()
{
    /* do something useful
    return 0;
}

And in my test application that will link and use this module.so I have this:

#include "module.h"

int main(void)
{
    if(module_init() != 0) {
    return -1;
    }
    return 0;
}

1) Is what I have done above is it a correct implementation of creating a portable library for linux and windows?

2) I am just wondering as I have wrapped the functions in extern "C" so that this library can been called from a program that has been compiled in C++. Do I still need this EXTERN_C in the following:

#define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type

3) What is the purpose of the EXTERN_C?

Many thanks in advance,

user2284570
  • 2,891
  • 3
  • 26
  • 74
ant2009
  • 27,094
  • 154
  • 411
  • 609
  • 5
    Its a macro that resolves to `extern "C"` for C++ compilation, and either `extern` or nothing for C compilation. (I honestly can't remember which, but highlight it and press F12, it should take you to the actual definition). It's *purpose* is to instruct the compiler to remove C++ name-mangling from the symbol being exported from your library. And no, if you already have the entire header wrapped in `extern "C" {` you can eliminate it (assuming gcc understands the same block-extern-C, and again, I don't honestly remember if it does). – WhozCraig Oct 17 '13 at 05:33
  • 1
    Please read [this](http://gcc.gnu.org/wiki/Visibility). You may want to use this mechanism. – n. m. could be an AI Oct 17 '13 at 06:24
  • @ant2009 Does you library have to be used from C, or can you limit it to C++11? – John Bandela Oct 29 '13 at 12:42
  • Look at [poco](http://pocoproject.org/) and [Qt](http://qt-project.org/) source code. Both are providing libraries with common API for Windows & Linux. – Basile Starynkevitch Sep 01 '14 at 08:18

5 Answers5

31

This is a typical way to export a DLL API for Windows and still support 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

In the DLL source:

#define MODULE_API_EXPORTS
#include "module.h"

MODULE_API int module_init()
{
    /* do something useful */
    return 0;
}

Your application source is correct.

Using the above model, on Windows the DLL will export the API while the application will import it. If not on Win32, the __declspec decoration is removed.

Since the header wraps the entire interface in extern "C", using the EXTERN_C macro on each interface is not required. extern "C" is used to tell the linker to use C linkage instead of C++. C linkage is standard across compilers, whereas C++ is not, limiting the use of a DLL to application built with the same compiler.

There is no need to integrate the return type into the API macro.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
15

extern "C" basically means that you are telling the compiler not to mangle your function name. Mangling is the process of "encoding" function names for later execution and is quite different in C and C++ as C++ can have different functions having the same name (via overloading etc...).

In C++ source, what is the effect of extern "C"?

Once compiled these functions can be called from anywhere but you might want to be sure what kind of library you are creating (static or dynamic) before you start.

Also I recommend you not using DEFINES like you do in the same file for portability purposes because of the maintenance or readability problems you might encounter later in the development. I would create a basic file defining an interface which is fully portable to WIN and UNIX then create two other libraries implementing the interface but for different platforms.

For example you can have: AbstractInterface.h, WinInterface.h, UnixInterface.h

Then only compile the ones you need depending on the platform.

Community
  • 1
  • 1
aout
  • 300
  • 1
  • 2
  • 10
14

For Linux, gcc without -fvisibility=hidden will make functions exported by default, except for static functions.

With -fvisibility=hidden, gcc will make no functions exported by default, except that functions decorated by

__attribute__ ((visibility ("default")))

For Windows, exported functions decorated by

__attribute__ ((dllexport))

when using the exported functions, they must be decorated by

__attribute__ ((dllimport))

The macros in your posts

__declspec(dllexport)

are supported by MSVC, though are supported by GCC targetting Windows too instead of using the above __attribute__ syntax.

So the crossed linux and windows macros are as following:

#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
  • Make sure that shared object or DLL projects must be compiled with -DBUILDING_DLL.
  • The project that depends on your shared object or DLL must be compiled without -DBUILDING_DLL

For more details about the visibility, please read http://gcc.gnu.org/wiki/Visibility

Community
  • 1
  • 1
Like
  • 1,470
  • 15
  • 21
4

Due to the concept of polymorphism which is specific to c++ language, All the functions defined in c++ are mangled. i.e., to create unique names for each overridden function, the "compiler" decorates the function names.

Since name mangling is handled by "compiler" and there is no specification to strictly define the name mangling rules, each compiler decorates the names in different ways. Simply put, gcc and msvc compilers create different function signatures for the same code. you can read further about name mangling at the wiki article here.

Your module.h file simply tells the compiler to use c style name mangling or no mangling at all. due to this directive, the library that is compiled by gcc can be used to link to a binary that is written in visual studio. This will help you to distribute the binaries of your library instead of source code.

On the other hand, if you do not use the EXTERN_C directive, the library and the project that links to the library should be compiled using same compiler. for example, you have to use gcc for linux compilation and msvc for windows compilation for both the library and the project linking to that library.

madrag
  • 1,685
  • 1
  • 9
  • 4
4

Instead of writing a header file yourself, you can also let CMake generate one for the building compiler using CMake's generate_export_header like this (examples taken from the linked page):

add_library(libfoo foo.cpp)
generate_export_header(libfoo)
#include "libfoo_export.h"
class LIBFOO_EXPORT FooClass {
    int bar;
};
tstenner
  • 10,080
  • 10
  • 57
  • 92