web-dev-qa-db-ja.com

C ++コードからC関数を呼び出す

C++から呼び出したいC関数があります。 C関数がg ++を使用してコンパイルできなかったため、「extern "C" void foo()」のようなアプローチを使用できませんでした。ただし、gccを使用してコンパイルできます。 C++から関数を呼び出す方法はありますか?

79
Dangila

他の回答とコメントから断片を集めて、CとC++のコードをきれいに分離した例を示します。

Cパート:

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

gcc -c -o foo.o foo.cでこれをコンパイルします。

C++パーツ:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

g++ -c -o bar.o bar.cppでこれをコンパイルします

そして、それをすべて一緒にリンクします:

g++ -o myfoobar foo.o bar.o

理由: CコードはプレーンなCコードである必要があり、「いつか別の言語からこれを呼び出す」ための#ifdefsはありません。一部のC++プログラマがC関数を呼び出す場合、それはtheirの問題であり、あなたの問題ではありません。また、C++プログラマーの場合、Cヘッダーは自分のものではない可能性があるため、変更しないでください。したがって、マングルされていない関数名(つまり、extern "C")の処理はC++コードに属します。

もちろん、Cヘッダーをextern "C"宣言にラップする以外は何もしない便利なC++ヘッダーを自分で作成することもできます。

55
Arne Mertz

Prof。Falken's answer に同意しますが、Arne Mertzのコメントの後、完全な例を挙げたいと思います(最も重要な部分は#ifdef __cplusplusです):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

次に、ファルケン教授の指示に従ってコンパイルとリンクを行います。

これは、gccでコンパイルするとき、マクロ__cplusplusが定義されていないため、somecode.hに含まれるヘッダーsomecode.cが前処理後に次のようになるためです。

void foo(void);

g++でコンパイルする場合、__cplusplusisが定義されるため、othercode.cppに含まれるヘッダーは次のようになります。

extern "C" {

void foo(void);

}
16
gx_

この答えは、アルネの理論的根拠が正しかった場合に着想を得ています。ベンダーは、かつてCとC++の両方をサポートしていたライブラリを作成しました。ただし、最新バージョンではCのみがサポートされていました。コードに残っている次の痕跡指令は誤解を招くものでした。

#ifdef __cplusplus
extern "C" {
#endif

これには、C++でコンパイルするのに数時間かかりました。 C++からCを呼び出すだけの方がずっと簡単でした。

Ifdef __cplusplus規則は、単一の責任原則に違反しています。この規則を使用するコードは、2つのことを同時に実行しようとしています。

  • (1)Cで関数を実行する-および-
  • (2)C++で同じ関数を実行する

それは、アメリカ英語とイギリス英語の両方を同時に書き込もうとするようなものです。これは不必要に#ifdef __thequeensenglishスパナ#Elif __yankeeenglishレンチ#else不要なツールを投げてしまい、#endifをコードに読みにくくする役に立たないツールです。

単純なコードと小さなライブラリの場合、ifdef __cplusplus規則が機能する場合があります。ただし、複雑なライブラリの場合は、いずれかの言語を選択してそれを使用することをお勧めします。いずれかの言語をサポートすると、両方をサポートするよりもメンテナンスが少なくなります。

これは、Urban LinuxでコンパイルするためにArneのコードに加えた変更の記録です。

foo.h

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"
#include <stdio.h>

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

メイクファイル

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c
0
Agriculturist