0

出于多种原因,我必须使用在 extern C lib 中定义的结构。我已经简化了我的代码以使其可读。

C Lib 中定义的结构体

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必须声明为静态的。就我而言,这是不可能的,因为它必须使用非静态成员......

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

4

2 回答 2

3

不,我不认为有“解决”这个问题的技巧。

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

通常你可以定义一个静态的“trampoline”函数来让你进入一个方法,但这需要外层(在这种情况下是C代码)支持传递,例如 void *你可以存储this指针的地方。

于 2012-05-04T10:23:43.623 回答
1

Does the FuncR struct really need to be defined/used in C code? Can you use C++-style member function pointers?

How about this?...

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;
};

[Comment added at this point:] Dang. Re-read OP's original post. (Supposedly we're stuck with the C struct's non-member function pointer.)

Hmm. On GCC I tried a whole lot of casting combinations and it never let me cast M::foo's address to much of anything else, so I wrote a runtime cast function template to force it to allow me to cast anything at all to any other type I want without complaining (Everyone: Please hold off on the screams of "that's not portable!" Sure it's portable... It's just how you use it that may or may not be portable!):

/*---- 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;
}

Which allowed me to compile the following code without error:

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);

Unfortunately when it ran, it segfaulted. Further investigation shows that, at least on GCC for 32-bit Linux for x86, sizeof(a member function pointer) is 8, while sizeof(a nonmember function pointer) is 4. When I changed the type of FuncR::_function to uint64_t, amazingly, the call succeeded. (I was surprised as well.)

So it seems regardless of any casting magic you might find that compiles without error, there's really no way at all you'd ever be able to squeeze a member function pointer into a nonmember function pointer, at least on GCC for 32-bit x86. And even if you could, that doesn't encapsulate the 'this' pointer, as 'unwind' mentioned in his post.


I think there's still hope, though.

unwind's post suggests a trampoline function but concedes that it would require separately passing the 'this' pointer & managing that in the C code as a void*. I'm assuming your C library is not modifiable? If so, you should still be able to do a trampoline without being able to pass a 'this' pointer through it, assuming you have a limited number of such function pointers you'll need to be specifying:

You could create an array of class A object pointers, sized to however many of these FuncR function pointer objects you'll be using:

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

Then create that many physical static nonmember functions (each conveniently named with a suffix number corresponding with its associated array index), and in the body of each, make them call A::foo() via its associated 'A' object in the arrayThatHoldsAObjPointers:

double trampoline_0(double d) { return arrayThatHoldsAObjPointers[0]->foo(d); }
double trampoline_1(double d) { return arrayThatHoldsAObjPointers[1]->foo(d); }
...and so on...

Then when you need to set a FuncR object for use by your C library, give it the address of one of the trampolines, and at the same time, store the pointer to that A object in the associated arrayThatHoldsAObjPointers[] element. To make the code that sets these easier to use, you could also create an array of function pointers to the trampolines.

于 2012-05-04T17:52:58.743 回答