アフシン:
私は私のコードを取り扱う一般のメッセージを作成しようとしています。各メッセージは、整数IDによって識別されます。すべてのメッセージハンドラは、同様の減速があると私はすぐにそれぞれのメッセージを処理したいので、私が使用しstd::map
接続して、特定のメッセージIDのメッセージハンドラを対応見つけること。それから私は、このハンドラを呼び出すと、それにメッセージを渡します。いくつかのこの操作を行うことだったし、ここでの例であるがあります。
const std::map<int, void(*)(void*)> g_handlers = {
{1, h1},
{2, h2}
};
...
// message
int message_id = 2;
int data = 3;
// handle message
g_handlers[message_id](&data);
しかし、この方法のためのいくつかの大きな制限があります。
- 異なるメッセージがあるので、私たちは、それらを渡すことによって、それらを一般化する必要がある
void*
パラメータ。このように、すべてのメッセージハンドラの構文は次のようになりますvoid (*)(void*)
し、我々は、マップの値として使用することができるようになります。 - このメッセージには型チェックはありません。誰かが間違ってメッセージID 2用メッセージID 1のメッセージハンドラを追加した場合、我々はすぐにこのバグを見つけられないことがあります。
私はこれらの問題を解決する方法を見つけるためにしようとしていたので、何か新しいことを試してみたかったと私は最終的に作業コードに達しています。ここでは、コードは次のようになります。
class handler_base {
public:
template <typename U>
void operator()(U* arg) {
run(arg, typeid(U));
}
private:
virtual void run(void* arg, const std::type_info& info) {}
};
template<typename T>
class handler : public handler_base {
public:
using type = T;
handler(void (*f)(T*)) :func(f) {
}
private:
void run(void* arg, const std::type_info& info) {
assert(info.hash_code() == typeid(T).hash_code());
func(static_cast<T*>(arg));
}
void (*func)(T*);
};
int main()
{
// 2 different types of handlers
handler h1(+[](double* v){ std::cout << "double called " << *v << "\n"; });
handler h2(+[](int* v){ std::cout << "int called " << *v << "\n"; });
const std::map<int, handler_base&> myhandler = {
{1, h1},
{2, h2}
};
double d = 1.5;
int i = 3;
myhandler.at(1)(&d);
//myhandler.at(1)(&i); // Error: failed assert due to type check
//myhandler.at(2)(&d); // Error: failed assert due to type check
myhandler.at(2)(&i);
}
今ここに私の質問は以下のとおりです。
- 使用している
&
マップがあるときに有効なマップ値としてconst
?私は自分自身をマップするとき、それはないですか分からないのですconst
が、それはこのケースではないか修正した場合、私は疑問に思います。 - どのような方法は、これを行うための簡単な方法がありますか?型チェックと同じコンテナを使用して別のコールバックメッセージハンドラの構文を提供しますか?
- あなたは、一般的にこのアイデアについてどう思いますか?型チェックと異質のコールバックのために、この複雑さを追加することをお勧めますか?私は個人的には常に「のこのルールのために行くシンプルがベストです」と私は普通(一般使用して、最初のアプローチを選択し
void(*)(void*)
、コールバックのために)、私はあなたがそれについてどう思いますか知りたいです。
プリンスライナー・舞踊:
私はあなたが完全に基本クラスをスキップすることができると思います。あなただけのラウンドトリップ変換のためのいくつかの関数ポインタとして直接関数ポインタを格納します。私はまた、多くのパラメータを受け入れる作っ:
#include <unordered_map>
#include <iostream>
#include <cassert>
struct Handler
{
template <typename T>
Handler(T fn)
: f((void(*)())(fn))
, info(typeid(T))
{
}
template <typename... Args>
void operator()(Args&&... args)
{
using Fn = void(Args...);
assert(info.hash_code() == typeid(Fn*).hash_code());
return ((Fn*)(f))(std::forward<Args>(args)...);
}
void (*f)();
const std::type_info& info;
};
int main()
{
std::unordered_map<int, Handler> cbmap;
cbmap.emplace(1, +[](int a, double b){std::cout << "1" << a << " " << b << "\n";});
cbmap.emplace(2, +[](double a){std::cout << "2" << a << "\n";});
cbmap.emplace(3, +[](double& a){std::cout << "3 " << a << "\n";});
double x = 42.0;
cbmap.at(1)(42,4.2);
cbmap.at(2)(4.2);
cbmap.at(3)(x);
}