How to call C++ function from C?
Asked Answered
P

6

118

I know this.

Calling C function from C++:

If my application was in C++ and I had to call functions from a library written in C. Then I would have used

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

This wouldn't mangle the name C_library_function and linker would find the same name in its input *.lib files and problem is solved.

Calling C++ function from C???

But here I'm extending a large application which is written in C and I need to use a library which is written in C++. Name mangling of C++ is causing trouble here. Linker is complaining about the unresolved symbols. Well I cannot use C++ compiler over my C project because thats breaking lot of other stuff. What is the way out?

By the way I'm using MSVC

Peery answered 30/4, 2010 at 11:36 Comment(3)
Read parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6Legitimate
When you control the C++ library: #12616183Roentgenograph
Possible duplicate of Elegantly call C++ from CCountershaft
B
140

You need to create a C API for exposing the functionality of your C++ code. Basically, you will need to write C++ code that is declared extern "C" and that has a pure C API (not using classes, for example) that wraps the C++ library. Then you use the pure C wrapper library that you've created.

Your C API can optionally follow an object-oriented style, even though C is not object-oriented. Ex:

// *.h file
// ...
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

typedef void* mylibrary_mytype_t;

EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

#undef EXTERNC
// ...
// *.cpp file
mylibrary_mytype_t mylibrary_mytype_init() {
  return new MyType;
}

void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
   MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
   delete typed_ptr;
}

void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
   MyType* typed_self = static_cast<MyType*>(untyped_self);
   typed_self->doIt(param);
}
Bartolome answered 30/4, 2010 at 11:41 Comment(8)
thanks!! got it. parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.6Peery
How do you link the final executable? Do you use C or C++? (When I used C, the linker couldn't find the C++ function; when I used C++, the linker couldn't find std::cout.)Paraphrastic
@Paraphrastic if you create a static library with the C++ compiler/linker that transitively includes its dependencies (including the C++ standard library, which may be implicitly linked by your linker), you should be able to link that static library against C code using a C compiler/linker.Bartolome
Can you export a template from C++ to C?Ligule
I have an "hybrid" behavior as I need my library to be called both by C++ and C code. In the *.cpp file I also needed to wrap the functions into #ifdef _cplusplus + extern "C" to avoid "undefined reference to" errors at link time.Pearly
If you want to be a bit more type safe, you can "extern C" declare a POD struct which is used on both sides, so that you don't need to deal with void pointers. I suppose one could even derive from that in order to have fully-fledged classes on the C++ side (with member functions). You could similarly then static_cast to the derived class (but from the base class, not void) when you call the member function wrappers.Paulpaula
By the way, re "C++ linker": Is there such a thing? I thought it's all the same ld, and the entire name decoration loops we (or rather, the compiler) jump through are there to create distinct C style functions for overloads and templates?Paulpaula
Last not least: I think the core idea is that the C wrappers essentially just make the first argument of member functions — this — explicit. Adding that would make the perfectly correct answer even better.Paulpaula
C
73

I would do it in the following way:

(If working with MSVC, ignore the GCC compilation commands)

Suppose that I have a C++ class named AAA, defined in files aaa.h, aaa.cpp, and that the class AAA has a method named sayHi(const std::string &name), that I want to enable for C code.

The C++ code of class AAA - Pure C++, I don't modify it:

aaa.h

#ifndef AAA_H
#define AAA_H

#include <string>

class AAA {
    public:
        AAA();
        void sayHi(const std::string &name);
};

#endif

aaa.cpp

#include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const std::string &name) {
    std::cout << "Hi " << name << std::endl;
}

Compiling this class as regularly done for C++. This code "does not know" that it is going to be used by C code. Using the command:

g++ -fpic -shared aaa.cpp -o libaaa.so

Now, also in C++, creating a C connector:

Defining it in files aaa_c_connector.h, aaa_c_connector.cpp. This connector is going to define a C function with C++ implementation, named AAA_sayHi(cosnt char *name), that will use an instance of AAA and will call its method:

aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus
extern "C" {
#endif
 
void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


#endif

aaa_c_connector.cpp

#include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

Compiling it, again, using a regular C++ compilation command:

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so

Now I have a shared library (libaaa_c_connector.so), that implements the C function AAA_sayHi(const char *name). I can now create a C main file and compile it all together:

main.c

#include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

Compiling it using a C compilation command:

gcc main.c -L. -laaa_c_connector -o c_aaa

I will need to set LD_LIBRARY_PATH to contain $PWD, and if I run the executable ./c_aaa, I will get the output I expect:

Hi David
Hi James

EDIT:

On some linux distributions, -laaa and -lstdc++ may also be required for the last compilation command. Thanks to @AlaaM. for the attention

Capitalistic answered 22/3, 2015 at 9:37 Comment(6)
you could also specify the lib link path with gcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaaAlkanet
what's usecpp.c?Harbard
The last compilation line should be: gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Note the -laaa and -lstdc+++Harbard
@AlaaM. someone edited my code and changed main.c to usecpp.c. I've just reverted it.... About the other comment, it should work also as is. The second compilation uses -laaa and it should probably be enough (I wrote it more than 1.5 years ago, I don't remember very well what I did, but I think I tested every line before posting)Capitalistic
@Capitalistic Oh OK I understand. And I tested it with Linaro arm-linux-gnueabihf-gcc and the 2 linking libs -laaa and -lstdc++ were necessary for it to work. Otherwise I get some linking errors...Harbard
Ok, added it as a note in the end of the post. Thanks for the attention!Capitalistic
B
6

Assuming the C++ API is C-compatible (no classes, templates, etc.), you can wrap it in extern "C" { ... }, just as you did when going the other way.

If you want to expose objects and other cute C++ stuff, you'll have to write a wrapper API.

Bertilla answered 30/4, 2010 at 11:41 Comment(4)
Not quite... the C++ library would need to be recompiled.Bartolome
Oh no. C++ API is fully object-oriented.Peery
@claws, see my article on making OOP-style C code.. and create a wrapper library using that style with a C interface, but an underlying C++ implementation. Then link to the C interface.Bartolome
You may want to take a look at wrapper 'tools' like swig, pyboost, ... that do equivalent stuff (yet not towards C...)Ideation
T
6

You will have to write a wrapper for C in C++ if you want to do this. C++ is backwards compatible, but C is not forwards compatible.

Trump answered 30/4, 2010 at 11:43 Comment(0)
O
3

export your C++ functions as extern "C" (aka C style symbols), or use the .def file format to define undecorated export symbols for the C++ linker when it creates the C++ library, then the C linker should have no troubles reading it

Oligosaccharide answered 30/4, 2010 at 11:40 Comment(0)
C
0
#include <iostream>

//////////////
// C++ code //
//////////////
struct A
{
  int i;
  int j;

  A() {i=1; j=2; std::cout << "class A created\n";}
  void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
  ~A() {std::cout << "class A destroyed\n";}
};

extern "C" {
  // this is the C code interface to the class A
  static void *createA (void)
  {
    // create a handle to the A class
    return (void *)(new A);
  }
  static void dumpA (void *thisPtr)
  {
    // call A->dump ()
    if (thisPtr != NULL) // I'm an anal retentive programmer
    {
      A *classPtr = static_cast<A *>(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast<A *>(thisPtr));
    }
  }
}

////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
  void *handle = createA();

  dumpA (handle);
  deleteA (handle);

  return 0;
}
Cabe answered 13/4, 2018 at 1:10 Comment(2)
Please consider add some explanation to your answers.Exarchate
A brief explanation of how the above answers the question makes the answer more useful to others.Mail

© 2022 - 2025 — McMap. All rights reserved.