I am trying to understand some code that I have inherited, but am having some trouble googling my way out of it, and have some questions I hope some of you can help me with. The code can be seen just below, and is a .hpp (c++ header file) which declares a function.
#ifdef _MSC_VER
#define EXPORT_SYMBOL __declspec(dllexport)
#else
#define EXPORT_SYMBOL
#endif
#ifdef __cplusplus
extern "C" {
#endif
EXPORT_SYMBOL float func(int int_param, float float_param);
#ifdef __cplusplus
}
#endif
My first point of confusion is
#ifdef _MSC_VER
#define EXPORT_SYMBOL __declspec(dllexport)
#else
#define EXPORT_SYMBOL
#endif
I understand this as "if we are compiling from a windows machine, do something special".
What does __declspec(dllexport) do exactly?
Why do I need EXPORT_SYMBOL at all?
Secondly I am confused about the necessity of wrapping the header in the extern "C" statement. As far as I can tell the header is checking to see if __cplusplus is defined, that is, if we are compiling with g++. The extern block is here to make sure I don't have issues with the g++ compiler if func() was found in say, a file called func.c instead of a .cpp file, and then compiled with gcc. I understand mangling, and how g++ changes function names to enable overloading.
As i have it contained in a .cpp file and hence intend to exclusively compile it with g++, do I need the extern "C" block, and #ifdef statements? Is there a case where this is crucial?
What does __declspec(dllexport) do exactly?
When DLL is created, it does not export any symbols by default (unlike static library). In Visual C++, __declspec(dllexport) is the easiest way to export symbols. There are other ways to export.
As i have it contained in a .cpp file and hence intend to exclusively
compile it with g++, do I need the extern "C" block, and #ifdef
statements? Is there a case where this is crucial?
This allows using the function if you load the DLL from a C program, from a C++ program built by any compiler, or even from other languages with C bindings. If you work exclusively with C++ and g++ compiler, there is still a case when this is important: loading the DLL dynamically - in which case you need to supply your function name as a string (mangled unless you use extern "C").
Related
Why shouldn’t extern "C" be specified for a function that needs to be defined as a C function? What effect would that have on the compiler when compiling the file as a C source?
If there is no effect on the C compiler, can’t we just define a function in a header file as below by removing the #ifdef __cplusplus check?
extern "C" {
int MyFunc();
}
An answer to another question says that the #ifdef is needed, but I don’t understand why:
Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. Generally, that means .cpp files and any files being included by that .cpp file. The same .h (or .hh or .hpp or what-have-you) could be interpreted as C or C++ at different times, if different compilation units include them. If you want the prototypes in the .h file to refer to C symbol names, then they must have extern "C" when being interpreted as C++, and they should not have extern "C" when being interpreted as C -- hence the #ifdef __cplusplus checking.
The construct extern "C" is a C++ construct and is not recognized by a C compiler. Typically, it will issue a syntax error message.
A common trick is to define a macro, for example EXTERN_C, that would expand to different thing depending on if you compile using C or C++. For example:
In a common header file:
#ifdef __cplusplus
#define EXTERN_C extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C
#define EXTERN_C_END
#endif
In other files:
EXTERN_C
int MyFunc(void);
EXTERN_C_END
If you compile a source file as C, it will not recognize extern "C", and would usually result in a compilation error.
If you compile a source file as C++, it will recognize extern "C", and the correct names will be linked.
Therefore, you can only use it reliably to specify C symbol names for files you compile as C++.
If you compile sources as C and C++, or your interfaces are intended for C and C++ clients, you would need to specify this one way or another in order for your clients to get the correct symbols when linking (and so on).
Related: You are allowed to write extern "C++" - for C++ translations.
Just a small question:
Can C++ use C header files in a program?
This might be a weird question, basically I need to use the source code from other program (made in C language) in a C++ one. Is there any difference between both header files in general? Maybe if I change some libraries...
I hope you can help me.
Yes, you can include C headers in C++ code. It's normal to add this:
#ifdef __cplusplus
extern "C"
{
#endif
// C header here
#ifdef __cplusplus
}
#endif
so that the C++ compiler knows that function declarations etc. should be treated as C and not C++.
If you are compiling the C code together, as part of your project, with your C++ code, you should just need to include the header files as per usual, and use the C++ compiler mode to compile the code - however, some C code won't compile "cleanly" with a C++ compiler (e.g. use of malloc will need casting).
If on, the other hand, you have a library or some other code that isn't part of your project, then you do need to make sure the headers are marked as extern "C", otherwise C++ naming convention for the compiled names of functions will apply, which won't match the naming convention used by the C compiler.
There are two options here, either you edit the header file itself, adding
#ifdef __cplusplus
extern "C" {
#endif
... original content of headerfile goes here.
#ifdef __cplusplus
}
#endif
Or, if you haven't got the possibility to edit those headers, you can use this form:
#ifdef __cplusplus
extern "C" {
#endif
#include <c_header.h>
#ifdef __cplusplus
}
#endif
Yes, but you need to tell the C++ compiler that the declarations from the header are C:
extern "C" {
#include "c-header.h"
}
Many C headers have these included already, wrapped in #if defined __cplusplus. That is arguably a bit weird (C++ syntax in a C header) but it's often done for convenience.
Why shouldn’t extern "C" be specified for a function that needs to be defined as a C function? What effect would that have on the compiler when compiling the file as a C source?
If there is no effect on the C compiler, can’t we just define a function in a header file as below by removing the #ifdef __cplusplus check?
extern "C" {
int MyFunc();
}
An answer to another question says that the #ifdef is needed, but I don’t understand why:
Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. Generally, that means .cpp files and any files being included by that .cpp file. The same .h (or .hh or .hpp or what-have-you) could be interpreted as C or C++ at different times, if different compilation units include them. If you want the prototypes in the .h file to refer to C symbol names, then they must have extern "C" when being interpreted as C++, and they should not have extern "C" when being interpreted as C -- hence the #ifdef __cplusplus checking.
The construct extern "C" is a C++ construct and is not recognized by a C compiler. Typically, it will issue a syntax error message.
A common trick is to define a macro, for example EXTERN_C, that would expand to different thing depending on if you compile using C or C++. For example:
In a common header file:
#ifdef __cplusplus
#define EXTERN_C extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C
#define EXTERN_C_END
#endif
In other files:
EXTERN_C
int MyFunc(void);
EXTERN_C_END
If you compile a source file as C, it will not recognize extern "C", and would usually result in a compilation error.
If you compile a source file as C++, it will recognize extern "C", and the correct names will be linked.
Therefore, you can only use it reliably to specify C symbol names for files you compile as C++.
If you compile sources as C and C++, or your interfaces are intended for C and C++ clients, you would need to specify this one way or another in order for your clients to get the correct symbols when linking (and so on).
Related: You are allowed to write extern "C++" - for C++ translations.
I've got a DLL that I've created as a C++ Win32 application. To prevent name mangling in my DLL, I have used the EXPORT definition defined below:
#ifndef EXPORT
#define EXPORT extern "C" __declspec(dllexport)
#endif
EXPORT int _stdcall SteadyFor(double Par[], double Inlet[], double Outlet[]);
To get this code to compile, I had to go into the project's Properties and set the C/C++ Calling Convention to __stdcall (/Gz) and set Compile As to Compile as C++ Code (/TP).
This worked in Debug mode, but Release mode is throwing error C2059: syntax error: 'string' on all of my EXPORT functions - even though I have configured the Release mode settings to be the same as the Debug settings.
How do I get Release Mode to compile?
Regards,
~Joe
(Developing under Visual Studio 2008 Professional)
EDIT:
A lot of comments about my #define, which does not appear to be causing any problems.
To eliminate the confusion, my header file has been rewritten as follows:
#ifndef coilmodel_h
#define coilmodel_h
extern "C" __declspec(dllexport) int _stdcall steadyFor(double Par[], double Inlet[], double Outlet[], char* FileIn, char* FileOut);
#endif
That is all of it.
The error is:
Description error C2059: syntax error: 'string'
File coilmodel.h
Line 4
Again, this error only appears in Release mode, not Debug mode.
Project is a C++ Win32 DLL application.
If your source file has a .c extension, the compiler you are using will compile it as C (not C++) and produce that error on the extern "C". If that is the case, then you need to use the /TP switch as you already noted or rename the file to .cpp. The other solution is to put #ifdefs around the extern:
#ifdef __cplusplus
extern "C"
#endif
I would guess EXPORT is defined as something else in Release builds. Since you've got an #ifndef around your definition, that won't do anything if it is already defined, then you get something else (maybe a string?) pasted at the beginning of your function declarations.
So maybe try something like this:
#ifdef EXPORT
#error EXPORT already defined!
#else
#define EXPORT extern "C" __declspec(dllexport)
#endif
Forcing Compile As to Compile as C++ Code (/TP) - did you set this on all build configurations - debug/release x 32/x64 etc. I avoid using this option, much easier to name the file appropriately for the compiler to choose automatically.
You only need the "C" part of extern "C" if the file is C++ to turn off the name mangling.
I prefer to arrange the shared public header using this format, so you can include in C/C++ internally or externally.
#ifdef __cplusplus
# define NOMANGLE extern "C"
#else
# define NOMANGLE
#endif
#ifdef EXPORT_BUILD
# define EXPORT NOMANGLE __declspec(dllexport)
#else
# define EXPORT NOMANGLE __declspec(dllimport)
#endif
Unprobable, but make sure dllexport or _stdcall is not #defined...
I have some quick questions on windows dll.
Basically I am using the ifdefs to handle the dllexport and dllimport, my question is actually regarding the placement of the dllexports and dllimports as well as extern keyword.
I am putting the dllimports/dllexports on the header files but do I have to put the dllexport and dllimports on the actualy definition?
What about for typedefs?
Do I put the dllimport/dllexport in front? as in
dllexport typedef map<string, int> st_map
Also regarding the extern keyword I have seen it being used like this:
extern "C" {
dllexport void func1();
}
I have also seen it being used like this:
extern dllexport func1();
One includes the "C" and the other does not, my question is what is the difference and do I need to use it? If I do then do I use it for both dllexport and dllimport also do I have to use it on both the header file declarations and the definitions?
My project is going to be shared library, it contains several class files which I want to export, some typdefs i want to export and some global functions which I also want to export all into a dll.
Anyone enlighten me please?
EDIT:
Okay I thought I'll post a small extract of what i've done, also notice that I am building the library for both linux and windows so I do a check for that:
mydll.h
#ifdef WINDOWS
# ifdef PSTRUCT_EXPORT
# define WINLIB __declspec(dllexport)
# else
# define WINLIB __declspec(dllimport)
# endif
#else
# define WINLIB
#endif
WINLIB void funct1();
Now in the source code:
mydll.cpp
#define PSTRUCT_EXPORT
void funct1() <---- do I need to add WINLIB in front of it?
Or is doing it in the header enough?
First, you don't need to import or export typedefs. As long as they're in the header files that both sides use, you're good. You do need to import/export functions and class definitions.
Presumably you use the same header files for both the importing and exporting code, so you could do some makefile magic to define a preprocessor macro on each side, then do something like this:
#if defined( LIBRARY_CODE )
#define MYAPI __declspec(dllexport)
#else
#define MYAPI __declspec(dllimport)
#endif
extern MYAPI void func1();
class MYAPI MyClass {
...
};
Regarding C vs. C++ functions, you can do this:
#if defined( __cplusplus__ ) // always defined by C++ compilers, never by C
#define _croutine "C"
#else
#define _croutine
#endif
extern _croutine void function_with_c_linkage();
Make sure you import this header file from your C++ source file (containing the implementation of this function) or the compiler won't know to give it C linkage.
typedefs do NOT need a dllimport/dllexport, it's just a definition
dllimport/dllexport are not standard, think of defining a macro for other platforms/compilers
also take care of the calling convention (cdecl,stdcall,...) used otherwise you'll run into problems (if you need to be interoperable with Visual basic use stdcall)
enclose within extern "C" so that your lib can be used from within C++ programs, use #ifdef __cplusplus to keep it only visible to C++.
Have a look at different OpenSource libs. There you'll find plenty of examples on how making a good library header. There could be issues with name decoration in the case of C++ without the extern "C".