QNo.152285と類似の質問になりますが、独自に用意したクラスを内部で使用し、extern "C" を宣言した、C++の関数をCプログラムから呼び出し、Cコンパイラでリンクすることを考えています。
この場合、C++関数内で使用されるクラスについても、extern "C"の記述が必要でしょうか?
またその記述の仕方はどのようになるでしょうか?
あるいは、そういったことはできないのでしょうか?(coutが使えることを考えると、できないわけではないとも思っていますが)
あるいは、コンパイルの際の、オプションなどで解決できるでしょうか?
クラスはnewで生成して使用しています。
環境はソラリスのCCとccを使っています。
どうか、ご回答よろしくお願いします。
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"の記述が必要でしょうか?
という疑問が的を外れたものであることが分かるはずです。
# 分からなければ、聞いてください
この回答への補足
ご回答ありがとうございます。
「 分からなければ、聞いてください」というお言葉に甘えて、もう一度たずねさせてください。
以下のような関数を定義しているファイルと
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コマンドについての説明など大変ためになりました。
ご回答いただければ幸いです。
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 を使うときにどうなるか、とか。
# 私も苦労したので、懐かしかったりします :-)
No.3
- 回答日時:
ごめんなさい、手が滑った。
分かると思うけど、> % CC -o libTEST.o -G t.o
は、
% CC -o libTEST.so -G t.o
の間違いです。
この回答への補足
回答ありがとうございます。
単に「こうやればよい」というだけでない、指針のある返答を受けることができて感謝しています。
現在、ご意見を参考に、試行錯誤していますが(とはいえ、お察しのとおりプロジェクトの一部分でもあるので、この件のみに関わってばかりもいられないのですが)、今回一応の、経過報告とさせていただきたいと思います。
以下の手順でコンパイルを行ったとき
% 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コマンドを使うことがナンセンスなのでしょうか?)。
自分にはこのあたりの理解がいかにも欠けていると感じさせられてしまいます。
もう少し、調べていこうと考えています。
No.4ベストアンサー
- 回答日時:
> ここで静的にC++の共通ライブラリをリンクする必要があるということだろうと考え、
考え方はあっていると思います。
ただ、-dy とか -dn は、コンパイルオプションに指定された共有オブジェクトや
-l オプションに効いてくる(はず)ので、暗黙にリンクされているものには有効では
ない(はず)だということ。
もし、聴いたとしても、静的なアーカイブファイルが無ければ -dn オプション自体
意味が無いこと、といったあたりが考えられます。
> そもそも、dllに対してlddコマンドを使う ことがナンセンスなのでしょうか?
そんなことはありません。何も出力されないのは、対象としている dll に関する外部
参照が解決されているだけのことです。
リンクエラーになっているのは、どんな名前のシンボルなのでしょう?
シンボル名が分かれば、c++ のライブラリに対して片っ端から nm コマンドで、その
シンボル名が含まれているものを探し、リンクのオプションに追加する、という荒業(*)
もあります。
(*) 仕組みの理解、というよりは、とりあえず動けば良い、という意味で :-)
# 近くに、Solaris の開発環境が無かったかなあ
# ちょっと自分でも試してみたくなった
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Mac OS 【USB-Cの充電】2本差しすると高速充電になる?故障の原因になる? 2 2022/08/08 09:38
- その他(プログラミング・Web制作) FORTRAN77の配列(除算) 2 2023/02/01 14:34
- C言語・C++・C# C++言語の16進数の表現についておしえてください 1 2022/11/14 17:46
- Excel(エクセル) エクセル if関数 4 2023/04/27 11:35
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
- C言語・C++・C# C言語 3 2022/10/04 15:07
- Visual Basic(VBA) VBAでfunctionを利用しようとしたときに「引数は省略できません」というエラーが出ます 1 2022/10/15 16:30
- その他(プログラミング・Web制作) pythonのmap、結果の利用は1度だけ? 5 2022/06/11 12:33
- その他(プログラミング・Web制作) Pythonで複数のメソッドをまとめて管理する方法について 1 2023/03/30 00:01
- その他(OS) Windowsで大量の画像サイズを半自動で変更する方法 6 2023/02/17 08:45
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語ソースのオラクルコンパイ...
-
C++の関数をfortranから呼びた...
-
LNK2019: 外部シンボル どのよ...
-
popen・pcloseについて
-
python エラー
-
エクセルのエラーメッセージ「4...
-
アプリケーションのDLLファイル...
-
<unistd.h>をVisualStudioでつ...
-
VBAを何回も作り直して、容量が...
-
C2146を回避するにはどうしたら...
-
unsigned long long 型のフォー...
-
C言語の規格
-
C言語をコンパイルするとコンピ...
-
win32api構造体引数の型指定に...
-
64ビットのlinuxで32ビットメモ...
-
1 つ以上の複数回定義されてい...
-
visual C++ でaruduinoとシリア...
-
cc と gcc の違い
-
” OS ビルド ” の意味が分か...
-
gcc のコンパイラオプションに...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
外部依存関係について
-
LNK2019: 外部シンボル どのよ...
-
C++でのエラー LNK2001
-
外部シンボルの未解決のエラー
-
error LNK2001について
-
外部シンボル" ~~"は未解決で...
-
コンパイルエラー
-
OpenCVでの画像サイズ取得につ...
-
LNK2019: 未解決の外部シンボル...
-
エラー 未解決の外部シンボル
-
LNK2001(Pro*c)
-
実行ファイルの容量を減らした...
-
popen・pcloseについて
-
PathIsDirectoryを使って
-
DB2付きのC言語がコンパイルで...
-
C++ の LNK2001エラー
-
CLapack C++
-
外部シンボルについて
-
未解決の外部シンボル
-
VC++ 6.0へのサンプルプログラ...
おすすめ情報