注册回调函数后的Segfault

时间:2012-04-10 02:59:28

标签: c++ segmentation-fault

我正在注册四个回调函数:

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消失。例如,

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

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

第二个:在弹出或推送单身Engine中声明的std :: vector时发生Segfault:

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();

我完全不知所措。我假设问题与static initialization order fiasco有关,但我不知道如何解决它。任何人都可以解释为什么会发生这种错误吗?

重要说明:

  • 如果我反转链接顺序,则不再是段错误。
  • 我正在使用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()

1 个答案:

答案 0 :(得分:0)

如果没有看到你的其他程序,就很难说为什么会出现这种情况。这听起来与时间有关。以下是您可以尝试的一些事项:

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

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

(我写这篇文章时的更新使得这部分不太有用,因为callstack显示它不在构造函数中。)这听起来像回调在一些类的构造期间注册。如果是这样的话:

  • 您可以移动注册调用,以便它们在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
}