2016-09-16 19 views
2

C++でクラスへのコールバックを行う最もクリーンな方法は何ですか?C++ 11ではなくC++でコールバックを行う最もクリーンな方法は?

私は通常のような関数を作成:

void registerCallback(void(*callback)(void* param), void* param); 

そして、このようなようにそれを呼び出す:

foo->registerCallback(callbackStatic, this); 

それから私は、このようなように私のクラスの静的関数を追加します。

static void callbackStatic(void* param) 
{ 
    ((Type*)(param))->callback(); 
} 

それは動作しますが、それは痛みです。コールバックごとに2つの関数を作成する必要があります。私がC++ 11を使うことができたら、代わりにラムダを使うでしょう。 Qtは非常に優れた信号とスロットのメカニズムを持っていますが、特別なプリコンパイラが必要です。

私はライブラリを作成していますので、要件を可能な限り低くしたいと思います。いいえC++ 11、ブーストなしなど.C++ 03だけを使ってlambdaやシグナル/スロットのようなコールバックシステムを構築する方法はありますか?

+2

C++ 11は本当に大きな障壁ですか?これは標準化されていなくても5年間標準であり、コンパイラはサポートされています。また、移動セマンティクスのためにコードをより効率的にすることができます。 – NathanOliver

+1

@ NathanOliver残念ながら、いくつかの企業にとってはそれが可能です。ミッションクリティカルな操作のコードがVS 2008のみに依存していることに驚かれるでしょう。 –

+0

@RawN私はそれを知っていますが、ほとんどの場合、アップグレードによって有効にならない新機能と同様に完全にテストする必要があるため、このライブラリを使用することはありません。 – NathanOliver

答えて

1

ラムダやシグナル/スロットのようなコールバックシステムをC++ 03で作成する方法はありますか?

もう1つの方法は、純粋で古い純粋な抽象インターフェイスを使用することです。これは、コールバック関数によるクラスメンバ関数の2段階補強を必要とせず、仮想多形性を使用するだけです。

CRTPとして静的多型を使用することもできます。これは、pre C++ 11標準でも動作します。

これは初期の標準化以降のものでした。 @R Sahu's answer

4


コードサンプル、あなたがゼロからスタートすることができるならば、私はC++でのコールバックのための関数を使用していないお勧めします。代わりに、オブジェクトを使用します。

struct CallbackHandler { virtual void doit() = 0; }; 

void registerCallback(Callbackhandler* handler); 

、その後

struct MyCallbackHandler : public CallbackHandler { ... }; 

registerCallback(new MyCallbackHandler()); 
+0

このようにするのは、2つのコールバックを購読する場合、両方ともdoit()を呼び出すため、ソースパラメータなどを渡す必要があります。 –

+0

@FigBug、私の答えは、コアアイデアを指摘しています。特定のニーズに合わせてさまざまな方法で洗練されています。 –

+0

@FigBugあなたのコメントがありませんか? –

1

これは少し複雑かもしれませんが、それは私の作品:

#include <functional> 
#include <iostream> 
#include <vector> 

class C { 
public: 
    void foo() { 
     std::cout << "foo" << std::endl; 
    } 
}; 

class D { 
public: 
    void bar() { 
     std::cout << "bar" << std::endl; 
    } 
}; 

class abstract_bind { 
public: 
    virtual void operator()() = 0; 
    virtual ~abstract_bind() {} 
}; 

template <typename F, typename T> 
class bind : public abstract_bind { 
public: 
    bind(F f_, T *t_) : f(f_), t(t_) {} 

    virtual void operator()() { 
     f(t); 
    } 

private: 
    F f; 
    T *t; 
}; 


int main() { 
    C c1, c2; 
    D d; 

    std::vector<abstract_bind*> v; 

    v.push_back(new bind<std::mem_fun_t<void, C>, C>(std::mem_fun(&C::foo), &c1)); 
    v.push_back(new bind<std::mem_fun_t<void, C>, C>(std::mem_fun(&C::foo), &c2)); 
    v.push_back(new bind<std::mem_fun_t<void, D>, D>(std::mem_fun(&D::bar), &d)); 

    for (size_t i=0; i<v.size(); ++i) 
     (*v[i])(); 

    for (size_t i=0; i<v.size(); ++i) 
     delete v[i]; 

    return 0; 
} 

基本的な考え方は、あなたがオブジェクトのメンバ関数を取得するためにstd::mem_funを使用することですそれに対応するポインタをバインドして「プレーン」関数を取得します。

1

あなたはCスタイルのコールバックを維持し、メンバ関数を呼び出すための構造を使用する場合があります。

// C-style callback 
// ================ 

void(*registered_callback)(void* param) = 0; 
void* registered_param = 0; 
void registerCallback(void(*callback)(void* param), void* param) { 
    registered_callback = callback; 
    registered_param = param; 
} 
void unregisterCallback(void(*callback)(void* param), void* param) { 
    registered_callback = 0; 
    registered_param = 0; 
} 

// Invoker 
// ======= 

struct InvokerBase { 
    static void callInvoker(void* invoker) { 
     reinterpret_cast<InvokerBase*>(invoker)->apply(); 
    } 

    virtual void apply() const = 0; 
}; 

template <typename T> 
class Invoker : public InvokerBase 
{ 
    public: 
    Invoker(T& object, void(T::*function)()) 
    : object(&object), function(function) 
    { 
     registerCallback(callInvoker, this); 
    } 
    ~Invoker() { 
     unregisterCallback(callInvoker, this); 
    } 

    void apply() const { 
     (object->*function)(); 
    } 

    private: 
    Invoker(const Invoker&); // no copy 
    Invoker& operator = (const Invoker&); // no copy 

    T* object; 
    void (T::*function)(); 
}; 


// Test 
// ==== 

#include<cassert> 
#include<iostream> 

struct Type { 
    void print() { std::cout << "Hello\n"; } 
}; 

int main() 
{ 
    Type x; 
    { 
     Invoker<Type> print_invoker(x, &Type::print); 
     registered_callback(registered_param); 
    } 
    assert(registered_callback == 0); 
} 
0

あなたがシナプスを試みることができる、それが何のプリコンパイルを必要としない信号スロットのライブラリです。シナプスではエミッタとしてどのようなオブジェクトも使用できます:http://zajo.github.io/boost-synapse/