我有一个类如下,主要方法如下:
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 (*)()’
我做错了什么?
答案 0 :(得分:2)
由于draw
是非静态成员函数,因此需要对该类的实例进行操作。
在纯C ++中,您将使用函数对象(a.k.a。“functor”,请参阅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 *
而不仅仅通过窗口int
查找内容,或者对其进行强力扫描罕见的注销等
我没有可以测试的环境,但它应该很接近。祝你好运!
答案 1 :(得分:1)
问题是成员函数指针需要调用某些信息,即要调用的实例,然后{{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);
}
,因此您只需要执行一次,因此它应该没问题。我只是不想让你保持这种“危险”。 ;)