0

いくつかの理由で、externClibで定義された構造体を使用する必要があります。読みやすくするために、コードを簡略化しました。

CLibで定義された構造体

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、関数ポインターが処理できないポインターが必要です。

多くの場合、静的な「トランポリン」関数を定義してメソッドを取得できますが、そのためには、外部レイヤー(この場合はCコード) がポインターvoid *の格納先などの受け渡しをサポートしている必要があります。this

于 2012-05-04T10:23:43.623 に答える
1

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

残念ながら、それが実行されたとき、それはsegfaultedしました。さらに調査すると、少なくとも32ビットLinux for x86のGCCでは、sizeof(メンバー関数ポインター)は8であり、sizeof(非メンバー関数ポインター)は4であることがわかります。FuncR::_functionのタイプをuint64_tに変更した場合、驚くべきことに、呼び出しは成功しました。(私もびっくりしました。)

したがって、エラーなしでコンパイルできるキャストマジックに関係なく、少なくとも32ビットx86のGCCでは、メンバー関数ポインターを非メンバー関数ポインターにスクイーズする方法はまったくありません。そして、あなたができたとしても、彼の投稿で言及されているように、それは「this」ポインタをカプセル化しません。


でも、まだ希望はあると思います。

unwindの投稿はトランポリン関数を示唆していますが、「this」ポインターを個別に渡し、Cコードでそれをvoid*として管理する必要があることを認めています。あなたのCライブラリは変更できないと思いますか?その場合、指定する必要のある関数ポインターの数が限られていると仮定すると、トランポリンに「this」ポインターを渡すことができなくても、トランポリンを実行できるはずです。

使用するこれらのFuncR関数ポインターオブジェクトの多くに合わせたサイズのクラスAオブジェクトポインターの配列を作成できます。

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

次に、その多くの物理的な静的非メンバー関数(それぞれに関連付けられた配列インデックスに対応するサフィックス番号で便利に名前が付けられます)を作成し、それぞれの本体で、arrayThatHoldsAObjPointersの関連付けられた「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...

次に、Cライブラリで使用するFuncRオブジェクトを設定する必要がある場合は、トランポリンの1つのアドレスを指定すると同時に、そのAオブジェクトへのポインターを関連するarrayThatHoldsAObjPointers[]要素に格納します。これらを設定するコードを使いやすくするために、トランポリンへの関数ポインターの配列を作成することもできます。

于 2012-05-04T17:52:58.743 に答える