I am creating programs using C. However, I require to use a lot of libraries that have API's only for C++. So, is it possible that I can create a shared object in C++ and then access its functionality using C?
If it is not possible to interface these codes, how do I get information from C++ code to C code? I tried calling C++ functions from C, but I get errors during linking when I include <string>
. So when I call C++ functions from C, should I only use that code which will be C compiler compatible?
#ifndef CPPFILE_H #define CPPFILE_H #ifdef __cplusplus extern "C" { #endif extern int myfunction(const char *filename); #ifdef __cplusplus } #endif #endif
#include "cppfile.hpp" #include <string> int myfunction(const char *filename) { String S(filename); return 0; }
#include "cppfile.hpp" int main(int argc, char **argv) { int i = myfunction(argv[1]); printf("%d\n", i); return 0; }
gcc -c cmain.c g++ -fPIC -shared -o cppfile.so cppfile.cpp
If the C++ compiler provides its own versions of the C headers, the versions of those headers used by the C compiler must be compatible. Oracle Developer Studio C and C++ compilers use compatible headers, and use the same C runtime library. They are fully compatible.
Shared libraries (also called dynamic libraries) are linked into the program in two stages. First, during compile time, the linker verifies that all the symbols (again, functions, variables and the like) required by the program, are either linked into the program, or in one of its shared libraries.
Simply put, A shared library/ Dynamic Library is a library that is loaded dynamically at runtime for each application that requires it. Dynamic Linking doesn't require the code to be copied, it is done by just placing name of the library in the binary file.
If you want to open a shared-library file, you would open it like any other binary file -- with a hex-editor (also called a binary-editor). There are several hex-editors in the standard repositories such as GHex (https://packages.ubuntu.com/xenial/ghex) or Bless (https://packages.ubuntu.com/xenial/bless).
You want something more like this (and here I will use a slightly more meaningful example):
#ifndef ANIMAL_H #define ANIMAL_H #ifdef __cplusplus class Animal { public: Animal() : age(0), height(0) {} Animal(int age, float height) : age(age), height(height) {} virtual ~Animal() {} int getAge(); void setAge(int new_age); float getHeight(); void setHeight(float new_height); private: int age; float height; // in metres! }; #endif /* __cplusplus */ #ifdef __cplusplus extern "C" { #endif struct animal; // a nice opaque type struct animal *animal_create(); struct animal *animal_create_init(int age, float height); void animal_destroy(struct animal *a); void animal_setage(struct animal *a, int new_age); void animal_setheight(struct animal *a, float new_height); int animal_getage(struct animal *a); float animal_getheight(struct animal *a); #ifdef __cplusplus } #endif #endif /* ANIMAL_H */
#include "animal.h" #define TO_CPP(a) (reinterpret_cast<Animal*>(a)) #define TO_C(a) (reinterpret_cast<animal*>(a)) void Animal::setAge(int new_age) { this->age = new_age; } int Animal::getAge() { return this->age; } void Animal::setHeight(float new_height) { this->height = new_height; } float Animal::getHeight() { return this->height; } animal *animal_create() { animal *a = TO_C(new Animal); return a; } animal *animal_create_init(int age, float height) { animal *a = TO_C(new Animal(age, height)); return a; } void animal_destroy(animal *a) { delete TO_CPP(a); } void animal_setage(animal *a, int new_age) { TO_CPP(a)->setAge(new_age); } void animal_setheight(animal *a, float new_height) { TO_CPP(a)->setHeight(new_height); } int animal_getage(animal *a) { TO_CPP(a)->getAge(); } float animal_getheight(animal *a) { TO_CPP(a)->getHeight(); }
#include "animal.h" #include <stdio.h> int main() { // 6'0" 25yo (perhaps a human? :P) struct animal *a = animal_create(25, 1.83); animal_setage(a, 26); // birthday printf("Age: %d\nHeight: %f", animal_getage(a), animal_getheight(a)); animal_destroy(a); return 0; }
#include "animal.h" #include <iostream> int main() { // 6'0" 25yo (perhaps a human? :P) Animal* a = new Animal(25, 1.83); a->setAge(26); // birthday std::cout << "Age: " << a->getAge() << std::endl; std::cout << "Height: " << a->getHeight(); delete a; return 0; }
So when you compile the library, you compile animal.cpp
with a C++ compiler. You can then link to it with C code, and use the animal_xxx
functions.
Note the use of struct animal
and Animal
. Animal
is a normal C++ type. It's exactly what it looks like. struct animal
, on the other hand, is an "opaque" type. That means that your C program can see it's there, and can have one, but it doesn't know what is inside it. All it knows is that it has a function that takes a struct animal*
.
In a real library you will want to have customisation points for memory allocation. So assuming this is the library libjungle
, you probably want at least jungle_setmalloc
and jungle_setfree
with sensible defaults. You can then set up the global new
and delete
in libjungle
's C++ code to use these user-defined functions.
This is entirely possible. Here is how, quickly: 1.) You have a header.h with a C API that doesn't include any Cplusiness.
#ifndef MIXEDCCPP_H #define MIXEDCCPP_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> // Any C-compatible headers will go here. // C API goes here. C Functions can't contain any CPPiness. void myclass_setName( void *pClassObj, const char *pName, int nameLen ); #ifdef __cplusplus } #endif #ifdef __cplusplus // Stuff that is only compatible with CPP goes here // __cplusplus section won't get processed while compiling C files. #include <vector> // CPP headers. class MyClass { // Classes etc. }; #endif // #ifdef __cplusplus #endif // MIXEDCCPP_H
Then in the .cpp, you simply create some C-API functions that can even include CPP right in them:
#include "mixedccpp.h" extern "C" { // C API goes here. C Functions can't contain any CPPiness in their prototypes. void myclass_setName( void *pClassObj, const char *pName, int nameLen ) { // But CPP knowledge can go inside the function - no problem, since this is a CPP file. MyClass *pMyClass = static_cast<MyClass *>(pClassObj); pMyClass->setName( pName, nameLen ); } } // #extern "C" // CPP Stuff goes here... or vice-versa.
In your case, you don't actually need any CPP code declared in your header since you are calling external libraries. But you need to create C-compatible functions in your CPP file which can call out to CPP libraries. Use extern "C" for those functions that need to be called from C files, and then use C-structs instead of classes and, if classes are needed, use void * to point to them and then cast them back to their class from the C function any time you need to access them. A standard makefile should be able to compile this just fine, assuming it compiles .cpp files as .cpp and understands extern "C" {}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With