Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a portable library to run on both linux and windows

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,

like image 757
ant2009 Avatar asked Oct 17 '13 05:10

ant2009


3 Answers

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.

like image 120
Mark Tolonen Avatar answered Sep 17 '22 01:09

Mark Tolonen


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.

like image 39
aout Avatar answered Sep 19 '22 01:09

aout


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.

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 the more details, please read http://gcc.gnu.org/wiki/Visibility

like image 21
Like Avatar answered Sep 18 '22 01:09

Like