49

We develop some project in plain C (C99). But, we have one library as source codes (math library) in C++. We need this library so I would like to ask, what is the most elegant way to integrate this source codes?

Ratio between sizes of C and C++ is 20:1 so moving to C++ is not the option. Should we use static library? DLL? (It's all on Windows).

Cartesius00
  • 23,584
  • 43
  • 124
  • 195
  • 8
    Does the C++ library provide a suitable C interface (free functions instead of member functions, no exceptions, extern "C", etc)? – R. Martinho Fernandes Sep 02 '11 at 09:20
  • 1
    FAQ for mixing C and C++: http://www.parashift.com/c++-faq/mixing-c-and-cpp.html – Maciej Sep 02 '11 at 09:56
  • 1
    possible duplicate of [How to call C++ function from C?](http://stackoverflow.com/questions/2744181/how-to-call-c-function-from-c) – Ciro Santilli OurBigBook.com May 29 '15 at 08:22
  • Not an answer to your question, but have you tried to compiler your C source files with a c++ compiler? With some codebases, it requires suprisingly little actual changes to make it work. I can't tell ofhand however, if there are many silent errors (things that do compile in c and c++, but are UB only c++). – MikeMB Sep 09 '15 at 13:36

4 Answers4

60

EDIT: Based on discussion in the comment, I should point out that separating things into a C-compatible struct duck and a derived class Duck is probably unnecessary. You can probably safely shovel the implementation into struct duck and eliminate class Duck, thus obviating real(…). But I don't know C++ well enough (in particular, the way it interacts with the C universe) to offer a definitive answer on this.


There is no reason you can't simply link all your C and C++ code together into a single binary.

Interfacing to the C++ code requires that you wrap the C++ API in a C API. You can do this by declaring a bunch of functions inside extern "C" { ... } when compiling the C++ code, and without the extern declaration when compiling the C client code. E.g.:

#ifdef __cplusplus
extern "C" {
#endif

typedef struct duck duck;

duck* new_duck(int feet);
void delete_duck(duck* d);
void duck_quack(duck* d, float volume);

#ifdef __cplusplus
}
#endif

You can define the duck struct in your C++ source, and even inherit the real Duck class from it:

struct duck { };

class Duck : public duck {
public:
    Duck(int feet);
    ~Duck();

    void quack(float volume);
};

inline Duck* real(duck* d) { return static_cast<Duck*>(d); }

duck* new_duck(int feet) { return new Duck(feet); }
void delete_duck(duck* d) { delete real(d); }
void duck_quack(duck* d, float volume) { real(d)->quack(volume); }
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • 43
    @Ulterior: Not IMO. If you represent ducks and frogs as void pointers, you might accidentally get a frog to quack, which would tear apart the fabric of the universe. – Marcelo Cantos Sep 02 '11 at 09:55
  • If you want to wrap an STL class, for example, you won't be able to derive from a struct like duck above. In that case, would void pointers be the only route? – Raffi Khatchadourian Feb 21 '13 at 21:56
  • 1
    @RaffiKhatchadourian: It should never be required. In the case of an STL container, you could forgo the inheritance and hold the container directly in the struct: `struct ducks { std::vector d; };`. My `Duck` example could have been the same, albeit with the inconvenience of `duck` needing a forwarding constructor. – Marcelo Cantos Feb 22 '13 at 00:51
  • How do you go about compiling this? Do you need to compile your C source code with g++ (instead of gcc) to get this working? – Raffi Khatchadourian Feb 25 '13 at 00:33
  • 2
    @RaffiKhatchadourian: The source for Duck is C++ code, and therefore must be compiled with a C++ compiler. Code that includes the header file in order to make use of the Duck class can be C or C++ code, and compiled as such. – Marcelo Cantos Feb 25 '13 at 23:58
  • Is there any reason this method should be preferred over Arnd Strube's answer? That is, is there any danger in using pointers to `class Duck` instead of to `struct duck`? – mtvec Jun 06 '13 at 14:00
  • @Job: Amd Strube's answer produces numerous warnings at any reasonable warning level. Changing `class Duck` to `struct Duck` fixes this, and is (I think) a reasonable alternative to my solution. I prefer to keep the C and C++ universes as cleanly separated as possible by defining `struct duck` as a C-compatible type. – Marcelo Cantos Dec 04 '13 at 14:10
  • Why do you think the real() function is needed? struct and class are exchangeable in C++ (the only difference is that if you use struct when *defining* a class in C++, all members are public by default). So really, the inheritance and real() function aren't needed. Forward-declare struct duck in the header and use only pointers and C will be happy. Include the regular C++ duck header in the implementation and add the requisite extern "C" calls and C++ does the right thing. – uliwitness May 13 '14 at 09:44
  • @uliwitness: From my previous comment: "Changing `class Duck` to `struct Duck` … is (I think) a reasonable alternative to my solution." I then go on to explain why I prefer my approach. – Marcelo Cantos May 13 '14 at 12:34
  • @Marcelo Cantos that explanation is what I'm asking about. How is *subclassing* (a C++ operation) separating the C and C++ universes? – uliwitness May 13 '14 at 14:20
  • @uliwitness: Fine, but that's not the same as me saying the real() function is needed. To clarify my position, a struct with methods isn't a C struct. A struct with virtual methods, virtual base classes, private and protected sections, etc., is even less a C struct. I want the pointer that I hand to the caller to point to a bona fide 100% plain old C struct with no C++ bits attached; the fact that it's a sub-object of the derived class doesn't change that. OCD? Perhaps, but separating concepts, even if there's no obvious need, keeps things simple in my head. – Marcelo Cantos May 14 '14 at 00:04
  • @MarceloCantos Just pointing out that, the way C++ is defined, there is no guarantee that you get from this that you don't get from just forward-declaring the class as a struct in the header. As long as you only expose the declaration. If you expose the definition, it won't work in C either. Keeping the code simpler and reducing the number of lines that *could* contain bugs in this case is preferable, IMO. – uliwitness May 18 '14 at 09:37
  • @uliwitness: You're probably right, but then I wonder what would happen if I added a virtual base class to the struct, or multiple inheritance. I think it would be just fine, and I have you assuring me that it is, but, much as I love using C++, it isn't my only language, and I don't have the time to investigate it sufficiently to convince myself that it's safe. I also believe you when you say it's safe, but there's still a nugget of doubt (has uliwitness considered *all* the edge cases? What if the need to cast through void * arises somewhere?). – Marcelo Cantos May 18 '14 at 23:22
  • So I fall back on a strategy that I can be 100% certain of without further research. One day, I'll probably get off my lazy arse, go do some homework and realise, "Hey, uliwitness was right on the money!" Maybe you can short-circuit the process and point to an authoritative source that confirms this. For now, I'll edit my answer to underscore my ignorance on the topic. – Marcelo Cantos May 18 '14 at 23:24
  • Shortly, how does it work, how does the .c file that includes it sees it, and how a different .cpp file that includes it sees it? – schanti schul Jul 12 '21 at 09:08
9

The only reason to want to inherit from the duck struct would be to expose some to its attributes in the C API, which is generally considered bad style anyway. Without inheritance, your C header would look like this:

struct Duck;

struct Duck* new_Duck(int feet);
void delete_Duck(struct Duck* d);
void Duck_quack(struct Duck* d, float volume);

And this would be the corresponding implementation, with no need for type casts:

extern "C" {
#include "Duck.h"
}

class Duck {
public:
    Duck(int feet) : {}
    ~Duck() {}

    void quack(float volume) {}
};

struct Duck* new_Duck(int feet) { return new Duck(feet); }
void delete_Duck(struct Duck* d) { delete d; }
void Duck_quack(struct Duck* d, float volume) { d->quack(volume); }

In the same way, a C API can be created for a C++ interface (pure virtual class) and its implementations. In that case, only the constructor need to be based on the concrete implementation (e.g. new_RubberDuck(2)). The destructor and all other functions will automatically operate on the correct implementation, same as in C++.

A.Robert
  • 304
  • 2
  • 7
  • 3
    The function implementations should also be labeled as extern "C". Otherwise you get link errors because "new_Duck::i" or whatever name mangling on your platform makes of C++ functions doesn't match the header's "new_Duck". – uliwitness May 13 '14 at 09:47
7

A C++ math library may well be implemented in the for of utility classes (static members only). In this case, a much simpler approach could be taken:

class FPMath {
public:
    static double add(double, double);
    static double sub(double, double);
    static double mul(double, double);
    static double div(double, double);
};

The header for the C interface would then be:

double FPMath_add(double, double);
double FPMath_sub(double, double);
double FPMath_mul(double, double);
double FPMath_div(double, double);

And the corresponding implementation might be:

double FPMath_add(double a, double b) { return FPMath::add(a, b); }
double FPMath_sub(double a, double b) { return FPMath::sub(a, b); }
double FPMath_mul(double a, double b) { return FPMath::mul(a, b); }
double FPMath_div(double a, double b) { return FPMath::div(a, b); }

But maybe this is stating the obvious....

A. Robert
  • 71
  • 1
  • 2
1

There is a way to create a "hack" that allows you to call member functions of some objects directly.

The first thing you have to do is to create an extern "C" factory function, which returns a pointer (as void*) to the object.

The second thing you need is the mangled name of the member function.

Then you can call the function using the mangled name, and passing the pointer returned from the factory function as the first argument.

Caveats:

  • Will of course not work calling member function that wants other objects, or references, or other C++ stuff, or functions returning objects or types not compatible with C types
  • Will not work on virtual member functions, and probably not on objects with virtual functions in them even if it's not a virtual function being called
  • The mangled name have to be a valid C symbol
  • Any many many more...

This is not something I recommend, quite the opposite in fact. I strongly advise against doing something like outlined in this answer. It's unsupported and probably undefined behavior and may break in weird and unpredictable ways.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621