1

4 つのコールバック関数を登録しています。

glfwSetMouseButtonCallback(procMouseButton);
glfwSetMousePosCallback(procMousePosition);
glfwSetCharCallback(procCharInput);
glfwSetKeyCallback(procKeyInput);

各コールバック関数は次のようになります。

void GLFWCALL procMouseButton(int button, int action) { 
    Input::instance().processMouseButton(button, action); // doesn't do anything yet
}

Inputシングルトンです:

Input& Input::instance()
{
    static Input instance;
    return instance;
}

コールバック関数が登録されると、segfault が発生します。問題を2つに絞り込みました。

最初:プロセス関数のいずれかを除外すると、segfault が消えます。例えば、

// this works
glfwSetMouseButtonCallback(procMouseButton);
//glfwSetMousePosCallback(procMousePosition);
glfwSetCharCallback(procCharInput);
glfwSetKeyCallback(procKeyInput);

// this works also
glfwSetMouseButtonCallback(procMouseButton);
glfwSetMousePosCallback(procMouseButton); // exclude procMousePosition
glfwSetCharCallback(procCharInput);
glfwSetKeyCallback(procKeyInput);

2 番目:ここでシングルトンで宣言されている std::vector をポップまたはプッシュすると、セグメンテーション違反が発生しますEngine

class Engine
{
    public:
        static Engine& instance();

        std::list<GameState*> states;
    private:
        Engine() {}
        Engine(Engine const& copy);
        Engine& operator=(Engine const& copy);
};

// either causes segfault after registering functions
Engine::instance().states.push_back(NULL);
Engine::instance().states.pop_front();

私は完全に困惑しています。問題は静的初期化順序の大失敗に関連していると思いますが、修正方法がわかりません。このエラーが発生する理由を説明できる人はいますか?

重要事項:

  • リンクの順序を逆にすると、セグメンテーション違反はなくなります。
  • コンパイルには MinGW/GCC を使用しています。
  • シングルスレッドで実行しています。
  • シングルトンにはデフォルトのコンストラクターがなく、すべてがによって初期化されますSingleton::instance().initialize();
  • 正確な segfault コール スタック:
0047B487    std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*) ()
00000000    0x00401deb in std::list >::_M_insert()
00000000    0x00401dbb in std::list >::push_back()
00401D92    Engine::pushState(GameState*) ()
00404710    StartupState::initialize() ()
00402A11    Engine::initialize() ()
00000000    0x00403f29 in main()
4

1 に答える 1

0

プログラムの残りの部分を確認しないと、セグメンテーション違反の原因を特定するのは困難です。それはタイミングに関連しているように聞こえます。以下のことを試してみてください。

  • Engine クラス、Input クラス (その他の関連クラス)、およびコールバック設定コードのコンストラクターにブレークポイントを配置します。これにより、コールバックが構築を使用するシングルトンの前に登録されているかどうかがわかります。ブレークポイントがプログラムのタイミングをずらす可能性があることに注意してください。そのため、1 つのクラスが最初にヒットした場合は、そのブレークポイントを無効にして再実行できます。これを複数回試して、結果が一貫していることを確認してください。

  • 参照の代わりにポインターへの変更を試みることができない理由はありますか (「大失敗」の言及など)?

(私がこれを書いている間のあなたの更新は、コールスタックがコンストラクターにないことを示しているため、この部分をあまり役に立たなくします。)これは、クラスの構築中にコールバックが登録されているように聞こえます。そうだとすれば:

  • main() の下で発生するように登録呼び出しを移動できますか? これにより、初期化を過ぎてしまうはずです。

  • クラスの構築を、通常のコンストラクターと init() 関数の 2 つのフェーズに分割します。重要なコードを init() 内に置き、全員が構築を終えた後にそれを呼び出します。

コールバックが後で発生するのを防ぐこともできます。ゲームの起動時にコールバック登録を後で移動できない場合は、フラグを設定して、「安全な」時間まで何もしないようにすることができます。このフラグが有効になるタイミングを調整すると、「どのくらい遅いか」が「十分に遅いか」を確認できます。追加の if() オーバーヘッドは、クラッシュよりも優れています。:)

volatile bool s_bCallbackSafe = false;    // set this at some point in your game/app

void GLFWCALL procMouseButton(int button, int action) {
    if (s_bCallbackSafe)
        Input::instance().processMouseButton(button, action); // doesn't do anything yet
}
于 2012-04-10T03:43:29.223 に答える