coolharsh55
coolharsh55

Reputation: 1199

Can I use shared library created in C++ in a C program?

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?

  1. The only data I would be passing and returning would be C compatible data types.
  2. Converting or migrating to cpp is not an option here.

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?

C++ header cppfile.hpp

#ifndef CPPFILE_H
#define CPPFILE_H
    #ifdef __cplusplus
    extern "C" {
    #endif

    extern int myfunction(const char *filename);

   #ifdef __cplusplus
   }
   #endif
#endif

C++ file cppfile.cpp

#include "cppfile.hpp"
#include <string>
int myfunction(const char *filename) {
    String S(filename);
    return 0;
}

C file cmain.c

#include "cppfile.hpp"
int main(int argc, char **argv)
{
     int i = myfunction(argv[1]);
     printf("%d\n", i);
     return 0;
}

Compiling:

gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp

Upvotes: 37

Views: 26306

Answers (3)

mrr
mrr

Reputation: 1233

You want something more like this (and here I will use a slightly more meaningful example):

C/C++ header - animal.h

#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 */

C++ animal implementation file - animal.cpp

#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();
}

C client code - main.c

#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;
}

C++ client code - main.cpp

#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.

Upvotes: 34

c.fogelklou
c.fogelklou

Reputation: 1801

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" {}

Upvotes: 10

Jonathan Leffler
Jonathan Leffler

Reputation: 753705

Your C code cannot use the C++ header <string>. You have to ensure that the functions in the C++ API that are to be called from C are declared extern "C" (as you have), and use only types recognized by a C compiler (as you have).

You also need to link with the C++ compiler if any of your code is in C++. You can do it otherwise if you're prepared to spend a lot of energy getting the loader options right, but it is far simpler just to use the C++ compiler:

gcc -c cmain.c
g++ -fPIC -shared -o cppfile.so cppfile.cpp
g++ -o cmain cmain.o cppfile.so

Of course, you need to:

  1. Add #include <stdio.h> in cmain.c.
  2. Use std::string S(filename); in cppfile.cpp.

Also, if the program is invoked without arguments, you get:

$ ./cmain
terminate called throwing an exceptionAbort trap: 6
$ ./cmain x3
0
$

You need to protect against misuse, even in test programs.

Upvotes: 4

Related Questions