28

C ライブラリを使用して C++ アプリケーションを開発しています。関数へのポインタを C ライブラリに送信する必要があります。

これは私のクラスです:

 class MainWindow : public QMainWindow {  
     Q_OBJECT  
     public:  
     explicit MainWindow(QWidget *parent = 0);  
     private:  
     Ui::MainWindow *ui;
     void f(int*);

 private slots:
     void on_btn_clicked(); 
};

これは私の on_btn_clicked 関数です:

void MainWindow::on_btn_clicked()
{
    void (MainWindow::* ptfptr) (int*) = &MainWindow::f;

    c_library_function(static_cast<void()(int*)>(ptfptr), NULL);

}

C 関数は、そのような関数へのポインタを取得する必要があります: void f(int*)。しかし、上記のコードは機能しません。 f メンバー関数を目的のポインターに変換できません。

誰でも助けてもらえますか?

4

7 に答える 7

25

非静的メンバー関数ポインターを通常の関数ポインターとして渡すことはできません。それらは同じものではなく、おそらく同じサイズでさえありません。

ただし、(通常) C を介して静的メンバー関数へのポインターを渡すことができます。通常、C API でコールバックを登録する場合、登録された関数に戻される「ユーザー データ」ポインターも渡すことができます。したがって、次のようなことができます。

class MyClass
{

    void non_static_func(/* args */);

public:
    static void static_func(MyClass *ptr, /* other args */) {
        ptr->non_static_func(/* other args */);
    }
};

次に、コールバックを次のように登録します

c_library_function(MyClass::static_func, this);

つまり、インスタンス ポインターを静的メソッドに渡し、それを転送関数として使用します。

完全な移植性のために厳密に言えばextern "C"、静的メンバーではなく宣言された無料の関数を使用して転送を行う必要があります (friend必要に応じて a として宣言されます)。 、これは C のコールバックを多用します。

于 2013-11-06T09:28:20.220 に答える
18

関数ポインターを非静的メンバー関数に渡すことはできません。できることは、インスタンス パラメーターを使用して呼び出しを行う静的関数またはグローバル関数を作成することです。

関数ラッパーと、ラッパーを呼び出すコールバック関数の 2 つのメンバーを持つヘルパー クラスを使用する便利な例を次に示します。

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
    template <typename... Args>
    static Ret callback(Args... args) { return func(args...); }
    static std::function<Ret(Params...)> func;
};

// Initialize the static member.
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

これを使用すると、( を使用して) 呼び出し可能な任意の非静的メンバー関数を格納std::bindし、関数を使用して c-pointer に変換できCallback::callbackます。例えば:

struct Foo {
    void print(int* x) { // Some member function.
        std::cout << *x << std::endl;
    }
};

int main() {
    Foo foo; // Create instance of Foo.

    // Store member function and the instance using std::bind.
    Callback<void(int*)>::func = std::bind(&Foo::print, foo, std::placeholders::_1);

    // Convert callback-function to c-pointer.
    void (*c_func)(int*) = static_cast<decltype(c_func)>(Callback<void(int*)>::callback);

    // Use in any way you wish.
    std::unique_ptr<int> iptr{new int(5)};
    c_func(iptr.get());
}
于 2013-11-06T10:36:53.627 に答える
12

正しく思い出せば、関数構文への「通常の」Cポインターを介してアクセスできるのは、クラスの静的メソッドのみです。したがって、静的にするようにしてください。クラスのメソッドへのポインターには、純粋な C メソッドでは意味を持たない「オブジェクト」(this) などの追加情報が必要です。

ここに示されている FAQには、適切な説明と、問題に対する可能な (醜い) 解決策が含まれています。

于 2013-11-06T09:24:56.583 に答える
7

@Snpsの答えは素晴らしいです。私は常にvoidパラメーターなしでコールバックを使用するため、コールバックを作成するメーカー関数で拡張しました。

typedef void (*voidCCallback)();
template<typename T>
voidCCallback makeCCallback(void (T::*method)(),T* r){
  Callback<void()>::func = std::bind(method, r);
  void (*c_function_pointer)() = static_cast<decltype(c_function_pointer)>(Callback<void()>::callback);
  return c_function_pointer;
}

それ以降は、クラス内または他の場所からプレーン C コールバックを作成し、メンバーを呼び出すことができます。

voidCCallback callback = makeCCallback(&Foo::print, this);
plainOldCFunction(callback);
于 2014-03-18T17:33:40.923 に答える
2

簡単に言えば、 std::mem_fnを使用して、メンバー関数ポインターを通常の C 関数ポインターに変換できます。

それが与えられた質問に対する答えですが、質問者はCコードがMainWindow *を持たずにMainWindowのインスタンスメソッドを呼び出すことができると期待しているため、この質問は混乱した前提を持っているようです。これは単に不可能です。

mem_fn を使用MainWindow::on_btn_clickedして C 関数ポインターにキャストするMainWindow*と、最初の引数として a を受け取る関数のままになります。

void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked);

それは与えられた質問に対する答えですが、インターフェースと一致しません。特定のインスタンスへの呼び出しをラップする C 関数を作成する必要があります (結局のところ、C API コードは MainWindow またはその特定のインスタンスについて何も知りません)。

void window_button_click_wrapper(int* arg)
{
    MainWindow::inst()->on_btn_clicked(arg);
}

これは OO アンチパターンと見なされますが、C API はオブジェクトについて何も知らないため、これが唯一の方法です。

于 2015-12-03T15:36:08.313 に答える