views:

222

answers:

7

It seems to me like a no-brainer, but I cannot find any information against or for it.

From the point of view of demangling etc, I don't suppose this to be a big problem, but I can't figure out, how I can write a little tiny C program which calls a function from a little tiny C++ library.

I am on linux right now, trying with static binding.

This must be something many people are running into or many books cover, but I feel like a blind gump sitting in front of this problem. Interestingly, there is no such question on SO either.

I do not even know IF this can work, far lesser HOW this has to be done.

+2  A: 

It is a problem from the point of demangling. Use extern C around the C++ you want to call from C.

Mangling and de-mangling was never standardized in C++, since it was deemed too platform dependent. The result is, that while you could figure out the method name of a C++ method by looking at the linker output, there is no portable way of finding the "real" linkable name of a C++ method of function.

Amigable Clark Kant
+4  A: 

If the C++ library does not provide a C interface, then you can't.

If it does, use C interface.

If you are the author, use the extern "C" feature. http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html

Let_Me_Be
You /can/, it's just painful, and very, VERY dependent on the compiler, including the compiler version.
Arafangion
+7  A: 

Typically, you need to force the C++ compiler to build the library with C linkage for the exported functions.

You can do that by doing the following to your header:

#ifdef __cplusplus
extern "C" {
#endif

void func(void);

/* ... Other function defs go here ... */

#ifdef __cplusplus
}
#endif

Normally, the linker will do C++ name-mangling to the functions, so that the C linker won't be able to find them. extern "C" forces it not to do that. You need to wrap it in the #ifdef, because extern "C" isn't valid in C.

UPDATE

As Charles Salvia says in a comment below, you need to ensure that no exceptions make their way out through your interface, because C has no way of handling them (at least not a platform-independent way).

Oli Charlesworth
Just be careful that `func` doesn't throw any uncaught C++ exceptions that propagate up through C code.
Charles Salvia
@Charles Salvia, good idea, but how would that be any worse than an uncaught exception in C++, they would both result in a crash.
mikerobi
Ì suggest changing the function declaration to `void func(void) NOTHROW;` with conditional definitions `#define NOTHROW` in C mode and `#define NOTHROW throw()` in C++ mode
Christoph
@mikerobi: it differs because (1) an uncaught exception in C++ doesn't provoke undefined behavior, it just terminates the program, whereas who knows what happens to an uncaught exception propagated into C code on J Random platform, and more importantly (2) in C++ it's possible to catch the exception at a higher level (for example, in `main`), whereas a C program using this C++ code *cannot* catch the exception anywhere. Hence, C interfaces can't usefully throw exceptions any more than they can usefully return a `std::string`.
Steve Jessop
@Charles Salvia: An uncaught exception in C++ does not result in a crash (which suggests that application has reached a BAD state). An uncaught exception causes normal program termination. Some OS have the ability to detect an application that exits via exception and inform the user about the condition. Note: One should always catch exceptions in main otherwise the unwinding of the call-stack may not occur. Once you have made sure the call-stack is unwound then re-throw the exception to get your OS to kick in.
Martin York
@Martin, I'm not sure how your comment applies to what I said. I think you meant to address this to mikerobi.
Charles Salvia
@Charles Salvia: Oops. @mikerobi: See above
Martin York
+3  A: 

Personally, I'd write a C++ file that exports functions that use C linkage. In other words, write a binding.

Arafangion
+1 for the most generally applicable and flexible answer. You can do this even if you don't have the source for the C++ library, and you can construct objects and call methods on them from within your binding.
Martin Broadhurst
+1  A: 

On Linux, you can get the mangled name of your C++ functions with the command

nm my-tiny-c++lib.a

Call the function from C by it's mangled name, but do not expect this trick to be portable...

Edit: then you have to link using g++, or with gcc -lstdc++

Edgar Bonet
this was a very interesting hack to check which battle I was fighting. I knew nm and checked the lib already to search for any clues, but had not the idea to call the function with that name.
lImbus
+1  A: 

You can write a C wrapper around any C++ library that has a C++ API. The wrapper should be written in C++.

You can even use your C++ classes. You forwardly declare them as struct. Some compilers will give a warning if you do this but you can probably either disable this warning or just ignore it.

You now create free-functions to create a pointer to such a struct (not an instance, as you don't see its full definition) and to dispose of such a pointer and all its methods by taking it as a parameter.

Note that if the class is in a namespace, you won't be able to do this so create your own struct that has just that member and use that "wrapper" instead.

For all your C functions. in the header only, put

#ifdef __cplusplus
extern "C" {
#endif

// all your functions

#ifdef __cplusplus
}
#endif
CashCow
A: 

Typical wrapper looks like this:

---- class_a.hpp ----

#include "class_a_wrapper"

class A {
     public:
     int foo(double p);
     double bar(int x, int y);
}

---- class_a_wrapper.h ----

#ifdef __cplusplus
extern "C" {
#endif

struct wrap_A;
int wrap_A_foo(struct wrap_A *obj, double p);
double wrap_A_bar(struct wrap_A *obj, int x, int y);

#ifdef __cplusplus
}
#endif

---- class_a_wrapper.cpp ----

#include "class_a.hpp"

int wrap_A_foo(struct wrap_A *obj, double p) {
    return (static_cast<A*>obj)->foo(p);
}
double wrap_A_bar(struct wrap_A *obj, int x, int y) {
    return (static_cast<A*>obj)->bar(x, y);
}
Vovanium