尝试使用指向成员函数的指针时出现强制转换问题

Casting issue when trying to use pointers to member functions

本文关键字:问题 转换 指针 函数 成员      更新时间:2023-10-16

出于几个原因,我必须使用在 extern C 库中定义的结构。我简化了我的代码,以使其可读。

在 C 库中定义的结构

extern "C" {
    typedef struct {
        double (*_function)(double x);
    } FuncR;
}

包含 A 类的 Cpp 文件

Class A {
public:
    A();
    void foo(double x); // Cannot be static since it uses m
    void bar();
private:
    double m; // non-static member by nature
};
void A::bar() {
    FuncR f;
    f._function = &A::foo;
};

调用f._function = &A::foo;生成以下错误:

error C2440 : '=' : cannot convert from 'double (__thiscall A::*)(double)' to 'double(__cdecl*)(double)'

我一直在寻找答案,显然foo必须宣布为静态。就我而言,这是不可能的,因为它必须使用非静态成员......

有什么技巧可以解决我的问题吗?

不,我认为没有"修复"这个问题的技巧。

方法调用需要一个this指针,函数指针无法处理该指针。

通常,您可以定义一个静态的"蹦床"函数来进入方法,但这需要外层(在本例中为 C 代码(支持传递,例如可以存储this指针的void *

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 时,令人惊讶的是,调用成功了。 (我也很惊讶。

因此,似乎无论您发现编译没有错误的任何铸造魔法,您实际上都无法将成员函数指针压缩到非成员函数指针中,至少在 32 位 x86 的 GCC 上。 即使你可以,这也不能概括"这个"指针,就像他的帖子中提到的"放松"一样。


不过,我认为仍有希望。

unwind的帖子提出了一个蹦床函数,但承认它需要单独传递"this"指针,并在C代码中将其作为void*进行管理。 我假设您的 C 库不可修改? 如果是这样,你仍然应该能够做一个蹦床,而不能通过它传递"this"指针,假设你有有限数量的这样的函数指针,你需要指定:

您可以创建一个类 A 对象指针数组,其大小为您将使用的 FuncR 函数指针对象的数量:

A* arrayThatHoldsAObjPointers[8]; // assuming you only need 8 such FuncR func ptr objects

然后创建许多物理静态非成员函数(每个函数都方便地命名为与其关联的数组索引对应的后缀编号(,并在每个函数的主体中,通过数组中关联的"A"对象调用 A::foo((:

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[] 元素中。 为了使设置这些内容的代码更易于使用,您还可以创建一个指向蹦床的函数指针数组。