3

C-ABI インターフェイスを使用して C++ ライブラリを作成しています。

これは、マングリングに関して GCC が extern "C" 修飾子を処理する方法です。

namespace x {

    extern "C" int monkey(int x) {
        return 1;
    }

    int chimpanzee(int x) {
        return 1;
    }
}

関連するnm出力:

00000000004005cd T _ZN1x10chimpanzeeEi
00000000004005bf T monkey

質問: 再利用の柔軟性を最大限に高めるために、C-ABI に関連する関数を名前空間内に残したいと考えています。重要な注意:ライブラリがコンパイルされたら、リンカーにマップ ファイル (GCC) またはモジュール定義ファイル (MSVC) を渡します。

  1. マングリング出力は標準的な動作ですか?他の主要なコンパイラ (特定の MSVC) もマングリングを取り除きますか?
  2. 外部ABIに関与しているときに関数を名前空間に配置することに関する落とし穴やベストプラクティスはありますか?
  3. これは、リンク時にデマングルされた関数の C-ABI エクスポートに干渉しますか?
4

2 に答える 2

5

あなたがしていることは素晴らしいです、そしてあなたが望む効果をあなたに与えるでしょう。C ++プログラミング言語、第3版、208ページから:「Cリンケージを持つ名前は名前空間で宣言できます。名前空間はC ++プログラムでの名前へのアクセス方法に影響しますが、リンカによる名前の表示方法には影響しません。printf()fromstdは典型的な例です。…で呼び出されてもstd::printf()、それは同じ古いCprintf()です。」

于 2010-11-15T16:12:19.240 に答える
2

これはMSVC用です。

名前空間自体は名前マングルされませんが、名前のマングルが発生すると、名前空間の名前が関数 (またはオブジェクト) の名前に組み込まれます。このプロセスは文書化されていませんが、ここで説明されています。

飛び回って特定の質問に答える:

1) 名前マングリングに関して、標準で定義された動作はありません。標準が実際に述べていることは、実装がextern "C"構造体に C 互換のリンケージを提供するということです。

7.5.3 【リンケージ仕様】

すべての実装は、C プログラミング言語「C」で記述された関数へのリンケージ、および C++ 関数「C++」へのリンケージを提供する必要があります。[例:

complex sqrt(complex); // C + + linkage by default 
extern "C" { double sqrt(double); // C linkage } 

—終わりの例]

これが最終的に意味することは、C にはnamespaces の概念がないため、extern "C"関数またはオブジェクトが名前空間にある場合、エクスポートされた名前は名前空間の修飾を失うということです。これはにつながります...

3) はい、リンケージに問題がある可能性があります。これを試して:

main.h

#ifndef MAIN_API
#   define MAIN_API __declspec(dllexport)
#endif

namespace x
{
    extern "C" MAIN_API void foo();
};

namespace y
{
    extern "C" MAIN_API void foo();
};

main.cpp

#include <cstdlib>
#include <iostream>
using namespace std;
#define MAIN_API __declspec(dllexport)
#include "main.h"

void x::foo()
{
    cout << "x::foo()\n";
}

void y::foo()
{
    cout << "y::foo()\n";
}

int main()
{
}

extern "C"-ed バージョンのx::foo()y::foo()は名前空間 ID を失っている ため、これによりリンカー エラーが発生します。そのため、最終的にはまったく同じ名前になります。foo()

2) これに関するベスト プラクティス。名前空間内の関数の C-ABI をエクスポートする必要がある場合は、エクスポートする名前が同じにならないように注意する必要があります。namespaceこれは、そもそもa を使用する目的にある程度反します。しかし、次のようなことができます:

#ifndef MAIN_API
#   define MAIN_API __declspec(dllexport)
#endif

namespace x
{
    extern "C" MAIN_API void x_foo();
};

namespace y
{
    extern "C" MAIN_API void y_foo();
};
于 2010-11-15T16:21:05.137 に答える