FuncR 结构真的需要在 C 代码中定义/使用吗?可以使用 C++ 风格的成员函数指针吗?
这个怎么样?...
class A;
struct classA_MemberFuncR {
double(A::*_function)(double);
};
class A {
public:
A() : m(1.234) {};
double foo(double x) {
std::cout << "In foo(" << x << "): this="
<< (void*)this << ", m=" << m << '\n';
return m+x;
}
void bar();
private:
double m; // non-static member by nature
};
void A::bar() {
classA_MemberFuncR fm;
fm._function = &A::foo;
double result = (this->*fm._function)(4.321);
std::cout << "Result is: " << result << std::endl;
};
[此时添加的评论:]当。重新阅读OP的原始帖子。 (假设我们被 C 结构的非成员函数指针卡住了。)
嗯。在 GCC 上,我尝试了很多转换组合,但它从来没有让我将 M::foo 的地址转换为其他任何东西,所以我编写了一个运行时转换函数模板来强制它允许我将任何东西转换为任何其他类型我想毫无怨言(大家:请不要再喊“那不是便携的!”当然是便携的......这就是你使用它的方式,它可能是便携的,也可能不是便携的!):
/*---- In a header file somewhere... ----*/
#include <stdarg.h>
template <typename ToTy, typename FromTy>
ToTy forceCast_helper(int dummy, ...);
template <typename ToTy, typename FromTy>
inline ToTy forceCast(FromTy obj) {
// ...has nothing to do with Star Wars!
return forceCast_helper<ToTy,FromTy>(1, obj);
}
/*---- In a source file somewhere... ----*/
template <typename ToTy, typename FromTy>
ToTy forceCast_helper(int dummy, ...) {
va_list args;
va_start(args, dummy);
ToTy ret = va_arg(args, ToTy);
va_end(args);
return ret;
}
这让我可以毫无错误地编译以下代码:
typedef double(*doubleFuncDouble_t)(double);
typedef double(A::*doubleClassAMemberfuncDouble_t)(double);
f._function = forceCast<doubleFuncDouble_t>(&A::foo);
// and then call this->foo(4.321) with it from within A::bar() as follows...
(this->*(forceCast<doubleClassAMemberfuncDouble_t>(f._function)))(4.321);
不幸的是,它在运行时出现了段错误。进一步调查表明,至少在 x86 的 32 位 Linux 的 GCC 上,sizeof(成员函数指针)为 8,而 sizeof(非成员函数指针)为 4。当我将 FuncR::_function 的类型更改为 uint64_t ,令人惊讶的是,调用成功了。 (我也很惊讶。)
因此,无论您发现任何铸造魔法似乎都可以毫无错误地编译,实际上您根本无法将成员函数指针压缩到非成员函数指针中,至少在 GCC 上是 32-位 x86。即使可以,也不会像他的帖子中提到的“展开”那样封装“this”指针。
不过我觉得还是有希望的。
unwind 的帖子建议使用蹦床函数,但承认它需要单独传递“this”指针并在 C 代码中将其作为 void* 进行管理。我假设您的 C 库不可修改?如果是这样,假设您需要指定的此类函数指针数量有限,您应该仍然可以在不通过它传递“this”指针的情况下进行蹦床:
您可以创建一个 A 类对象指针数组,大小与您将使用的这些 FuncR 函数指针对象的数量无关:
A* arrayThatHoldsAObjPointers[8]; // assuming you only need 8 such FuncR func ptr objects
然后创建许多物理静态非成员函数(每个都用与其关联数组索引对应的后缀编号方便地命名),并在每个函数的主体中,通过其关联的“A”对象调用 A::foo()在arrayThatHoldsAObjPointers中:
double trampoline_0(double d) { return arrayThatHoldsAObjPointers[0]->foo(d); }
double trampoline_1(double d) { return arrayThatHoldsAObjPointers[1]->foo(d); }
...and so on...
然后,当您需要设置一个 FuncR 对象以供您的 C 库使用时,给它一个蹦床的地址,同时,将指向该 A 对象的指针存储在关联的 arrayThatHoldsAObjPointers[] 元素中。为了使设置这些的代码更易于使用,您还可以创建一个指向蹦床的函数指针数组。