11

Windows 環境、VSTS 2008 + C++ プロジェクトで確認したいのですが、extern C は関数レベルにしか適用できず、クラス レベルには適用できません (クラスのすべてのメンバー関数が C 言語名マングリングを使用するように)。いくつかの方法を試しましたが、常にコンパイル エラーが発生します。

前もって感謝します、ジョージ

4

5 に答える 5

13

extern "C"非常に複雑な(しかし完全に合法的な)ハックを介して、メンバー関数に適用することができます。

extern "C" typedef int bar_t(int x);

struct foo {
     bar_t bar; // yes, this declares a nonstatic member function!
};

int foo::bar(int x) { return x; } // definition

これは、ISO C++03 9.3[class.mfct]/9 に従って可能です。

メンバー関数は、関数型の typedef を使用して宣言できます (定義はできません)。結果のメンバー関数は、関数宣言子が明示的に提供された場合とまったく同じ型になります。8.3.5 を参照してください。

ただし、ISO C++03 7.5[dcl.link]/4 のため、これは実際には何も購入しません。

クラスメンバの名前とクラスメンバ関数のメンバ関数型については、AC 言語リンケージは無視されます。

于 2009-12-23T00:58:18.570 に答える
5

以前の回答に寄せたコメントを見ると (「[私の] 質問は、クラス内のすべての関数が自動的に C スタイルの名前マングリングを持つように、クラス レベルで適用できるかどうかだけですextern Cextern "C"そのように働きなさい。

構文的にextern "C"は、中括弧で区切られたブロックの単一のステートメントに適用できます。

extern "C" int my_foo(int i)
{
    ...
}

extern "C" {
    int my_bar(int i)
    {
        ...
    }

    int my_baz(int i)
    {
        ...
    }
}

C ヘッダー全体extern "C"に適切なガードを付けて使用するのが一般的です。#ifdef __cplusplus

意味的には、適用の実際の効果は、extern "C"「通常の」(つまり、非クラスの) 関数と関数へのポインタにのみ適用されます。もちろん、C++ テンプレートに適用することはできません。また、それをクラス メソッドに適用することもできません (クラス メソッドは、それが呼び出されたオブジェクトを知る必要があり、C スタイルのリンケージにはその情報を関数に渡す方法がないため)。

名前空間に存在する関数に適用することextern "C"可能ですが、C 経由で使用すると名前空間の情報が消えてしまいます。


アップデート

すでにクラスがあり (簡単にするためにPOD クラスを使用します)、それを C から使用できるようにしたい場合は、C でextern "C"呼び出し可能な関数に適用する必要があります。残念ながら、これは単純なケースでも見苦しくなります:

// in the header file
#ifdef __cplusplus
namespace X {
#endif
    struct A
    {
        int x;
#ifdef __cplusplus
        A() : x(5) { }
        int foo()
        {
             return x += 5;
        }
#endif
    };
#ifdef __cplusplus
    extern "C" {
#endif
        int A_foo(struct A a);
        struct A A_create();
#ifdef __cplusplus
    }
}
#endif


// in the .cc file
#include "try.h"

namespace X {
    extern "C" {
        int A_foo(A* a)
        {
            return a.foo();
        }

        A A_create()
        {
            return A();
        }
    }
}

// in the .c file
#include <stdio.h>
#include "try.h"

int main()
{
    struct A a = A_create();
    printf("%d", A_foo(a));
}

gcc を使用すると、次のようにコンパイルできます。

  • C++ ファイル:g++ try.cc -c -o try.o
  • Cファイル:gcc try.c try.o

重要な点がいくつかあります。

  • C++ ファイルがバックグラウンドで STL を呼び出す場合、またはnewor delete(またはnew[]or delete[]) を呼び出す場合は、最終的なプログラムを C++ ランタイム ライブラリにリンクする必要があります (gcc でのコマンド ライン スイッチは-lstdc++.
  • おそらく、C と C++ の両方のコードをコンパイルするときに、同一の最適化フラグを渡したいと思うでしょう (最適化はオブジェクトのサイズに影響を与える可能性があり、サイズが一致しないと多くの問題が発生する可能性があります)。マルチスレッドについても同様です。
  • C++ コードでは例外を自由に使用できますが、例外がC コードと交差すると、すべての賭けが無効になります。
  • より複雑なものが必要な場合は、おそらく PIMPL パターンを参照してください。
  • 構造体が C コードのスコープ外にある場合、C++ デストラクタは呼び出されません(一部のコンパイラはそうすることを約束している場合がありますが、標準ではありません)。これらのオブジェクトをクリーンアップする必要がある場合は、デストラクタを呼び出す extern "C" 関数を呼び出す必要があります。

デストラクタを明示的に呼び出すには:

extern "C" void A_destroy(struct A a)
{
    a.~A();
}
于 2009-06-22T04:02:53.367 に答える
2

うーん... extern "C"C スタイルのリンケージを強制します。クラスAFAIKでは使用できません。

于 2009-06-22T03:19:02.780 に答える