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

前端 未结 3 1671
情话喂你
情话喂你 2020-12-04 08:26

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 t

相关标签:
3条回答
  • 2020-12-04 09:21

    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.

    0 讨论(0)
  • 2020-12-04 09:31

    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.

    0 讨论(0)
  • 2020-12-04 09:31

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

    0 讨论(0)
提交回复
热议问题