您需要一个指向函数的指针:
double(*)(double*)
你所拥有的是一个指向成员函数的指针:
double (MyClass::*)(double*)
这些不是一回事。甚至所有类似的。第一个可以直接调用;第二个只能通过对象调用,使用->*
or.*
运算符。
这不仅仅是编译器对类型的挑剔。指向成员函数的指针甚至不是一个指针(或至少不仅仅是一个指针)。它需要更多信息才能处理动态绑定。
有关详细信息,请参阅http://www.parashift.com/c++-faq/pointers-to-members.html 。
在 C 风格 API 中使用指向成员函数的指针的“经典”方法是编写一个MyClass*
从其他地方获取 a 的包装函数(通常是与函数指针一起传递的“userinfo”、“thunk”等) ) 并用它调用myobj->*pmf(args)
。然后,您可以将指针传递给该包装器,因为它只是一个普通函数。更智能的现代方法包括bind
lambdas。
让我们先处理现代方式,因为它更简单,如果可以的话,这是你应该做的。首先,如果你有 C++11,你可以使用std::bind
, 或std::mem_fn
, 或者只写一个 lambda:
auto pt2apply = [&](double *parameter) { return my_solver->pt2apply(parameter); }
现在,这东西的类型不是double(*)(double*)
。事实上,它是一种未指明的东西,而且可能是难以形容的丑陋。这里我们只是用来auto
避免处理它,但我们必须将它传递给set_pt2func
,然后该方法必须将它存储在成员变量中,因此我们需要某种可以键入的类型。答案是std::function<double(double *)>
。这是一个对象,可以存储任何可以用 a 调用double *
并返回 a的副本double
。所以:
void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
pt2Function = pt2function;
}
就是这样。
现在,这需要 C++11。如果您使用的是 C++98,则可以使用boost::lambda
C++11 的 lambda 代替(丑得多,但基本思想相同)。或者您可以使用(几乎与基于 Boost 库的boost::bind
C++11 相同)或具有类似功能的许多其他库之一。std::bind
并存储仿函数boost::function
(同样,几乎与 C++11 相同std::function
,尽管有时效率较低)。(如果你有带有 TR1 的 C++03,你可能在std
or中有一些 C++11 的特性std::tr1
,但是不同的编译器制造商直到 C++11 几乎完成之前都没有理顺细节,所以最简单的使用方法是通过boost::tr1
,如果你有 Boost,你不妨直接使用它。)
如果由于某种原因你被 C++98 卡住并且没有 Boost,你可以使用std::mem_fn
, 并将结果存储在std::binary_function
. 这更难看,所以我不会在这里给出完整的细节。
现在让我们看看经典的解决方案。只有当您有一个现有的 API 使用函数指针(例如,回调)并且您无法更改它时,才真正值得这样做。我建议寻找使用 C 函数的示例代码,例如qsort_r
,因为这个想法几乎相同:您将函数指针与“thunk”一起传递,并且它们两者总是一起旅行。(现代解决方案基本上都是关于将这两个东西绑定到一个对象中,您可以调用该对象而无需跟踪 thunk,以使您的代码更易于阅读和编写,并使编译器更容易捕获错误。 )
这里的关键是您需要一个包装函数,一个可以获取函数指针的普通 C 风格函数。您的 thunk 将是指向您的MyClass
对象的指针。在我的示例中,我将把 thunk 作为 a 传递,void *
因为尽管这显然是次要的(要编写更多代码,编译器捕获错误的机会更少),但这是您最常看到的您需要处理的通用 C API。
所以,把它们放在一起:
void MySolver::set_pt2func(double(*pt2function)(double*, void *), void *userinfo)
{
pt2Function = pt2function;
userInfo = userinfo;
}
double wrapMyClassPt2Apply(double *parameter, void *userinfo)
{
return ((MyClass*)userinfo)->apply_func(parameter);
}
my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);
请注意,如果您使用的是pt2apply
指向成员函数的指针,您将->*
在包装器中调用它。但我们不再需要pt2apply
了;包装器可以只调用apply_func
成员->
。
然后,当 MySolver 想要调用该函数时,它会执行以下操作:
pt2Function(parameter, userInfo);
最后一件事: wrapMyClassPt2Apply 不能是成员函数,否则您会遇到与开始时相同的问题;指向它的指针将是指向成员函数的指针,而不是指针。所以,你有两个选择:
使其成为免费功能。这是一件完全合理的事情。自由函数可以是类接口的一部分。或者,它们甚至可以比私有方法更好地隐藏(通过将它们放在 .cpp 文件中而不在 .h 文件中提及它们)。
使其成为静态方法。有些人更喜欢这个,但实际上,唯一有用的时候是 MyClass.cpp 之外的人将进行set_pt2func
调用,并且已经是 的朋友(或子类)MyClass
,因为这样您可以将方法设为私有(或受保护)。