CousCous
CousCous

Reputation: 31

Writing a DLL in C (without C++)

I want to write a DLL with Visual Studio and by default all tutorials or templates include C++ (.cpp file).

If I try to create a DLL project without a precompiled header, the generated project only has a "dllmain.cpp" file (which is optional according to the Microsoft site).

So I deleted the file "dllmain.cpp" just to have MyDLL.c and MyDLL.h. So I tried to have a project as pure as possible by :

So I at the end, the code I have look like :

MyDLL.h :

#pragma once

#ifdef DLL_API
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

DLL_API void dll_init(void);

MyDLL.c :

#include "MyDLL.h"
#include <stdio.h>


void dll_init(void)
{
    printf("------- Init DLL -------");
}

It compiles but I just get the warning : "warning C4273 : inconsistent DLL linkage". Error I can fix by forcing : "#define DLL_API __declspec(dllexport)"

Is it really possible to write a DLL in C only. Or the use in C++ is mandatory and it is the use of extern "C" that allows to wrap in C. What is the philosophy?

Upvotes: 1

Views: 1462

Answers (1)

Aconcagua
Aconcagua

Reputation: 25518

At first: You can write both C++ and C compatible header by conditionally providing the extern "C" flag:

#ifdef __cplusplus
extern "C"
{
#endif

DLL_API void dll_init(void);

#ifdef __cplusplus
}
#endif

This is a standard pattern that profiting from the fact that __cplusplus is only defined by a C++ compiler.

Then you need to make sure your compiler definitions are setup correctly; to make them legal, you at very least need to undefine them before re-defining:

#ifdef DLL_API
#undef DLL_API                              // <-- (!)
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

If it is good practice to use the same symbol name for different purposes is questionable, though. You might consider renaming one of those two.

Then for compiling the DLL source file you need to make sure that DLL_API already is defined before you include the header. This can occur e.g. by defining it before the include (but you need to do so everywhere you include the header within your library):

#define DLL_API 1
#include "mydll.h"

Alternatively you might define DLL_API via the IDE settings (not sure where that needs to be done in MSVC – not using that one, so you need to look up on your own).

An alternative pattern might look as follows:

//include/mydll.h
#ifndef DLL_API
#define DLL_API __declspec(dllimport)
#endif

//source/mydll.h:
#define DLL_API __declspec(dllexport)
#include <include/mydll.h>

// any DLL source (assuming placed within source folder):
#include "mydll.h"

Better? You trade the #define/#undef trouble or the additional pre-processor symbol for an additional header. Good idea to use the same file name? Rename the internal/private one to your needs, it's not about naming conventions, it's about the pattern...

Background extern "C":

C and C++ use different function name patterns; C++ supports function overloading and the real function names get prefixes by which the function signatures can get identified (name mangling!).

So if you have two functions

void f(int i);
void f(int i, double d);

true function names as found in the compiled library/executable might actually look (transparent to you!) like i_f and id_f (only exemplary, true mangling rules you find here).

With extern "C" you now tell the C++ compiler that functions you load from a library need to be looked up without name mangling, just as a C compiler would do – and that functions to be compiled in your project should be compiled without applying name mangling.

By telling the C++ compiler to do so, though, you lose the ability to use C++ features in the signature of the function like overloading, namespaces, etc. (not that you could not use C++ features inside the function, but in the signatures no way...).

Upvotes: 1

Related Questions