1. 導入
C++で開発をしていると、既存のC言語ライブラリを呼び出したり、逆にC++で書いた関数をC言語から利用したりする機会があります。しかし、そのままリンクしようとすると「undefined reference」というエラーに遭遇することがよくあります。これは、C++特有の「名前修飾(Name Mangling)」という仕組みが原因です。本記事では、この仕組みを理解し、安全に異言語連携を行うためのベストプラクティスを解説します。
2. 基礎知識
C++は「関数オーバーロード(多重定義)」をサポートしています。引数の型や数が異なる同じ名前の関数を複数定義できるため、コンパイラは内部的に関数名をユニークな文字列に変換します。これを「名前修飾」と呼びます。例えば、void func(int) は _Z4funci のような形式に変換されます。
一方、C言語にはオーバーロードの概念がないため、関数名はソースコード上の名前がそのままシンボルとしてオブジェクトファイルに記録されます。この「シンボルの命名規則の違い」により、リンカが関数を見つけられずリンクエラーが発生するのです。
3. 実装/解決策
この問題を解決するために用意されているのが `extern “C”` です。これを指定することで、C++コンパイラに対して「この関数はC言語の規約(名前修飾なし)に従うこと」を明示できます。
実務では、ヘッダーファイル内で `__cplusplus` マクロを使用して、C++環境下でのみ `extern “C”` が適用されるようにガードを書くのが一般的です。これにより、C言語とC++の両方から同一のヘッダーを安全に利用できます。
4. サンプルプログラム
以下のコードは、C++の関数をC言語から呼べるように定義する典型的な例です。
/ my_api.h /
ifndef MY_API_H
define MY_API_H
ifdef __cplusplus
extern “C” {
endif
/ C言語からも呼び出せる関数宣言 /
void perform_calculation(int value);
ifdef __cplusplus
}
endif
endif
/ my_api.cpp /
include “my_api.h”
include
void perform_calculation(int value) {
/ C++の強力な機能を使って実装可能 /
std::cout << "計算結果: " << value 2 << std::endl;
}
5. 応用・注意点
実務で注意すべきポイントがいくつかあります。
・クラスのメンバ関数は指定できない
`extern “C”` はグローバル関数や静的関数に対してのみ有効です。C++のクラスのメンバ関数(インスタンスメソッド)には適用できません。Cから呼び出す場合は、クラスを隠蔽したラッパー関数(インターフェース関数)を別途用意する必要があります。
・例外の伝播
`extern “C”` で定義された関数からC++の例外を投げて、C言語側のコードにキャッチさせようとすると、未定義動作となります。C言語との境界線では、必ず `try-catch` ブロックを配置し、例外を適切に処理・変換してください。
・名前修飾は悪ではない
名前修飾は、プログラムの型安全性を保証するための重要な仕組みであり、実行時のオーバーヘッドはありません。単に「C言語と連携する窓口だけを修飾から外す」という意識を持つことが、安定したバイナリ作成の鍵となります。

コメント