- ベストアンサー
クラスを使ったC++の関数をCで呼び出す方法
- C++の関数をCプログラムから呼び出すためには、extern "C"を宣言し、Cコンパイラでリンクする必要があります。
- C++関数内で使用されるクラスについても、extern "C"の記述が必要です。
- コンパイルの際のオプションを使用することで、C++の機能をCから呼び出すことができます。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
> ここで静的にC++の共通ライブラリをリンクする必要があるということだろうと考え、 考え方はあっていると思います。 ただ、-dy とか -dn は、コンパイルオプションに指定された共有オブジェクトや -l オプションに効いてくる(はず)ので、暗黙にリンクされているものには有効では ない(はず)だということ。 もし、聴いたとしても、静的なアーカイブファイルが無ければ -dn オプション自体 意味が無いこと、といったあたりが考えられます。 > そもそも、dllに対してlddコマンドを使う ことがナンセンスなのでしょうか? そんなことはありません。何も出力されないのは、対象としている dll に関する外部 参照が解決されているだけのことです。 リンクエラーになっているのは、どんな名前のシンボルなのでしょう? シンボル名が分かれば、c++ のライブラリに対して片っ端から nm コマンドで、その シンボル名が含まれているものを探し、リンクのオプションに追加する、という荒業(*) もあります。 (*) 仕組みの理解、というよりは、とりあえず動けば良い、という意味で :-) # 近くに、Solaris の開発環境が無かったかなあ # ちょっと自分でも試してみたくなった
その他の回答 (3)
- a-kuma
- ベストアンサー率50% (1122/2211)
ごめんなさい、手が滑った。分かると思うけど、 > % CC -o libTEST.o -G t.o は、 % CC -o libTEST.so -G t.o の間違いです。
- a-kuma
- ベストアンサー率50% (1122/2211)
> 「 分からなければ、聞いてください」というお言葉に甘えて、もう一度たずねさせてください。 (長いので、以下、割愛) よろしいですよ。 一応、確認ですが、補足でかかれた t.cpp では t.h がインクルードされている、ということで 良いですね。その後のくだりで nm コマンドによる確認がなされているので、間違いないと 思いますが。 リンクエラーが発生する原因は、多分、以下のようなことによるものです。 c++ では、プログラムをリンクするにあたって、シンボルの解釈以外に以下の ことをリンカが考慮する(というよりは、リンカに指示する)必要があります。 ・例外周りの初期化や catch されない例外の扱い ・静的なスコープを持つオブジェクトは main() が呼ばれる前にコンストラクタが呼ばれる このため main() のシンボルを含むオブジェクトをリンクする際には、c だけの ときに比べて、少しだけ余計な処理をしなくてはいけなくなります。 rokuroh さんが直面しているプロジェクト(多分、仕事ですよね)で、c++ を分からない 人間が作っている部分に対して、c++ で作成した機能を提供したい、ってことですよね。 一番面倒が無いのは、共有オブジェクトで提供しちゃうことです。dll を CC でリンクまで しちゃえば、あまり細かいことには、頭を悩ませなくて済みます(の、はずです)。 % cc -c t.cpp % CC -o libTEST.o -G t.o % cc -c test.c % cc -o test test.o -lTEST でも、ひとりは、c++ の仕組みをおさえておく必要があったりします。 例えば、dlopen() を使って、この dll を使うときにどうなるか、とか。 # 私も苦労したので、懐かしかったりします :-)
- a-kuma
- ベストアンサー率50% (1122/2211)
extern "C" で宣言された関数内で処理が閉じていれば、何ら問題ありません。 ざっくり言うと、呼び出したCの関数側で、その関数内でクラスを使った処理を 行っているかどうかが分からない(戻り値が int で、引数が void のような) 場合には OK です。 ソースをコンパイルしてできたオブジェクトファイルには、関数の名前が シンボル名として埋め込まれています。リンカは、このシンボル名を手がかりに 呼び出し関係を解決してゆきます。 c++ には、関数のオーバーロードの機能があるために、関数名だけでは、どの 処理を呼び出してよいか区別がつきませんので、引数の情報もシンボル名に 含めます。 逆にこうしてしまうと、c からは見つけることができなくなってしまいます。 そのために extern "C" があります。関数の名前がひとつであることが保証され ているならば extern "C" をつけることで、c の規約にのっとったシンボル名を 使ってオブジェクトファイルを作成します。 試しに、ひとつだけ関数を持つファイルのオブジェクトを作り、そのシンボル 情報を見てみると、感覚として分かります。例えば extern "C" int func(int, int); int func(int a, int b) { return a * b; } というようなソースを extern "C" の行を生かしたり、殺したりして % CC -c t.cpp % nm t.o としてみてください。 そのあたりの感覚が分かれば > この場合、C++関数内で使用されるクラスについても、extern "C"の記述が必要でしょうか? という疑問が的を外れたものであることが分かるはずです。 # 分からなければ、聞いてください
補足
ご回答ありがとうございます。 「 分からなければ、聞いてください」というお言葉に甘えて、もう一度たずねさせてください。 以下のような関数を定義しているファイルと int func(int a, int b) { printf("printf test"); cout << "cout test" << endl; return a*b; } そのヘッダーファイル #ifdef __cplusplus extern "C" { #endif int func(int, int); #ifdef __cplusplus } #endif これらを %CC -c t.cpp としてコンパイルします。 そして、以下のファイルの中でその関数を使用します。 #include <string.h> #include "t.h" main(){ int ret; ret = func(1,2); printf("ret %d\n", ret); } 上記ファイルのコンパイルを以下のように行います。 %cc -c test.c このコンパイルは問題ありません。 そして実行ファイルを以下のように作成します。 %cc -o testexe t.o test.o この際にリンカのエラーが発生します。 下記のようにCCを使用する場合は、問題ありません。 %CC -o testext t.o test.o t.oをnmコマンドで確認すると確かにextern "C" を宣言することで、関数funcについては、修飾される情報は消えていますが、coutを使うことで、cとしては解釈できない情報が付属しているようです。 以前の質問で例として挙げられているプログラムにcoutが使われていたので、使えるものと思い、はじめの質問にその旨書きましたが、上記のような自分の手順では、使えないようです。実際には、自身で定義したクラスの使用を考えていますが、問題点の切り分けの面では上のようなcoutの使用不可で条件を満たしていると思います。 長々とした質問となり、申しわけありません。 nmコマンドについての説明など大変ためになりました。 ご回答いただければ幸いです。
補足
回答ありがとうございます。 単に「こうやればよい」というだけでない、指針のある返答を受けることができて感謝しています。 現在、ご意見を参考に、試行錯誤していますが(とはいえ、お察しのとおりプロジェクトの一部分でもあるので、この件のみに関わってばかりもいられないのですが)、今回一応の、経過報告とさせていただきたいと思います。 以下の手順でコンパイルを行ったとき % CC -c t.cpp % CC -o libTEST.so -G t.o % cc -c test.c % cc -L. -o testexe test.o -lTEST <-ここでエラー 最後の処理でリンクすることができず、 % CC -L. -o testexe test.o -lTEST <-これはOK 上記であれば問題ありません。 ということは、 % CC -o libTEST.so -G t.o ここで静的にC++の共通ライブラリをリンクする必要があるということだろうと考え、-dnオプションなど試してみているのですが、エラーにもならない代わりに、特にその後の処理の状況に変化がおきるわけでもありません。 また、libTEST.soに対して、lddコマンドで確認してみても、オプションの有無に関わらず、何の出力も行われません(そもそも、dllに対してlddコマンドを使うことがナンセンスなのでしょうか?)。 自分にはこのあたりの理解がいかにも欠けていると感じさせられてしまいます。 もう少し、調べていこうと考えています。