CからC ++関数を呼び出す方法は?


84

私はこれを知っている。

C ++からC関数を呼び出す:

アプリケーションがC ++であり、Cで記述されたライブラリから関数を呼び出さなければならなかった場合、

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

これは名前C_library_functionを壊さず、リンカーは入力* .libファイルで同じ名前を見つけ、問題は解決されます。

CからC ++関数を呼び出す???

しかし、ここではCで書かれた大きなアプリケーションを拡張しているので、C ++で書かれたライブラリを使用する必要があります。ここでは、C ++の名前マングリングが問題を引き起こしています。リンカーは、未解決のシンボルについて不平を言っています。CプロジェクトでC ++コンパイラを使用することはできません。これは、他の多くのものを壊してしまうためです。抜け道は何ですか?

ちなみに私はMSVCを使用しています



あなたは、C ++ライブラリを制御する場合:stackoverflow.com/questions/12615683/...
チロSantilli郝海东冠状病六四事件法轮功

回答:


95

C ++コードの機能を公開するためのCAPIを作成する必要があります。基本的に、extern "C"として宣言され、C ++ライブラリをラップする純粋なCAPI(クラスを使用しないなど)を持つC ++コードを記述する必要があります。次に、作成した純粋なCラッパーライブラリを使用します。

Cがオブジェクト指向ではない場合でも、CAPIはオプションでオブジェクト指向スタイルに従うことができます。例:

 // *.h file
 // ...
 #ifdef __cplusplus
 #define EXTERNC extern "C"
 #else
 #define EXTERNC
 #endif

 typedef void* mylibrary_mytype_t;

 EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
 EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
 EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

 #undef EXTERNC
 // ...


 // *.cpp file
 mylibrary_mytype_t mylibrary_mytype_init() {
   return new MyType;
 }

 void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
    MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
    delete typed_ptr;
 }

 void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
    MyType* typed_self = static_cast<MyType*>(untyped_self);
    typed_self->doIt(param);
 }


1
最終的な実行可能ファイルをどのようにリンクしますか?CまたはC ++を使用していますか?(Cを使用した場合、リンカーはC ++関数を見つけることができませんでした。C++を使用した場合、リンカーはstd :: coutを見つけることができませんでした。)
Zack

1
@Zack依存関係を一時的に含むC ++コンパイラ/リンカー(リンカーによって暗黙的にリンクされる可能性のあるC ++標準ライブラリを含む)を使用して静的ライブラリを作成する場合、その静的ライブラリをCコードに対してリンクすることができるはずです。 Cコンパイラ/リンカー。
Michael Aaron Safyan 2016年

テンプレートをC ++からCにエクスポートできますか?
MarcusJ

ライブラリをC ++とCコードの両方で呼び出す必要があるため、「ハイブリッド」動作が発生します。また、リンク時に「未定義の参照」エラーを回避するために*.cpp file、関数を#ifdef _cplusplus+にラップする必要がありましextern "C"た。
ココノップ

41

私は次の方法でそれを行います:

(MSVCを使用している場合は、GCCコンパイルコマンドを無視してください)

ファイルaaa.h、aaa.cppで定義されているAAAという名前のC ++クラスがあり、クラスAAAsayHi(con​​st char * name)という名前のメソッドがあり、Cコードで有効にしたいとします。

クラスAAAのC ++コード-純粋なC ++、私はそれを変更しません:

aaa.h

#ifndef AAA_H
#define AAA_H

class AAA {
    public:
        AAA();
        void sayHi(const char *name);
};

#endif

aaa.cpp

#include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const char *name) {
    std::cout << "Hi " << name << std::endl;
}

C ++で通常行われているようにこのクラスをコンパイルします。このコードは、Cコードで使用されることを「知りません」。コマンドの使用:

g++ -fpic -shared aaa.cpp -o libaaa.so

さて、これもC ++で、Cコネクタを作成します。ファイルaaa_c_connector.h、aaa_c_connector.cppで定義します。このコネクタは、AAAのインスタンスを使用してそのメソッドを呼び出すAAA_sayHi(cosnt char * name)という名前のC関数を定義します。

aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus
extern "C" {
#endif

void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


#endif

aaa_c_connector.cpp

#include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

再度、通常のC ++コンパイルコマンドを使用してコンパイルします。

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so

これで、C関数AAA_sayHi(con​​st char * name)を実装する共有ライブラリ(libaaa_c_connector.so )ができました。これで、Cメインファイルを作成して、すべて一緒にコンパイルできます。

main.c

#include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

Cコンパイルコマンドを使用してコンパイルします。

gcc main.c -L. -laaa_c_connector -o c_aaa

LD_LIBRARY_PATHに$ PWDを含めるように設定する必要があります。実行可能ファイル./c_aaaを実行すると、期待どおりの出力が得られます。

Hi David
Hi James

編集:

一部のLinuxディストリビューションでは、-laaa-lstdc++も最後のコンパイルコマンドのために必要となる場合があります。@AlaaMに感謝します。注意のために


1
次のgcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
コマンドで

usecpp.cですか?
Alaa M. 2016

1
最後のコンパイル行は次のようになりますgcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa-laaaおよび-lstdc+++
Alaa M. 2016

@AlaaM。誰かが私のコードを編集してに変更main.cしましたusecpp.c。元に戻したところですが……他のコメントについては、そのままでも動作するはずです。2番目のコンパイルは使用して-laaaおり、おそらくそれで十分です(1。5年以上前に書いたので、何をしたかはよく覚えていませんが、投稿する前にすべての行をテストしたと思います)
SomethingSomething

1
わかりました。投稿の最後にメモとして追加しました。注目してくれてありがとう!
SomethingSomething

6

これを行う場合は、C ++でCのラッパーを作成する必要があります。C ++には下位互換性がありますが、Cには上位互換性がありません。


5

C ++ APIがC互換(クラス、テンプレートなどがない)であると仮定すると、extern "C" { ... }他の方法で行った場合と同じように、それをラップすることができます。

オブジェクトやその他のかわいいC ++のものを公開したい場合は、ラッパーAPIを作成する必要があります。


完全ではありません... C ++ライブラリを再コンパイルする必要があります。
Michael Aaron Safyan 2010

大野。C ++ APIは完全にオブジェクト指向です。

2
@ claws、OOPスタイルのCコードの作成に関する私の記事を参照してください。Cインターフェイスでそのスタイルを使用してラッパーライブラリを作成しますが、基盤となるC ++実装です。次に、Cインターフェイスにリンクします。
Michael Aaron Safyan 2010

swig、pyboostなどのラッパーの「ツール」を見てみることをお勧めします...同等のことを行います(ただし、Cには向いていません...)
xtofl 2010

2

C ++関数をextern "C"(別名Cスタイルシンボル)としてエクスポートするか、C ++ライブラリの作成時に.defファイル形式を使用してC ++リンカーの装飾されていないエクスポートシンボルを定義すると、Cリンカーは問題なく読み取ることができます。


0
#include <iostream>

//////////////
// C++ code //
//////////////
struct A
{
  int i;
  int j;

  A() {i=1; j=2; std::cout << "class A created\n";}
  void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
  ~A() {std::cout << "class A destroyed\n";}
};

extern "C" {
  // this is the C code interface to the class A
  static void *createA (void)
  {
    // create a handle to the A class
    return (void *)(new A);
  }
  static void dumpA (void *thisPtr)
  {
    // call A->dump ()
    if (thisPtr != NULL) // I'm an anal retentive programmer
    {
      A *classPtr = static_cast<A *>(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast<A *>(thisPtr));
    }
  }
}

////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
  void *handle = createA();

  dumpA (handle);
  deleteA (handle);

  return 0;
}

2
あなたの答えにいくつかの説明を追加することを検討してください。
HMD

上記が質問にどのように答えるの簡単な説明は、答えを他の人にとってより有用にします。
SOS

-3

関数宣言の前にextern“ C”キーワードを付けることができます。

extern“ C” int Mycppfunction()

{{

//コードはここにあります

0を返します。

}

その他の例については、「extern」キーワードについてGoogleでさらに検索できます。あなたはさらにいくつかのことをする必要があります、しかしあなたがグーグルからたくさんの例を得るのは難しいことではありません。


単一の説明ではなく、3つの反対票。いつものように素晴らしい、SOコミュニティ。
Zimano
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.