ゴール:
無関係な型、つまり共通の基本クラスを持たない型で、型セーフな動的ポリモーフィズム(つまり、関数呼び出しの実行時ディスパッチ)を実現したいと思います。これは達成可能であるか、少なくとも理論的には健全であるように思われます。私は自分の問題をより正式に定義しようとします。
問題の定義:
次の場合:
- 2つ以上の無関係な型
A1, ..., An
。それぞれに、と呼ばれるメソッドがf
あり、署名が異なる可能性がありますが、戻り型は同じR
です。と boost::variant<A1*, ..., An*>
オブジェクト(または他のv
タイプのバリアント)。これらのタイプのいずれかの値をいつでも想定でき、想定する必要があります。
私の目標は、に含まれる値の実際のタイプがである場合に機能するように実行時にディスパッチさv.f(arg_1, ..., arg_m);
れるものと概念的に同等の命令を作成することです。呼び出し引数が各関数の仮パラメーターと互換性がない場合、コンパイラーはエラーを発生させる必要があります。Ai::f
v
Ai
Ai
もちろん、構文に固執する必要はありません。v.f(arg_1, ..., arg_m)
たとえば、のようなものcall(v, f, ...)
も受け入れられます。
私はこれをC++で達成しようとしましたが、これまでのところ、良い解決策を思い付くことができませんでした(私にはたくさんの悪い解決策があります)。以下に、「良い解決策」の意味を明確にします。
制約:
良い解決策は、v.f(...)
イディオムを模倣できるもの、たとえばcall_on_variant(v, f, ...);
、次の制約を満たすものです。
- この方法で呼び出す必要のある関数ごとに(たとえば)、またはコード内の別の場所、特にグローバルスコープで多態的に処理できる(たとえば)無関係な型のリストに対して、個別の宣言を行う必要はありません。
f
ENABLE_CALL_ON_VARIANT(f)
A1, ..., An
ENABLE_VARIANT_CALL(A1, ..., An)
- 呼び出しを行うときに、入力引数のタイプに明示的に名前を付ける必要はありません(例
call_on_variant<int, double, string>(v, f, ...)
)。リターンタイプの命名はOKなので、たとえばcall_on_variant<void>(v, f, ...)
受け入れられます。
うまくいけば私の願いと要件を明確にする実証的な例に従います。
例:
struct A1 { void f(int, double, string) { cout << "A"; } };
struct A2 { void f(int, double, string) { cout << "B"; } };
struct A3 { void f(int, double, string) { cout << "C"; } };
using V = boost::variant<A1, A2, A3>;
// Do not want anything like the following here:
// ENABLE_VARIANT_CALL(foo, <whatever>)
int main()
{
A a;
B b;
C c;
V v = &a;
call_on_variant(v, f, 42, 3.14, "hello");
// Do not want anything like the following here:
// call_on_variant<int, double, string>(v, f, 42, 3.14, "hello");
V v = &b;
call_on_variant(v, f, 42, 3.14, "hello");
V v = &c;
call_on_variant(v, f, 42, 3.14, "hello");
}
このプログラムの出力は次のようになりますABC
。
最良の(失敗した)試み:
目的のソリューションに最も近いのは、次のマクロです。
#define call_on_variant(R, v, f, ...) \
[&] () -> R { \
struct caller : public boost::static_visitor<void> \
{ \
template<typename T> \
R operator () (T* pObj) \
{ \
pObj->f(__VA_ARGS__); \
} \
}; \
caller c; \
return v.apply_visitor(c); \
}();
テンプレートメンバーのみがローカルクラスで許可されている場合、これは完全に機能します(この質問を参照)。誰かがこれを修正する方法、または別のアプローチを提案する方法を知っていますか?