1

I am registering four callback functions:

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

Each callback function looks similar to this:

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

Input is a singleton:

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

After the callback functions are registered, a segfault occurs. I have narrowed down the problem to two things.

First: Excluding any of the process functions causes the segfault to disappear. For example,

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

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

Second: Segfault occurs when popping or pushing a std::vector declared here in singleton 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();

I am completely baffled. I am assuming the problem is related to static initialization order fiasco, but I have no idea how to fix it. Can anyone explain why this error is occurring?

Important notes:

  • If I reverse the linking order, it no longer segfaults.
  • I am using MinGW/GCC for compiling.
  • I am running single threaded.
  • The singletons do not have default constructors, everything is initialized by Singleton::instance().initialize();
  • The exact segfault call stack:
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

如果没有看到程序的其余部分,很难说它为什么会出现段错误。这听起来与时间有关。您可以尝试以下几件事:

  • 在引擎类、输入类(任何其他涉及的类)和回调设置代码的构造函数中放置断点。这将告诉您回调是否在他们使用构造的单例之前注册。请注意,断点可能会影响程序的计时,因此如果一个类首先命中,您可以禁用该断点并重新运行。多次尝试以检查结果是否一致。

  • 您是否有理由不能尝试更改指针而不是引用(如“惨败”提及)?

(你在我写这篇文章时的更新使得这部分不是那么有用,因为调用堆栈显示它不在构造函数中。)这听起来像回调在某些类的构造过程中注册。如果是这样的话:

  • 你可以移动注册调用,以便它们发生在 main() 下吗?这应该可以让你通过初始化。

  • 将类构造分为两个阶段:普通构造函数和 init() 函数。将关键代码放在 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 回答