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


90

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


1
サンプルコードとg++エラーメッセージをいくつか書いていただけませんか
Matthieu Rouget 2013年

7
C ++コンパイラでコンパイルすると、C ++になります。CコードはC ++コンパイラでコンパイルする必要はありません。それらは異なる言語です。コードは有効なC ++ではないため、C ++コンパイラでコンパイルされません。
xaxxon 2013年

3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
自閉症

2
私の試み:void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_ 2013年

1
これは、特にC(C ++ではなく)コンパイラをCコードに使用する方法を示す良い答えがあるため、開いたままにしておく必要があります。
クリス・ストラットン2013年

回答:


126

次のようにCコードをコンパイルします。

gcc -c -o somecode.o somecode.c

次に、次のようなC ++コード:

g++ -c -o othercode.o othercode.cpp

次に、C ++リンカーを使用してそれらをリンクします。

g++ -o yourprogram somecode.o othercode.o

また、C関数の宣言を含めるときに、C ++コンパイラにCヘッダーが来ることを通知する必要があります。だからでothercode.cpp始まります:

extern "C" {
#include "somecode.h"
}

somecode.h 次のようなものを含める必要があります:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(この例ではgccを使用しましたが、原則はどのコンパイラでも同じです。それぞれCおよびC ++として個別にビルドし、リンクします。)


7
@Arne良い点。一部の人々extern "C"は、ヘッダーのを#ifdef __cplusplus。でラップすることにより、CでC ++をシュミアします。
2013年

@Arne以下の私の答えを参照してください。ご覧のとおり、私はそのうちの1人です;)
gx_ 2013年

1
どうもありがとうございます !それは私にとって非常に役に立ちました:)
Hesham Eraqi 2014年

次のエラーが発生しました。エラー:#337:リンケージ仕様は以前の「foo」(1行目で宣言)と互換性がありません。コンパイルは正常です。誰か説明できますか?
faizanHussainRabbani 2015

@FaizanRabbani、詳細がないわけではありません。
ファルケン教授2015

61

きれいに分離された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コードである必要があり#ifdef、「いつか別の言語からこれを呼び出す」という意味ではありません。一部のC ++プログラマーがC関数を呼び出す場合、それを行う方法は彼らの問題であり、あなたの問題ではありません。また、C ++プログラマーの場合、Cヘッダーは自分のものではない可能性があり、変更しないでください。したがって、マングルされていない関数名(つまり、extern "C")の処理はC ++コードに属します。

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


7
スジは通ってるようだ。根拠のための1
gx_

最後に、これの完全に明確な説明。トンありがとう!
Daniel Soutar 2018年

16

私はファルケン教授の答えに同意しますが、アルネ・メルツのコメントの後、完全な例を挙げたいと思います(最も重要な部分は#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
    // ...
}

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

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

void foo(void);

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

extern "C" {

void foo(void);

}

4
thb、私は#ifdef __cplusplusCコードが好きではありません。Cコードは下位レベルであり、いつかC ++コードから呼び出される可能性がある場合でも気にする必要はありません。#ifdefC ++で記述されたライブラリにCバインディングヘッダーを提供する場合にのみ、C ++コードでのみ使用されるImo 。その逆ではありません。
Arne Mertz 2013年

2
@ Prof.Falkenはもちろんですが、これはCコードではなく、C ++コードの「下向き」の互換性を提供できるようにすることを目的とした定義です。
Arne Mertz 2013年

1

この答えは、アルネの論理的根拠が正しかった事例に触発されています。ベンダーは、かつて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規則が機能する可能性があります。ただし、複雑なライブラリの場合は、いずれかの言語を選択してそれを使用するのが最善です。一方の言語をサポートすると、両方をサポートしようとするよりもメンテナンスが少なくて済みます。

これは、UbuntuLinuxでコンパイルするために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

# -*- 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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.