我可以在C程序中使用C ++创建的共享库吗?

时间:2013-02-17 04:23:35

标签: c++ c compilation shared-libraries

我正在使用C创建程序。但是,我需要使用许多只有C ++的API。那么,我是否有可能在C ++中创建共享对象,然后使用C?

访问其功能
  1. 我传递和返回的唯一数据是C兼容数据类型。
  2. 此处无法转换或迁移到cpp。
  3. 如果无法连接这些代码,如何从C ++代码获取信息到C代码? 我尝试从C调用C ++函数,但是当我包含<string>时,我在链接期间遇到错误。所以当我从C调用C ++函数时,我应该只使用那些与C编译器兼容的代码吗?

    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 ++文件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;
    }
    

    编译:

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

3 个答案:

答案 0 :(得分:28)

你想要更像这样的东西(在这里我将使用一个更有意义的例子):

C / C ++标题 - 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.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客户端代码 - 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 ++客户端代码 - 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;
}

因此,在编译库时,使用C ++编译器编译animal.cpp。然后,您可以使用C代码链接到它,并使用animal_xxx函数。

请注意struct animalAnimal的使用。 Animal是普通的C ++类型。它正是它的样子。另一方面,struct animal是一种“不透明”类型。这意味着你的C程序可以看到它,并且可以有一个,但它不知道里面是什么。它只知道它有一个函数需要struct animal*

在真实的库中,您需要具有内存分配的自定义点。因此,假设这是库libjungle,您可能希望至少jungle_setmallocjungle_setfree具有合理的默认值。然后,您可以在new的C ++代码中设置全局deletelibjungle以使用这些用户定义的函数。

答案 1 :(得分:10)

这完全有可能。以下是快速: 1.)你有一个带有C API的header.h,它不包含任何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

然后在.cpp中,您只需创建一些甚至可以包含CPP的C-API函数:

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

在您的情况下,您实际上不需要在标头中声明任何CPP代码,因为您正在调用外部库。但是您需要在CPP文件中创建C兼容的函数,这些函数可以调用CPP库。对于那些需要从C文件中调用的函数,使用extern“C”,然后使用C-structs而不是类,如果需要类,则使用void *指向它们,然后将它们从它们转发回来只要您需要访问它们,C函数就可以运行。假设它将.cpp文件编译为.cpp并理解extern“C”{}

,标准的makefile应该能够很好地编译它。

答案 2 :(得分:4)

您的C代码无法使用C ++标头<string>。您必须确保要从C调用的C ++ API中的函数声明为extern "C"(如您所有),并且只使用C编译器识别的类型(如您所愿)。

如果您的任何代码都在C ++中,您还需要与C ++编译器链接。如果你准备好花大量精力来获得正确的加载器选项,你可以做到这一点,但是使用C ++编译器要简单得多:

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

当然,您需要:

  1. #include <stdio.h>
  2. 中添加cmain.c
  3. std::string S(filename);。{/ li>中使用cppfile.cpp

    此外,如果在没有参数的情况下调用程序,则会得到:

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

    即使在测试程序中,您也需要防止滥用。

相关问题