4

C API で結果をコールバックとして使用できるように、boost または std bind() を使用する方法はありますか? 私が使用するサンプルコードは次のとおりです。

#include <boost/function.hpp>
#include <boost/bind/bind.hpp>

typedef void (*CallbackType)();

void CStyleFunction(CallbackType functionPointer)
{
    functionPointer();
}

class Class_w_callback
{
public:
    Class_w_callback()
    {
        //This would not work
    CStyleFunction(boost::bind(&Class_w_callback::Callback, this));
    }
    void Callback(){std::cout<<"I got here!\n";};
};

ありがとう!

4

4 に答える 4

3

これは、移植可能な C++ では実行できません。ただし、クロージャーに似た C 関数の作成を可能にするライブラリがあります。これらのライブラリの実装にはアセンブリ コードが含まれており、新しいプラットフォームへの手動の移植が必要ですが、関心のあるアーキテクチャをサポートしている場合、問題なく動作します。

たとえば、 Bruno Haible によるトランポリンライブラリを使用すると、次のようなコードを記述できます。

extern "C" {
#include <trampoline.h>
}

#include <iostream>

typedef int (*callback_type)();

class CallbackDemo {
  static CallbackDemo* saved_this;
public:
  callback_type make_callback() {
    return reinterpret_cast<callback_type>(
      alloc_trampoline(invoke, &saved_this, this));
  }

  void free_callback(callback_type cb) {
    free_trampoline(reinterpret_cast<int (*)(...)>(cb));
  }

  void target(){
    std::cout << "I got here, " << this << '\n';
  };

  static int invoke(...) {
    CallbackDemo& me = *saved_this;
    me.target();
    return 0;
  }
};

CallbackDemo *CallbackDemo::saved_this;

int main() {
  CallbackDemo x1, x2;
  callback_type cb1 = x1.make_callback();
  callback_type cb2 = x2.make_callback();
  cb1();
  cb2();
}

静的メンバーを使用しているにもかかわらず、によって作成されたトランポリンalloc_trampolineは再入可能であることに注意してください。返されたコールバックが呼び出されると、最初にポインタが指定されたアドレスにコピーされ、次に元の引数で元の関数が呼び出されます。コードをスレッドセーフにsaved_thisする必要がある場合は、スレッドローカルにする必要があります。

于 2013-09-17T12:33:30.577 に答える
1

グローバル

他の人が述べたように、グローバルが必要です(静的メンバーは変数 member として隠されているグローバルです)。もちろん、コールバックで異なるパラメーターを使用するために複数のオブジェクトが必要な場合は機能しません。

コールバックのコンテキスト パラメータ

AC ライブラリは、void *または類似のコンテキストを提供する場合があります。その場合は、その機能を使用してください。

たとえば、ffmpeg ライブラリは、次のように定義されたデータを読み取るためのコールバックをサポートしています。

int(*read_packet)(void *opaque, uint8_t *buf, int buf_size);

パラメータはopaqueに設定できますthis。コールバック内で、型 (クラスの名前) にキャストするだけです。

Calback のライブラリ コンテキスト パラメータ

structAC ライブラリは、そのオブジェクト (ポインター)を使用してコールバックを呼び出す場合があります。exampleという名前の型を提供し、次のexample_tようなコールバックを定義するという名前のライブラリがあるとします。

callback(example_t *e, int param);

次に、その構造にコンテキスト(別名thisポインター) を配置example_tし、コールバックでそれを取得できる場合があります。

シリアル コール

その特定の C ライブラリを使用するスレッドが 1 つしかなく、ライブラリ内の関数を呼び出したときにのみコールバックをトリガーできる (つまり、ランダムな時点でトリガーされるイベントを取得しない) と仮定すると、グローバル変数を使用できます。 . あなたがしなければならないことは、各呼び出しの前に現在のオブジェクトをグローバルに保存することです。このようなもの:

object_i_am_working_with = this;
make_a_call_to_that_library();

object_i_am_working_withこのようにして、コールバック内でいつでもポインターにアクセスできます。これは、マルチスレッド アプリケーションや、ライブラリがバックグラウンドでイベントを自動的に生成する場合 (つまり、キーの押下、ネットワークからのパケット、タイマーなど) には機能しません。

オブジェクトごとに 1 つのスレッド (C++11 以降)

これは、マルチスレッド環境における興味深いソリューションです。以前の解決策がどれも利用できない場合は、スレッドを使用して問題を解決できる場合があります。

C++11 には、という名前の新しい特殊指定子thread_localがあります。昔は、各スレッドの実装に固有のものを手作業で処理する必要がありました...今ではこれを行うだけです:

thread_local Class_w_callback * callback_context = nullptr;

次に、コールバックで、をクラスcallback_contextへのポインタとして使用できます。Class_w_callback

もちろん、これは、作成するオブジェクトごとに 1 つのスレッドを作成する必要があることを意味します。これは、ご使用の環境では実行できない場合があります。私の場合、すべて独自のループを実行しているコンポーネントがあるため、それぞれに独自のthread_local環境があります。

ライブラリが自動的にイベントを生成する場合、おそらくそれもできないことに注意してください。

スレッドを使用した古い方法 (および C ソリューション)

前述したように、昔はローカル スレッド環境を自分で管理する必要がありました。pthread(Linux ベース) を使用すると、次の方法でスレッド固有のデータにアクセスできますpthread_getspecific()

void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);

これにより、動的に割り当てられたメモリが使用されます。これはおそらく、thread_localLinux の g++ で が実装されている方法です。

MS-Windows では、おそらくTlsAlloc関数を使用するでしょう。

于 2019-06-19T00:03:16.217 に答える