2012-03-13 47 views
2

cl /c /clr /W4コンパイラして、以下のコードをコンパイルするこの問題を解決する方法はあります.. 警告クラスC4793をテンプレートクラスで修正するにはどうすればよいですか?

warning C4793: 'Interface::'vcall'{0}'' : function compiled as native

だからプラグマはどんな効果があるとは思えないと言いますか?またはこれはバグです(プラグマは非テンプレートクラスで動作します)?この警告を安全に無効にすることはできますか?

#pragma unmanaged 

struct Interface 
{ 
    virtual void Foo() = 0; 
}; 

template< class T > 
struct UsesFunPtr 
{ 
    UsesFunPtr() 
    { 
    &T::Foo; 
    } 
}; 

void DoIt() 
{ 
    UsesFunPtr<Interface> a; 
} 

#pragma managed 

更新:私は警告が表示されなくなり、最後の行削除する場合 - そうComicSansMs'と答え、次の:at the time of definition for the template正確であるとき?誰も、最後の行がその後に続くコードがないのに、なぜなら前のコードに影響を与える理由を説明できますか?

答えて

7

投稿者が理由を説明したので、大きな改訂版& T:Foo;問題ではなかった

この警告の起源は、ややばかげて複雑です。

調査の後、我々はComicSansMSも掲載マイクロソフトから次を見つける:テンプレート関数がインスタンス化されると、それが管理または非管理されている場合

、テンプレートの定義時のプラグマ状態が決定されます。

ファンクションテンプレート用です。使用しているクラステンプレートではありません。

実際、この関数テンプレートのインスタントメッセージは、慎重に関連するです。しかし、この警告が動作する独特の方法は、コンパイルプロセスと関係しています。

コンパイラは、実際にあなたが管理の#pragmaでファイルを終了したときにUsesFunPtrのctorのを呼び出して使用するためのマネージコードを生成しています。それは、そこにいくつかの管理されていないコードがあることを警告します。ここでは、その理由を詳細に分析します。

サンクは、基本的に特定のvtable呼び出しを中心としたラッパー関数です。ウィキペディアには、この件に関してまともな記事があります。サンクを生成する理由は、関数のアドレス(& T :: Foo)を取っているためです。

ファイルの最後の行である場合は、この:

#pragma unmanaged 

が、それはあなたがそのオブジェクトにマネージコードとアンマネージコードを混在している場合でも、文句を停止します。これは、フロントエンドとバックエンドの間の接続が切断されているためです。上のコードを最後の#pragmaコマンドでコンパイルします。

この警告は、コンパイル時の警告ではありません。その後、

void DoIt() 
{ 
    long a; 
    long long b; 
    b = 4; 
    a = b; 
    UsesFunPtr<Interface> d; 
} 

あなたは「コンパイルx.cpp」の際に切り捨てについての警告が表示されます:それはあなたがそうのようなあなたのドイト()関数を修正した場合は、「...コードの生成」それが言うとき、コンパイル後に来ますコード生成フェーズ(Generating code ...)に移動します。このフェーズでは、この質問に関する警告が表示されます。

コンパイラは、種類のJavaバイトコードと同様に、等、解析し、中間バイナリ形式ソートの作成フロントエンドです。コードジェネレータは、このバイトコード(この場合はWindows x86)からターゲットプラットフォーム上に実際の出力を作成するバックエンドです。

バックエンドがコードを保持するまで、最適化は行われません。サンクは最適化の一種であるため、警告を出すのはコンパイラではなく、コンパイルされたILコードを取るコードジェネレータです。それは私が知っているどのような方法でもオフにすることができる最適化ではありません。これは標準的な方法です。

しかし、そのテンプレートをインスタンス化するのはコンパイラです。バックエンドはちょうど完全なクラスを見て、それを理解するように言われています。

管理対象C++/CLIのコンパイル時に違いがあります。時にコンパイラは、リンク時コード生成と呼ばれるものを使用することができます。これが起こると、リンカはバックエンドを呼び出すリンカです。 (様々な理由で)不可能な場合、コンパイルの一般的なプロセス(フロントエンド→バックエンド→リンカー)を経ます。

#pragmaは、受け取った順にILに渡されます。これは、#pragma managedが行われた後、UsesFunPtrの関数サポートコードが実際には "最後に追加されました"ことを示しているようです。だからあなたのコードは、管理対象外の空間にあるにもかかわらず、コードジェネレータは、見ている:

  1. アンマネージドオブジェクトの定義を、あなたが書いていないクラスについて
  2. マネージド
  3. 追加サポートコードを使用すると、それは他のオブジェクトファイルとのインタフェースとは関係ありません:vtableの作成やコピーなどそれを取得ILは、実際には2つを接続していないため

ジェネレータは、あなたが完全管理またはアンマネージコードであることを、そのUsesFunPtrのctorの、あるいはクラスを意味している場合区別する方法はありません。それは、(プラグマによって)管理されていない、それを作成し、その上で動作し、管理された空間で(プラグマによって)サンクを生成する関数をサポートする関数を参照します。それは接続を伝えることはできません。その#pragmaをそこに置くので、最後に#pragmaを見ただけです。生成されたサポートコードが管理されます。

#pragma unmanagedを最後に置くと、テンプレートを混在させた管理コードがあっても、その警告は表示されません。これは、コードがアンマネージドとしてコンパイルされているため、サンクは問題にならないからです。

これを合計する方法は?最後に#pragma managedは、フロントエンドとバックエンドの間で誤った通信(または間違った前提ですか?)を引き起こし、バックエンドはそれに不満を持ちます。

ウィー、それは楽しいものでした!

+0

私はprgamaのものがバグかもしれないことに同意しますが、&T :: Fooに何も間違っているとは確信していません。それは何もしませんが、構文は有効です。ここでの目的は、問題を解明することです。実際のコードはstd :: bind(&T :: Foo、instanceOfT)です。これは完全に有効で呼び出し可能な関数オブジェクトになります。そこにコンパイラのバグはありません。 – stijn

+0

@stijnハ、私はあなたの実際のコードで、std :: bindを使って提案したことをやっただけです。それはこれを知るのを助けていたでしょう。その場合、これを修正するために、Microsoftにバグレポートを提出します。彼らはそれをいかに速く修正するかはかなりクールです。たとえば、関連する修正プログラム:http://connect.microsoft.com/VisualStudio/feedback/details/122489/warning-c4793-and-pragma-unmanaged私は私の答えを改めるつもりです。 –

+0

+1この予期しない複雑な問題に対する優れた答え。 – ComicSansMS

2

MSDNからの引用:

テンプレート関数をインスタンス化されると、それが管理または非管理されている場合、テンプレートの定義時のプラグマ状態が決定されます。

したがって、DoItの定義のために#pragma unmanagedをアクティブにする必要があります。それがすでにそうであれば、それはバグかもしれません。

/clrスイッチにもかかわらず、関数がネイティブコードとしてコンパイルされていることを理論的に示しているので、この警告を無効にすることは理論上安全です。その情報を失うことが許容される場合は、それを無効にしてください。

+1

投稿されたコードスニペットは、私がコンパイラに与えているとおりであるため、プラグマはできるだけアクティブでなければなりません。] – stijn

+1

テンプレート関数はテンプレートクラスとは異なります。 –

0

一部の#pragma。 #pragma warning(...)いくつかの警告については、グローバルな効果があります。つまり、コンパイラは、インクルードツリー全体とソースファイルが解析された後にのみ、コンパイラで考慮します。したがって、管理された/管理されていないプラグマが実際のスイッチを警告またはプラグマとは別の場所に置いて、それ自体がグローバルであるように、いくつかの邪魔にならない方法で実装されている可能性があります。

最後のプラグマが「管理」されているので、すべてのコードが管理されていることをお勧めします。この場合、参照されるpure_call関数のように見えるMSVCは、 ()= 0)を返します。

私のプログラムが正しく動作する限り、通常はMSVCを無視します。