0

我有一个如下的类和一个如下的主要方法:

class Mesh
{
public:
    void draw()

    void draw(){}
}

int main(int argc,char* argv[])
{
    Mesh mesh;
    glutDisplayFunc(mesh.draw);
}

所以我想将对象的成员函数作为参数传递,但编译器显示以下错误消息:

error: argument of type ‘void (Mesh::)()’ does not match ‘void (*)()’

我究竟做错了什么?

4

2 回答 2

2

由于draw是一个非静态成员函数,它需要一个类的实例来操作。

在纯 C++ 中,您将使用函数对象(又名“函子”,参见std::function或其前身boost::function)。遗憾的是,这不是 C 或 C API 中的选项。

在这种情况下,由于没有数据传递给回调,因此您必须创建某种静态函数(类静态或文件静态),以便在调用时执行正确的操作。

如果你只有一个Mesh,那么很容易:要么将类中的所有内容设为静态(在这种情况下,它基本上是一个命名空间),要么有一个非成员函数调用draw你的一个实例Mesh

// at some global-ish file scope...

// we define our class
class Mesh { ... };

// and here's the one global instance of it
Mesh myOneMesh;

// and here is our global / static function that "knows"
// which instance to draw
void drawMyOneMesh() { myOneMesh.draw(); }

// then, in the main flow of your program:
glutDisplayFunc( &drawMyOneMesh );

如果您有多个网格,看起来您能做的最好的事情就是将其从当前窗口中关闭。在不了解您的应用程序或 GLUT API 的情况下,我可能会做这样的事情来启用每个窗口的网格回调:

#include <map>

// this could also be a class with all-static members, if one
// were so inclined.  might be worth it, if only to protect 'map'
// from external meddling.

namespace WindowToMesh
{

    typedef std::map< int, Mesh * > Map;
    Map map;

    void addMapping( int win, Mesh * mesh )
    {
        map.insert( { win, mesh } );
    }

    void removeMapping( int win )
    {
        map.erase( win );
    }

    void draw()
    {
        int win = glutGetWindow();
        Map::iterator it = map.find( win );
        if ( it != map.end() )
            it.second->draw();
    }

} // end namespace WindowToMesh

现在,在您的主程序中,您可以将新Mesh窗口与当前窗口相关联:

Mesh * m = new Mesh( ... );
WindowToMesh::addMapping( glutGetWindow(), m );

您可以将(有效静态)WindowToMesh::draw函数关联为回调:

glutDisplayFunc( &WindowToMesh::draw );

当您准备好销毁 时Mesh,请确保您在同一个窗口中,然后:

WindowToMesh::removeMapping( glutGetWindow() );

根据其他因素,进行双向映射可能是有意义的(因此您可以通过Mesh *而不只是通过 window找到东西int,或者对罕见的取消注册进行暴力扫描等。

我没有可以测试的环境,但它应该很接近。祝你好运!

于 2013-05-12T09:36:51.307 回答
1

问题是成员函数指针需要调用一些信息,即要调用的实例,然后this在成员函数的实现中指针可以访问这些信息,因此您可以访问成员变量。我认为您想这样做,因为您的设计看起来像是要绘制在Mesh.

有可能将成员函数指针转换为“普通”函数指针。但是您必须记住,一旦调用此函数,这条信息( 的实例)就必须可用Mesh这意味着从成员函数指针到“普通”函数指针的典型转换会添加一个参数(实例)。

但是glutDisplayFunc()接受一个void(*)(void)函数指针,即一个参数列表,所以它不会告诉被调用函数任何东西。您必须确保一旦调用了传递的函数,您就可以重建“上下文”,即Mesh实例。

这可以通过Mesh从代码中的全局点指向一个实例来完成。当您调用我们即将编写的辅助函数时,将始终使用它。因此,当您将指针传递给glutDisplayFunc()我们的辅助函数Mesh时,一旦我们告诉它应该使用哪个,就知道该使用哪个。

class Mesh
{
public:
    void draw() { ... }
}

Mesh *instance;

// Helper function called by glut:
void display() {
    // Now we can access a particular instance:
    instance->draw();
}

int main(int argc,char* argv[])
{
    Mesh mesh;

    // First point to this instance globally:
    instance = &mesh;

    // Then, pass our helper function which now has an empty parameter list:
    glutDisplayFunc(&display);
}

此解决方案有一个限制。您不能对不同的实例重用相同的函数指针。因此,您不能将任何实例转换为函数指针,而只能为每个全局定义的实例指针转换一个。但是由于您只想将绘制函数传递给glutDisplayFunc,因此您只需执行一次,所以应该没问题。我只是不想对你隐瞒这个“危险”。;)

于 2013-05-12T09:32:27.977 回答