web-dev-qa-db-ja.com

C ++で作成された共有ライブラリをCプログラムで使用できますか?

Cを使用してプログラムを作成しています。ただし、C++専用のAPIを備えた多くのライブラリを使用する必要があります。では、C++で共有オブジェクトを作成し、Cを使用してその機能にアクセスすることは可能ですか?

  1. 渡して返すデータはC互換のデータ型だけです。
  2. ここでは、cppへの変換または移行はオプションではありません。

これらのコードをインターフェイスできない場合、C++コードからCコードに情報を取得するにはどうすればよいですか? CからC++関数を呼び出してみましたが、<string>を含めると、リンク中にエラーが発生します。したがって、CからC++関数を呼び出す場合、Cコンパイラー互換のコードのみを使用する必要がありますか?

C++ヘッダー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ファイル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
30
kicker86

あなたはこのようなものを望んでいます(そしてここでは少し意味のある例を使用します):

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 animalおよびAnimalの使用に注意してください。 Animalは通常のC++型です。それはまさにそれがどのように見えるかです。一方、struct animalは「不透明」タイプです。つまり、Cプログラムはそれがそこにあることを確認でき、1つ持つことができますが、その中に何があるのか​​はわかりません。知っているのは、それがstruct animal*を取る関数を持っているということだけです。

実際のライブラリでは、メモリ割り当てのカスタマイズポイントが必要になります。したがって、これがライブラリlibjungleであるとすると、少なくともjungle_setmallocjungle_setfreeが適切なデフォルトで必要になります。次に、newのC++コードでグローバルdeleteおよびlibjungleを設定して、これらのユーザー定義関数を使用できます。

28
Miles Rout

これは完全に可能です。以下はその方法です。1.)Cplusinessを含まないC APIを含むheader.hがあります。

#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ライブラリを呼び出すことができるCPPファイルにC互換の関数を作成する必要があります。 Cファイルから呼び出す必要がある関数にはextern "C"を使用し、クラスの代わりにC構造体を使用します。クラスが必要な場合は、void *を使用してそれらをポイントし、それらをクラスからクラスにキャストします。 Cは、それらにアクセスする必要があるときにいつでも機能します。標準のメイクファイルは、.cppファイルを.cppとしてコンパイルし、extern "C" {}を理解していると仮定して、これを問題なくコンパイルできるはずです。

10
c.fogelklou

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>_に_cmain.c_を追加します。
  2. _cppfile.cpp_でstd::string S(filename);を使用します。

また、引数なしでプログラムを呼び出すと、次のようになります。

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

テストプログラムであっても、誤用を防ぐ必要があります。

4