2012-01-09 5 views
1

私は最近custm CALLBACKの関数ポインタに問題があり、問題を一時的に解決する呼び出し規約を使用するようになった。funy CALLBACKはうまくいったが、呼び出し関数のシグネチャはまだ間違っていた!!そのBUGを見つけるのに多くの時間を費やしました。 がcaling規則はあなたが時々OKではない何かに何かをやらせることに気づいた...今までだ標準の呼び出し規約が存在しますか?

OKは...今、私は、呼び出し規約についてlitleもっと知りたい: Visual Studioは、それは__cdecl自分のしています、 __thiscallなど(IIRC)。

標準のC++ではいくつかの呼び出し規約が規定されていますが、どうすれば使えますか?


編集:私はバグ見つけることができませんでしたいくつかのコードれている:

class Object; 
    class EventArgs; 

    typedef void(__cdecl Object::*MethodHandler)(Object* sender, EventArgs args); 

///..... this is how I call it..(snapshot) 
(iter->second.sender->*iter->second.memberFunct)(sender, args); 
///... 
    void __cdecl Triger(EventArgs args) //missing "Object* sender" here!!! but it works! 
    { 
     if(args == "test") 
     cout << "test args received" << endl; 
    } 

を(。ところで、タイプ名は私のカスタムクラスである)うまく働いた !関数が呼び出されましたが、__cdeclがないと、私はESPレジスタエラーを受け取りました。

+0

バグは何ですか?通常、コンパイラエラーなしでハンドラ/コールバックを登録するための関数ポインタのハードコーディングされたキャストは、何か間違ったことをしている間違いです。上記のコードでは、関数の引数が異なるため、 "Trigger"はMethodHandler型ではありません。 – selbie

+0

ありがとうございます、それは同じ署名ではありませんが、私は__cdeclを使用しない場合、__cdelcを使用してうまく動作し、ESPの登録エラーとprogrm chrush ..非常に興味深い.. – codekiddy

+0

あなたは幸運を得ているので引数がスタックにプッシュされる順序に変更します。 cdeclのトリックはおそらくちょうどそれがクラッシュしないようにスタックをマッサージしています。あなたのコードのどこかで、コンパイルエラーなしでその関数をコールバックサービスに登録するために、 "(MethodHandler *)Trigger"(またはvoid *キャスト)を行っています。コードを修正して、コードをキャストなしでコンパイルすると、クラッシュが消えてしまいます。これはどこでも同じ呼び出し規約と同じ引数リストを意味します。 – selbie

答えて

1

__cdeclなしでクラッシュする理由は、Windowsコンパイラのデフォルトでは__stdcallを使用するためです。後者は、呼び出し先関数にスタックをクリーンアップさせます。したがって、呼び出し元は2つの引数をスタックにプッシュして「トリガー」を呼び出しますが、トリガーは関数出口でスタックから1つの引数だけをポップします。したがって、あなたはクラッシュします。 __cdecl呼び出し規約ではこれを回避します。

私が上記のコメントで述べたように、__cdeclを使用してクラッシュを修正すると、本当のバグが隠れてしまいます。トリガの引数リストが "MethodHandler"の引数リストと一致しません。それはおそらくあなたのクラッシュの原因です。

これはおそらく、より良い修正です:

typedef void(Object::*MethodHandler)(Object* sender, EventArgs args); 

void Triger(Object* sender, EventArgs args) 
{ 
    if(args == "test") 
    cout << "test args received" << endl; 
} 

あなたは「トリガー」は、その後のコールバックのために登録されます方法を示すコードを共有していませんでした。あなたがそれをしなかった場合、あなたのコードがコンパイルされませんでしたので

MethodHandler handler = (MethodHandler)Trigger; 

:しかし、私は強くあなたがこのような何かのキャストを入れている疑いがあります。キャストがコンパイルに必要ないようにコードを修正し、あなたの本当のバグもあなたのクラッシュと一緒に消えます。

+0

あなたはちょうど私の問題だったが、キャスティングが必要ですが、私はstackoverflowの質問をしてきたが、永遠に鋳造せずに同じことを行うことは不可能だと言ったことを知っているねえ...これを見てくださいhttp://stackoverflow.com/questions/8756993/how-to-force-implicit-conversion-in-polymorphism – codekiddy

+0

派生クラスから基本クラスにメンバポインタをキャストする必要がある場合は、あなたがタイプチェックのいくつかを保つことを可能にするstatic_cast。 – servn

3

いいえ;呼び出し規約はプラットフォーム固有です。先頭の二重のアンダースコアは、これが実装予約された概念であるという手掛かりです。言語そのものは、この詳細レベルでの実装方法については何も要求していません。

+0

実際、C++ *は呼び出し規約を知っています:extern "C"とextern "C++"(後者はデフォルトであるため、通常は明示的に記述されません)。しかし、通常、これらの呼び出し規約は名前のマングリングのみが異なります。 – celtschk

+0

お返事ありがとう!私はextern "C"を使用して、それがいくつかの利点を持っているかどうかを調べるつもりです:) – codekiddy

+0

@celtschk:これらは「リンケージ仕様」と呼ばれていますが、 (実際にはプラットフォームが任意の仕様をサポートできるようにする)、OPが興味を持つと思われる「呼び出し規約」(スタックフレームのクリーンアップ、引数の順番、レジスタの対スタックなど)は実装の詳細のままです。 –

関連する問題