物理の駅 Physics station by 現役研究者

テクノロジーは共有されてこそ栄える

Visual Studio: Cで書かれたライブラリをC++から呼ぶ方法

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"{} で囲もう。

参照

qiita.com