C言語で書かれたライブラリ (.libファイル) を C++からは直接呼ぶことができない。検証してみる。
libtestというプロジェクトで、構成の種類をスタティックライブラリ (.lib)
にしたプロジェクトを作成した
C言語で書かれたライブラリと、サンプルプログラムを以下のように作成。
libtest.c
#include "libtest.h" double libtest() { return 0.0; }
libtest.h
#pragma once double libtest();
simple.cpp
#include <iostream> #include <libtest.h> int main() { std::cout << libtest() << std::endl; return 0; }
これで、libtestとsimpleをビルドすると、simpleの方のビルドは失敗した。出力は以下の通り。
ビルドを開始しました... 1>------ ビルド開始: プロジェクト: libtest, 構成: Release Win32 ------ 1>libtest.c 1>libtest.vcxproj -> C:\Users\Masahiro\source\repos\libtest\Release\libtest.lib 2>------ ビルド開始: プロジェクト: simple, 構成: Release Win32 ------ 2>simple.obj : error LNK2001: 外部シンボル "double __cdecl libtest(void)" (?libtest@@YANXZ) は未解決です 2>C:\Users\Masahiro\source\repos\libtest\Release\simple.exe : fatal error LNK1120: 1 件の未解決の外部参照 2>プロジェクト "simple.vcxproj" のビルドが終了しました -- 失敗。 ========== ビルド: 1 正常終了、1 失敗、0 更新不要、0 スキップ ==========
Visual C++ユーザーにはおなじみの 外部シンボル は未解決です
である。libtest.cには確かに関数の定義が書かれているのに、呼ばれていない。
libtest.c のファイル名、libtest.cpp (拡張子 c→cpp)に変更して、libtestとsimpleプロジェクトをビルドする。
ビルドは成功した。出力は以下の通り。
ビルドを開始しました... 1>------ ビルド開始: プロジェクト: libtest, 構成: Release Win32 ------ 1>libtest.cpp 1>libtest.vcxproj -> C:\Users\Masahiro\source\repos\libtest\Release\libtest.lib 2>------ ビルド開始: プロジェクト: simple, 構成: Release Win32 ------ 2>コード生成しています。 2>0 of 3 functions ( 0.0%) were compiled, the rest were copied from previous compilation. 2> 0 functions were new in current compilation 2> 0 functions had inline decision re-evaluated but remain unchanged 2>コード生成が終了しました。 2>simple.vcxproj -> C:\Users\Masahiro\source\repos\libtest\Release\simple.exe ========== ビルド: 2 正常終了、0 失敗、0 更新不要、0 スキップ ==========
次に、libtest.cpp を libtest.cに戻して、simple.cpp のヘッダーの宣言を以下のように変えてみる。
extern "C" { #include <libtest.h> }
ビルドしたときの出力。
ビルドを開始しました... 1>------ ビルド開始: プロジェクト: libtest, 構成: Release Win32 ------ 1>libtest.c 1>libtest.vcxproj -> C:\Users\Masahiro\source\repos\libtest\Release\libtest.lib 2>------ ビルド開始: プロジェクト: simple, 構成: Release Win32 ------ 2>コード生成しています。 2>Previous IPDB and IOBJ mismatch, fall back to full compilation. 2>All 3 functions were compiled because no usable IPDB/IOBJ from previous compilation was found. 2>コード生成が終了しました。 2>simple.vcxproj -> C:\Users\Masahiro\source\repos\libtest\Release\simple.exe ========== ビルド: 2 正常終了、0 失敗、0 更新不要、0 スキップ ==========
成功した。
下記の参照リンクによると
結局のところ、C++からCのモジュールを呼び出す際にはextern "C"を用いるということです。なぜなら、C++コンパイラでは、マングリングによって、シンボル名が関数名ではなくなり、Cコンパイラとの互換性がなくなってしまうからです。
結論: C++から、C言語で書かれたライブラリを呼ぶとき、ヘッダー宣言時に、extern "C"{}
で囲もう。
参照