8

The environment is Windows, MS Visual Studio 2012. How can I use my DLL libraries written in C++ in a C (not C++) application? In my DLL I used namespaces, classes, etc. For example I wrote two simple projects:

  • DLL project
  • EXE project (uses the DLL)

Both are written in C++. Can anybody show me a similar application written in C that uses C++ libraries?

Thank you.

Carey Gregory
  • 6,836
  • 2
  • 26
  • 47
Andrey Bushman
  • 11,712
  • 17
  • 87
  • 182
  • 1
    possible duplicate of [In C++ source, what is the effect of extern "C"?](http://stackoverflow.com/questions/1041866/in-c-source-what-is-the-effect-of-extern-c) – harper Jun 30 '13 at 17:52
  • 6
    OMG, how is this a duplicate of that? This is a perfectly reasonable question. – MK. Jun 30 '13 at 17:55
  • @harperI so don't think. For example, what header file must I create for my DLL? Problem with the namespaces, templates, e.t.c. – Andrey Bushman Jun 30 '13 at 17:56
  • 2
    Guys, the OP wants to know how a dll created with C++ can be used in a C program. How is his question unclear? And the question linked is only tangentially related -- certainly not a duplicate. – greatwolf Jun 30 '13 at 19:57

2 Answers2

19

The solution to this is to write a C interface to your DLL in C++ (either as part of the DLL, or as a separate DLL), and then use that C interface from your C code.

Be sure that your C interface consists of functions which are all extern "C".

Edit: As SigTerm noted in the comments, it is not a good idea to make the functions stdcall (they are not by default, but some people copy that from WinAPI functions), because that causes trouble when trying to use the functions from other languages.

Here are the challenges, and how they can be solved:

Your class is in a namespace

Normally, you can export C++ classes in C by just declaring an opaque struct ClassName*. However this is not possible for classes in namespaces since C doesn't know the concept of a namespace. There are several ways to solve it:

  • The simple way: Just have your C interface hand out and accept void pointers, maybe thinly disguised by typedefs to the actual types.

    Advantage: Trivial to implement. All you need is a type cast at each entry point.

    Disadvantage: The C interface will not be type safe. It is easy to pass the wrong pointer to your interface and mess things up.

  • The intrusive way: In your C++ library, define global scope base classes for each C++ class you want to expose, and derive the actual class from that. Note that the global scope base classes may be empty because you'll static_cast to the actual type anyway before using it.

    Advantage: Easy to implement, typesafe.

    Disadvantage: A lot of work if you have many classes. Also, it may not be possible if you can't change all the classes which you want top expose that way. In addition, being base classes, you cannot separate them out so that only C clients need them. Every C++ client will get the global classes, even if it doesn't otherwise include the C interface.

  • Using handles: Use an integer handle, which actually contains the pointer cast to int. If your compiler supports it, use intptr_t from stdint.h because it is guaranteed to be large enough no matter what platform you're on. Otherwise, it's probably best to use a long to be on the safe side.

    Advantage: Easy to implement, slightly more typesafe than void*, a familiar interface style for any C programmer who has ever dealt with low level functions like OS file handling.

    Disadvantage: Not very typesafe; you still can pass the wrong type of handle to your function.

  • Using "encapsulated" handles: For each type you want to expose, define at global scope a C struct containing as only member either a void* or an integer handle.

    Advantage: Relatively easy implementation (although not as easy as void* or raw handle), typesafe (as long as the C code doesn't mess with the struct member).

    Disadvantage: Possibly makes it harder for the C compiler to optimize. But that depends very much on the C compiler.

My suggestion would be to go with the encapsulated handles, unless it turns out to have performance issues, in which case I'd use integer handles.

The class interface accepts and returns std::string

There's no way for C code to work with std::string, and you certainly don't want to do the work of wrapping the interface to that in C (and your C interface users would not like you for that anyway). Therefore it is mandatory to use char* / char const* in your C interface.

Passing strings to your library

The case of passing strings to your library is easy: You simply pass a char const* in and let the constructor of std::string do the rest.

Returning strings from your library

This is the complicated case. You can't just return the result of c_str because it will leave you with a dangling pointer when the temporary std::string gets destructed. So you have basically the following options:

  • Store a static std::string (so it won't be deallocated on return), copy the result of your function call into that, and return c_str).

    Advantage: Simple to implement, guaranteed to give you the full string.

    Disadvantage: It is not thread safe, also the user has to remember to immediately copy the content to somewhere, or the next call to the function will invalidate the data, or even worse, the pointer.

  • Allocate a character array of appropriate size in your wrapper function, copy the contents of the string into that array, and return a pointer to it.

    Advantage: You are guaranteed to return the full string (assuming memory allocation didn't fail).

    Disadvantage: It is a well-known problem that deallocating memory in another DLL than the one which allocated it doesn't work correctly. Therefore you'd have to provide a deallocation interface, and the user of your C interface would have to remember to always use that instead of simply freeing the memory.

  • Let your user pass a character array and a length, and use strncpy to copy the string data to the character array.

    Advantage: You don't have to do memory management for the string; that's the respoonsibility of the user. Also, if the user wants the string to be written in a specific place, he can simply pass the address to the wrapper instead of making another copy.

    Disadvantage: If the array the user supplied is not long enbough, you'll get a truncated string.

My suggestion: Given the problems with cross-DLL memory allocation, I'd say to use the third way and accept the risk of to truncated strings (but make sure you have a way to report the error to your users).

You have a member enum in one of your classes

That one is easy: Provide an identical (except for the name) global-scope enum definition fin your C interface. Since identical enum definitios give identical values for the identifiers, simply casting the values from your in-class enum to the global one and back should work perfectly. The only problem is that whenever you update the in-class enum, you have to remember to also update the out-of-class enum.

You have public members of type std::vector

Well, to start with, that's a bad interface decision anyway. The way to handle it is basically the same as you actually should handle those vectors in C++ as well: Provide an iterator interface to them.

Now C++ iterators don't work well in C, but then, in C there's also no reason to adhere at the C++ iterator style.

Given that the interface members are std::vector, another option would be to give out pointers to the first element of the vector. However those would get invalidated the next time your vector is resized, so that would be an option only for very restricted cases.

You have a print function that takes an std::ostream and a reference to an object.

Of course std::ostream is not available to C code. In C, files are accessed using FILE*. I think the best option here is to use an iostream class which wraps around a FILE* and allows you to construct from one. I don't know if that is provided by MSVC++, but if not, Boost has AFAIK such a stream.

Your print wrapper would then take a FILE* and an object handle (or void pointer, or whatever option you chose for representing your objects in C), construct a temporary ostream object from the FILE pointer, extract the pointer to the object from the handle, and then call print on the pointed-to object.

Additional consideration: Exceptions

Don't forget to catch exceptions and turn it into errors C can handle. The best option usually is to return an error code.

Example header for the C interface

The following is a possible C interface header for your linked example library

/* somelib_c_interface.h */
#ifndef SOMELIB_C_INTERFACE_H
#define SOMELIB_C_INTERFACE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdlib.h>

/* typedef for C convenience */
typedef struct
{
  intptr_t handle;
} some_namespace_info;

typedef struct
{
  intptr_t handle;
} some_namespace_employee;

/* error codes for C interface */
typedef int errcode;
#define E_SOMELIB_OK 0              /* no error occurred */
#define E_SOMELIB_TRUNCATE -1       /* a string was created */
#define E_SOMELIB_OUT_OF_MEMORY -2  /* some operation was aborted due to lack of memory */
#define E_SOMELIB_UNKNOWN -100      /* an unknown error occurred */

/* construct an info object (new)
errcode some_namespace_info_new(some_namespace_info* info, char const* name, char const* number);

/* get rid of an info object (delete) */
errcode some_namespace_info_delete(some_namespace_info info);

/* Some_namespace::Info member functions */
errcode some_namespace_info_get_name(some_namespace_info info, char* buffer, size_t length);
errcode some_namespace_info_set_name(some_namespace_info info, char const* name);

errcode some_namespace_info_get_number(some_namespace_info info, char* buffer, size_t length);
errcode some_namespace_info_set_number(some_namespace_info info, char const* name);

/* the employee class */

/* replicated enum from employee */
enum some_namespace_employee_sex { male, female };

errcode some_namespace_employee_new(some_namespace_employee* employee,
                                    char const* name, char const* surname, int age,
                                    some_namespace_employee_sex sex);

errcode some_namespace_employee_delete(some_namespace_employee employee);

errcode some_namespace_employee_get_name(some_namespace_employee employee, char* buffer, size_t length);
errcode some_namespace_employee_set_name(some_namespace_employee employee, char const* name);

errcode some_namespace_employee_get_surname(some_namespace_employee employee, char* buffer, size_t length);
errcode some_namespace_employee_set_surname(some_namespace_employee employee, char const* name);

/* since ages cannot be negative, we can use an int return here
   and define negative results as error codes, positive results as valid ages
   (for consistency reason, it would probably be a better idea to not do this here,
   but I just want to show another option which sometimes is useful */
int some_namespace_employee_get_age(some_namespace_employee employee);

errcode some_namespace_employee_set_age(some_namespace_employee employee, int age);

errcode some_namespace_employee_get_set(some_namespace_employee employee,
                                        enum some_namespace_employee_sex* sex);
errcode some_namespace_employee_set_sex(some_namespace_employee employee,
                                        enum some_namespace_employee_sex sex);

typedef struct
{
  intptr_t handle;
} info_iter;

info_iter_delete(info_iter iterator);

some_namespace_info info_iter_get(info_iter iterator);
void info_iter_next(info_iter iterator);
bool info_iter_finished(info_iter iterator);

info_iter some_namespace_employee_phones(some_namespace_employee employee);
info_iter some_namespace_employee_emails(some_namespace_employee employee);
info_iter some_namespace_employee_sites(some_namespace_employee employee);

errcode some_namespace_print(FILE* dest, some_namespace_employee employee);

#ifdef __cplusplus
}
#endif

#endif // defined(SOMELIB_C_INTERFACE_H)
celtschk
  • 19,311
  • 3
  • 39
  • 64
  • How it will solve problems with namespaces, templates, e.t.c.? I gave a specific example (in the link) and asked the specific response (code). – Andrey Bushman Jun 30 '13 at 17:58
  • Sorry, when I follow the link, I only get a white page. – celtschk Jun 30 '13 at 18:02
  • Maybe it is of browser problem. I placed zip file on the mega.co.nz. I use the Google Chrome. Please try the other browser for the download, please. – Andrey Bushman Jun 30 '13 at 18:05
  • Well I now got it to display a message that my browser doesn't allow to write data, and that I should use standard settingd. It doesn't say why it wants to write data, what data it wants to write, or why it needs to write data. You should not need to write data to the browser in order to display source code. Therefore I'm not going to lower my security settings to see the code. Sorry about that. If you put the code somewhere where it can be seen without compromising security, I'm willing to have a look. – celtschk Jun 30 '13 at 18:08
  • Do I understand correctly that `some_library.h` (respectively `import_some_library.h`) is the (complete) interface to your library? – celtschk Jun 30 '13 at 18:18
  • yes, it is the same header file, but in it I replace DllExport to the DllImport. – Andrey Bushman Jun 30 '13 at 18:22
  • Microsoft compiler mangles names for stdcall functions even with extern C. – SigTerm Jun 30 '13 at 18:27
  • @Bush: OK, I've written up a list of solutions/suggestions for the issues arising in that code. Please check. – celtschk Jun 30 '13 at 19:31
  • @SigTerm: Does it mangle `stdcall` functions the same way in C code? Then I don't see any problem with that. – celtschk Jun 30 '13 at 19:33
  • @celtschk: If you export "extern C" stdcall function, it'll get a name similar to `_testcall@8`, unless you explicitly take care of it with def file or linker directive. This will cause problems when you try to load dll from another language. – SigTerm Jun 30 '13 at 19:48
  • @SigTerm: Are `extern "C"` functions `stdcall` by default? Because if not, anything `stdcall` does or does not is irrelevant to my post because nowhere did I suggest to make them `stdcall`. – celtschk Jun 30 '13 at 19:57
  • @celtschk: C++ uses cdecl by default. However, after looking at winapi some people that write *.dll s for other languages have a bad habit of slapping stdcall onto everything. This quirk is rarely mentioned, and since you already wrote plenty of text, it would make sense to mention this. – SigTerm Jul 01 '13 at 00:34
  • @SigTerm: Ah, I see. Of course if I would write a compiler for Windows (no matter which language), I would make sure that it has a way to call Windows API functions … – celtschk Jul 01 '13 at 20:30
  • How does `stdcall` make the API harder to use from other languages? More languages support `stdcall` than `cdecl` or `fastcall`, that's why it was chosen for the Win32 API in the first place. All calling conventions perform some level of name mangling, the way to get a bare name is with the linker .def file. – Ben Voigt Jul 01 '13 at 21:00
  • Maybe you should have added an "@SigTerm" — I just took his statement on good faith. – celtschk Jul 01 '13 at 21:07
  • 1
    @BenVoigt: If you use `__cdecl` with `extern "C"` and `__declspec(dllexport)`, you'll get unmangled bare names. – SigTerm Jul 01 '13 at 21:57
  • @SigTerm: Seems like you get leading underscores, no? – Ben Voigt Jul 01 '13 at 22:39
  • 1
    @BenVoigt: Your comment is ambiguous. cdecl is default calling convention and can be omitted. `__declspec(dllexport)` is standard dll export declaration. When used in dll, name for exported function (the one you can pass to GetProcAddress) will be stored in unmangled form, without leading underscore even if there's no *.def. Example (dumpbin /exports): `1 0 00001415 createFont = @ILT+1040(_createFont)`. Using stdcall without def (or linker directive) will result in mangled names that start with underscore even with extern C. Example: `18 0 000010CD _testcall@8 = @ILT+200(_testcall@8)` – SigTerm Jul 01 '13 at 23:58
3

You will have to create C wrapper functions which can pass opaque pointers to your class instances.
Here is what seems like a reasonable example: http://www.daniweb.com/software-development/cpp/threads/355990/strategy-for-consuming-c-dlls-in-c

Quote from the link:

typedef Foo* FOO_HDL;
extern "C" { 
__declspec(dllexport) FOO_HDL FooCreate() {
  return new Foo();
};
__declspec(dllexport) void FooDestroy(FOO_HDL obj) {
  delete obj;
};
__declspec(dllexport) double FooGetValue(FOO_HDL obj) {
  return obj->GetValue();
};
__declspec(dllexport) void FooSetValue(FOO_HDL obj, double NewValue) {
  obj->SetValue(NewValue);
};
};
MK.
  • 33,605
  • 18
  • 74
  • 111