クラスを使ったC++の関数をCで呼び出す方法

このQ&Aのポイント
  • C++の関数をCプログラムから呼び出すためには、extern "C"を宣言し、Cコンパイラでリンクする必要があります。
  • C++関数内で使用されるクラスについても、extern "C"の記述が必要です。
  • コンパイルの際のオプションを使用することで、C++の機能をCから呼び出すことができます。
回答を見る
  • ベストアンサー

クラスを使ったC++の関数をCで呼び出すには?

 QNo.152285と類似の質問になりますが、独自に用意したクラスを内部で使用し、extern "C" を宣言した、C++の関数をCプログラムから呼び出し、Cコンパイラでリンクすることを考えています。  この場合、C++関数内で使用されるクラスについても、extern "C"の記述が必要でしょうか?  またその記述の仕方はどのようになるでしょうか?  あるいは、そういったことはできないのでしょうか?(coutが使えることを考えると、できないわけではないとも思っていますが)  あるいは、コンパイルの際の、オプションなどで解決できるでしょうか?  クラスはnewで生成して使用しています。 環境はソラリスのCCとccを使っています。 どうか、ご回答よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.4

> ここで静的にC++の共通ライブラリをリンクする必要があるということだろうと考え、 考え方はあっていると思います。 ただ、-dy とか -dn は、コンパイルオプションに指定された共有オブジェクトや -l オプションに効いてくる(はず)ので、暗黙にリンクされているものには有効では ない(はず)だということ。 もし、聴いたとしても、静的なアーカイブファイルが無ければ -dn オプション自体 意味が無いこと、といったあたりが考えられます。 > そもそも、dllに対してlddコマンドを使う ことがナンセンスなのでしょうか? そんなことはありません。何も出力されないのは、対象としている dll に関する外部 参照が解決されているだけのことです。 リンクエラーになっているのは、どんな名前のシンボルなのでしょう? シンボル名が分かれば、c++ のライブラリに対して片っ端から nm コマンドで、その シンボル名が含まれているものを探し、リンクのオプションに追加する、という荒業(*) もあります。   (*) 仕組みの理解、というよりは、とりあえず動けば良い、という意味で :-) # 近くに、Solaris の開発環境が無かったかなあ # ちょっと自分でも試してみたくなった

その他の回答 (3)

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.3

ごめんなさい、手が滑った。分かると思うけど、 > % CC -o libTEST.o -G t.o は、 % CC -o libTEST.so -G t.o の間違いです。

rokuroh
質問者

補足

回答ありがとうございます。 単に「こうやればよい」というだけでない、指針のある返答を受けることができて感謝しています。 現在、ご意見を参考に、試行錯誤していますが(とはいえ、お察しのとおりプロジェクトの一部分でもあるので、この件のみに関わってばかりもいられないのですが)、今回一応の、経過報告とさせていただきたいと思います。 以下の手順でコンパイルを行ったとき % 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コマンドを使うことがナンセンスなのでしょうか?)。 自分にはこのあたりの理解がいかにも欠けていると感じさせられてしまいます。 もう少し、調べていこうと考えています。

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.2

> 「 分からなければ、聞いてください」というお言葉に甘えて、もう一度たずねさせてください。 (長いので、以下、割愛) よろしいですよ。 一応、確認ですが、補足でかかれた 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)
回答No.1

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"の記述が必要でしょうか? という疑問が的を外れたものであることが分かるはずです。 # 分からなければ、聞いてください

rokuroh
質問者

補足

ご回答ありがとうございます。 「 分からなければ、聞いてください」というお言葉に甘えて、もう一度たずねさせてください。 以下のような関数を定義しているファイルと 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コマンドについての説明など大変ためになりました。 ご回答いただければ幸いです。

関連するQ&A

  • C++の関数をCで呼ぶには??

    C++の関数をCで呼ぶには、「extern C」を使用することが有名だと思いますが、これだとC++で記述したソースコードにextern Cを追加することにあると思います。 では、C++のほうはまったく手をつけず、C++の関数をCで呼ぶことは可能なのでしょうか? 知っている方がいましたらよろしくお願い致します。

  • CからC++の関数呼び出し

    CからC++の関数を呼び出して値を取得したいのですが、コンパイルエラーになってしまいます。 呼び出す関数内でさらに同じクラスの関数を呼び出して値をreturnしたいだけなのですが、 ご教示願います。 ソースは下記のような感じです。 Cソース(AAA.c) main(){ ・・・・・ if ( funcA() ){ ・・・・ } } C++ソース(BBB.cpp) int clsB::funcB { return iflg; } extern "C" { int funcA() { return funcB(); } } ※上記externは特に何かの関数内に書いているわけではありません。 C++ヘッダ(BBB.hpp) class clsB : public XXX { private: int iflg; ・・・・ public: int funcB(); ・・・・ } 上記にコンパイルすると、 externしている箇所でfuncBはスコープにありません?のようなエラーが出てしまいます。 ラッパ関数を使って使用するなどと聞いたのですが、やり方がいまいちわかりません。 どうか教えていただけますでしょうか。 また、他にやり方があるかと思いますが、いいやり方があれば教えていただけると幸いです。 以上です、よろしくお願いいたします。 不足内容あれば指定願います。

  • 「.c」拡張子でC++文法を使用したソースのコンパイルについて

    すみません、C&C++初心者です。 よく分からないので教えて下さい。 「.c」拡張子のファイルなのですが、以下C++の文法が使用されたソースがあります。 ・変数宣言が関数内の処理途中でされている ・構造体の変数宣言で「struct」の記述が省略されている ・const変数がswitch分のcaseラベルに使用されている これを以下環境でコンパイルしたところ、エラーとならず正常終了しました。 OS:HP-UX コンパイラ:aCC です。 (ちなみにwindows環境(win2003sv、clコンパイラ)でコンパイルしたところ、想定通りC++文法箇所でエラーになりました) 自分なりに調べた限り、C++対応コンパイラというものは、拡張子によって 「.c」ならC言語、「.cc」「.cpp」ならC++、といった判断をしてコンパイルする、 といった記述がよく見受けられました。 しかし本件の場合、「.c」ですがC++の文法が許容されたということになります。 本サイトでも検索したところ、「C言語での変数宣言の場所」というご質問で 「新しいgccコンパイラでは、変数の途中処理宣言は許容されている」 という内容のものは確認できましたが、これに似たような現象なのでしょうか。 ご存じの方おりましたらご教授よろしくお願いいたします。

  • クラスのメンバ関数を別ファイルで定義したときのバグ

    C++ においてヘッダファイルで宣言したクラスのメンバ関数を別のソースファイルで定義して、コンパイルするとうまくいきません。エラーは出ないのですが、同名の何もしない関数としてコンパイルされているようなのです。クラスのメンバ関数を宣言したのと同じヘッダに書くとちゃんとコンパイルされます。 どうしてそうなるのか、いまいち原因がわかりません。

  • C言語で、記憶クラス指定子extern・staticを関数に指定

    C言語の本に、「関数の定義と呼び出す側が別ソースファイルの場合、プロトタイプはヘッダーファイルに書き、定義側と呼び出し側の両方でインクルードしましょう」ということが書かれていました。 例えば、 ===code1a.c=== extern void funcB(int); static void funcA() { funcB(1); } ===code1b.c=== void funcB(int a) { printf("%d\n",a); } このような場合には、もしcode1b.cの中の関数funcBに引数を追加した場合、再コンパイルしても気づかないのでよくない。 そこで、次のようにヘッダーファイルを作り、プロトタイプはそこに書くべきだ。 ***code2b.h*** extern void funcB(int); ***code2a.c*** #include "code2b.h" static void funcA() { funcB(1); } ******code2b.c**** #include "code2b.h" void funcB(int a) { printf("%d\n",a); } 記述は以上のようなことです。 #include "code2b.h" とは、 extern void funcB(int); が書いてあるのと同じだと思います。 私が思ったのは、本の勧める方法では、 funcBを定義しているcode2b.cで、プロトタイプの記憶クラス指定子が、externになっているが良いのか(externとは、別のソースファイルで定義されているという意味ではないか)ということです。 extern, staticは、プロトタイプに書くべきなのか、関数の定義に書くべきなのか、も両方に書くべきなのでしょうか。 私の処理系では、 ・プロトタイプ宣言でexternを付けて関数定義でstaticを付ける、 ・staticを付けた関数を他のソースファイルで呼ぶ、 などの明らかに矛盾する場合は、コンパイルエラーになります。 でも、extern単独での役割はなさそうです。 他の処理系でも同じでしょうか。 (main等省略)

  • Cコンパイル時のマルチデファイン優先

    Cコンパイル時にincludeファイルの都合上、マルチデファインになってしまいます。 この点はコンパイラが宣言無視をしてくれるのですが、extern優先にしてリンクをするにはどのようにすれば良いのでしょうか。 ファイル内でinclude文(ファイル内)定義より先にextern文でシンボル定義をすれば、extern優先でリンクしてくれるのでしょうか(試して無いのですが)。 コンパラ・リンカ製品の仕様になるのでしょうけれど「マルチデフは絶対ダメ」なコンパイラでない場合、extern宣言を優先にする一般的な方法はありますか 。

  • C++のテンプレート関数やテンプレートクラスをC#で利用するには?

    C++で作成したテンプレート関数やテンプレートクラス をC#で利用したいと考えております。 テンプレート関数やクラスを明示的実体化してコンパイルし、 C++/CLIでラップすればC#でも使えるという話を聞きました。 テンプレートでない通常の関数やクラスの場合は、 http://www.atmarkit.co.jp/fdotnet/special/vcppinvista01/vcppinvista01_01.html で紹介されているのを見つけたのですが、 テンプレート関数やクラスの場合 具体的にC++/CLI側でどのように記述すればよいか分かりません。 例えば、 ---- [test.h] template <class Type1> class Test{ public:   Type1 x;   template <class Type2>   Type1 func(Type2 y){ ... } }; ----- のようなクラスがあった場合、どうすればよいでしょうか。 明示的な実体化の方法とC++/CLIで明示的実体化した関数やクラスが どのような名前になるのかが分かれば上記サイトの方法で いけるのではないかと思っているのですが…。 環境はVisual Studio2005(or 2008)です。 よろしくお願いいたします。

  • C++のクラスについて

    C++でクラスをmain関数をすっきり書くことを目的に暗中模索中です。 あるファイルでC++のクラスを作ったとします そのクラスのファイルのオブジェクトをmain関数で利用したいのですがオブジェクトの生成をmain関数ではなく他のファイルでしてそれをincludeする形で利用したいのですがどういうコードを書いたらいいか分かりません 考え方や書き方を教えてください

  • c++,ある関数のクラスから別のクラスの関数を呼ぶ

    c++で、あるクラスのメンバ関数から、別のクラスのメンバ関すを呼びたいのですが、どのようにしたらできますか? 例えば、以下のような単純なコードを考えています。やりたいことは、Aのメンバ関数であるaaa()からBのクラスであるbbb()を呼びたいと思っています。その理由を少し説明します。ここでは、Aというクラスとmain関数はオープンソースコードを例えています。できるだけ、元のオープンソースコードを書き換えずに新たな機能を拡張したいと思っています。そこで、Bというクラスを使って、元のオープンソースコードに機能を拡張しようとしています。このような理由なので、bbb()という関数はaaa()という関数から呼びたいです。メイン関数には何も書き加えないのがベストです。 現状では、実行すると「this is aaa」という出力しか出ません。ここに「this is bbb」の出力を加えたいです。クラスAとBにある程度コマンドを追加して、解決できないでしょうか。 #include <iostream> using namespace std; //------------------------------------------------------ class A{ public: void aaa(); virtual void bbb(){} }; //------------------------------------------------------ //------------------------------------------------------ class B : public A{ public: void bbb(); }; //------------------------------------------------------ //------------------------------------------------------ void A::aaa() { cout << "this is aaa \n"; bbb(); } //------------------------------------------------------ //------------------------------------------------------ void B::bbb() { cout << "this is bbb \n"; } //------------------------------------------------------ //------------------------------------------------------ int main() { A a; a.aaa(); return 0; } //------------------------------------------------------

  • C言語での変数宣言の場所

    今まで2年ほどJavaを使っていましたが、最近になってCを使う必要が出てきました。Cは大学の頃に授業で学んだ程度のレベルです。 それはさておき。 JavaやC++ではメソッド内のどの場所ででも、新たな変数を宣言して使用できますが、Cでは関数内の最初の方でしか宣言できないですよね? 先日、その事を意識せずに、Javaと同じように変数を関数の任意の場所で宣言しているようなCのソースを書き、gccでコンパイルしたところ、コンパイルが通ってしまいました。 その時のファイルは「.c」ファイルです。 このソースはC++のソースとして、コンパイラが認識してしまったのでしょうか?拡張子が「.cpp」ではなく「.c」のままでしたが、コンパイラは拡張子ではなく、ソースを読み込んでから、そのプログラムがCなのかC++なのか判断しているのでしょうか? いまいちピンと来ないので、どなたか解説お願いします。

専門家に質問してみよう