C++ 如何在不相关的类型上实现动态多态性(运行时调用调度)?
目标:C++ 如何在不相关的类型上实现动态多态性(运行时调用调度)?,c++,c++11,runtime,polymorphism,boost-variant,C++,C++11,Runtime,Polymorphism,Boost Variant,目标: 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>; //
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");
}
我希望在不相关的类型上实现类型安全的动态多态性(即函数调用的运行时调度),即在没有公共基类的类型上。在我看来,这是可以实现的,或者至少在理论上是合理的。我将尝试更正式地定义我的问题
问题定义:
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");
}
鉴于以下情况:
- 两个或多个不相关的类型
A1,…,一个
,每个类型都有一个名为
的方法,可能具有不同的签名,但具有相同的返回类型f
;及R
- a
boost::variant)。有人知道如何解决这个问题吗,或者有人提出了替代方法吗?好的,这里有一个简单的例子:
template <typename R, typename ...Args> struct visitor : boost::static_visitor<R> { template <typename T> R operator()(T & x) { return tuple_unpack(x, t); // this needs a bit of code } visitor(Args const &... args) : t(args...) { } private: std::tuple<Args...> t; }; template <typename R, typename Var, typename ...Args> R call_on_variant(Var & var, Args const &... args) { return boost::apply_visitor(visitor<R, Args...>(args...), var); }
用法:模板 结构访问者:boost::static\u访问者 { 模板 R运算符()(T&x) { return tuple_unpack(x,t);//这需要一些代码 } 访问者(Args const&…Args):t(Args…{} 私人: std::元组t; }; 模板 R调用变量上的变量(Var&Var,Args const&…Args) { 返回boost::apply_visitor(visitor(args…),var); }
R result = call_on_variant<R>(my_var, 12, "Hello", true);
我已经通过解包元组隐藏了调用函数所需的一定数量的工作,但我相信这已经在其他地方完成了R result=call_on_variant(my_var,12,“Hello”,true);
此外,如果需要存储引用而不是参数的副本,也可以这样做,但需要更加小心。(您可以有一组引用。但您必须考虑是否还允许临时对象。)我曾经通过模拟.NET委托解决了这个问题:template<typename T> class Delegate { //static_assert(false, "T must be a function type"); }; template<typename ReturnType> class Delegate<ReturnType()> { private: class HelperBase { public: HelperBase() { } virtual ~HelperBase() { } virtual ReturnType operator()() const = 0; virtual bool operator==(const HelperBase& hb) const = 0; virtual HelperBase* Clone() const = 0; }; template<typename Class> class Helper : public HelperBase { private: Class* m_pObject; ReturnType(Class::*m_pMethod)(); public: Helper(Class* pObject, ReturnType(Class::*pMethod)()) : m_pObject(pObject), m_pMethod(pMethod) { } virtual ~Helper() { } virtual ReturnType operator()() const { return (m_pObject->*m_pMethod)(); } virtual bool operator==(const HelperBase& hb) const { const Helper& h = static_cast<const Helper&>(hb); return m_pObject == h.m_pObject && m_pMethod == h.m_pMethod; } virtual HelperBase* Clone() const { return new Helper(*this); } }; HelperBase* m_pHelperBase; public: template<typename Class> Delegate(Class* pObject, ReturnType(Class::*pMethod)()) { m_pHelperBase = new Helper<Class>(pObject, pMethod); } Delegate(const Delegate& d) { m_pHelperBase = d.m_pHelperBase->Clone(); } Delegate(Delegate&& d) { m_pHelperBase = d.m_pHelperBase; d.m_pHelperBase = nullptr; } ~Delegate() { delete m_pHelperBase; } Delegate& operator=(const Delegate& d) { if (this != &d) { delete m_pHelperBase; m_pHelperBase = d.m_pHelperBase->Clone(); } return *this; } Delegate& operator=(Delegate&& d) { if (this != &d) { delete m_pHelperBase; m_pHelperBase = d.m_pHelperBase; d.m_pHelperBase = nullptr; } return *this; } ReturnType operator()() const { (*m_pHelperBase)(); } bool operator==(const Delegate& d) const { return *m_pHelperBase == *d.m_pHelperBase; } bool operator!=(const Delegate& d) const { return !(*this == d); } };
class A { public: void M() { ... } }; class B { public: void M() { ... } }; A a; B b; Delegate<void()> d = Delegate<void()>(&a, &A::M); d(); // calls A::M d = Delegate<void()>(&b, &B::M); d(); // calls B::M
您可以像.NET代理一样使用它:模板 类委托 { //静态_断言(false,“T必须是函数类型”); }; 模板 类委托 { 私人: 类助手数据库 { 公众: 助手数据库() { } 虚拟~HelperBase() { } 虚拟返回类型运算符()()常量=0; 虚拟布尔运算符==(常量HelperBase&hb)常量=0; 虚拟助手数据库*Clone()常量=0; }; 模板 类助手:公共助手数据库 { 私人: 类别*m_对象; 返回类型(类::*m_pMethod)(); 公众: Helper(类*pObject,返回类型(类::*pMethod)():m_pObject(pObject),m_pMethod(pMethod) { } 虚拟~Helper() { } 虚拟返回类型运算符()()常量 { 返回(m_pObject->*m_pMethod)(); } 虚拟布尔运算符==(常量HelperBase&hb)常量 { const Helper&h=静态施法(hb); 返回m_pObject==h.m_pObject&&m_pMethod==h.m_pMethod; } 虚拟助手数据库*克隆()常量 { 返回新助手(*此); } }; HelperBase*m_pHelperBase; 公众: 模板 委托(类*POObject,返回类型(类::*pMethod)() { m_pHelperBase=新助手(pObject,pMethod); } 代表(常数代表和d) { m_pHelperBase=d.m_pHelperBase->Clone(); } 代表(代表和d) { m_pHelperBase=d.m_pHelperBase; d、 m_pHelperBase=nullptr; } ~Delegate() { 删除m_pHelperBase; } 委托和运算符=(常量委托和d) { 如果(此!=&d) { 删除m_pHelperBase; m_pHelperBase=d.m_pHelperBase->Clone(); } 归还*这个; } 委托和运算符=(委托和运算符) { 如果(此!=&d) { 删除m_pHelperBase; m_pHelperBase=d.m_pHelperBase; d、 m_pHelperBase=nullptr; } 归还*这个; } ReturnType运算符()()常量 { (*m_pHelperBase)(); } 布尔运算符==(常量委托和d)常量 { 返回*m_pHelperBase==*d.m_pHelperBase; } 布尔运算符!=(常量代理和d)常量 { 返回!(*this==d); } };
template<typename T> class Delegate { //static_assert(false, "T must be a function type"); }; template<typename ReturnType> class Delegate<ReturnType()> { private: class HelperBase { public: HelperBase() { } virtual ~HelperBase() { } virtual ReturnType operator()() const = 0; virtual bool operator==(const HelperBase& hb) const = 0; virtual HelperBase* Clone() const = 0; }; template<typename Class> class Helper : public HelperBase { private: Class* m_pObject; ReturnType(Class::*m_pMethod)(); public: Helper(Class* pObject, ReturnType(Class::*pMethod)()) : m_pObject(pObject), m_pMethod(pMethod) { } virtual ~Helper() { } virtual ReturnType operator()() const { return (m_pObject->*m_pMethod)(); } virtual bool operator==(const HelperBase& hb) const { const Helper& h = static_cast<const Helper&>(hb); return m_pObject == h.m_pObject && m_pMethod == h.m_pMethod; } virtual HelperBase* Clone() const { return new Helper(*this); } }; HelperBase* m_pHelperBase; public: template<typename Class> Delegate(Class* pObject, ReturnType(Class::*pMethod)()) { m_pHelperBase = new Helper<Class>(pObject, pMethod); } Delegate(const Delegate& d) { m_pHelperBase = d.m_pHelperBase->Clone(); } Delegate(Delegate&& d) { m_pHelperBase = d.m_pHelperBase; d.m_pHelperBase = nullptr; } ~Delegate() { delete m_pHelperBase; } Delegate& operator=(const Delegate& d) { if (this != &d) { delete m_pHelperBase; m_pHelperBase = d.m_pHelperBase->Clone(); } return *this; } Delegate& operator=(Delegate&& d) { if (this != &d) { delete m_pHelperBase; m_pHelperBase = d.m_pHelperBase; d.m_pHelperBase = nullptr; } return *this; } ReturnType operator()() const { (*m_pHelperBase)(); } bool operator==(const Delegate& d) const { return *m_pHelperBase == *d.m_pHelperBase; } bool operator!=(const Delegate& d) const { return !(*this == d); } };
class A { public: void M() { ... } }; class B { public: void M() { ... } }; A a; B b; Delegate<void()> d = Delegate<void()>(&a, &A::M); d(); // calls A::M d = Delegate<void()>(&b, &B::M); d(); // calls B::M
这适用于没有参数的方法。如果可以使用C++11,则可以修改它以使用可变模板来处理任意数量的参数。如果没有C++11,则需要添加更多委托专门化来处理特定数量的参数:A类 { 公众: void M(){…} }; B类 { 公众: void M(){…} }; A A; B B; 委托d=委托(&a,&a::M); d();//调用A::M d=委托(&b,&b::M); d();//调用B::M
template<typename ReturnType, typename Arg1> class Delegate<ReturnType(Arg1)> { ... }; template<typename ReturnType, typename Arg1, typename Arg2> class Delegate<ReturnType(Arg1, Arg2)> { ... };
模板 类委托 { ... }; 模板 类委托 { ... };
使用这个委托类,也可以模仿.NET事件,这些都是基于委托的。
不幸的是,这不能在C++中完成(但-见结论)。遵循一个证明
考虑事项1:[关于模板的需要] 为了确定在满足表达式
时(或其任何等效形式)在运行时调用的正确成员函数call\u-on\u variant(v,f,…)
,必须在给定variant对象Ai::f
的情况下,检索v
持有的值的类型v
。这样做必然需要至少定义一个(类或函数)模板 这样做的原因是,无论如何做,都需要迭代变量可以保存的所有类型(类型列表显示为Ai
,检查变量是否保存该类型的值(通过boost::variant::types
),以及(如果是)检索该值作为必须执行成员函数调用的指针(在内部,这也是boost::get
所做的) 对于列表中的每一种类型,都可以这样做boost::apply\u visitor
// Helper template for type resolution template<typename F, typename V> struct extractor { extractor(F f, V& v) : _f(f), _v(v) { } template<typename T> void operator () (T pObj) { T* ppObj = get<T>(&_v)); if (ppObj != NULL) { _f(*ppObj); return; } } F _f; V& _v; }; // v is an object of type boost::variant<A1*, ..., An*>; // f is the name of the function to be invoked; // The remaining arguments are the call arguments. #define call_on_variant(v, f, ...) \ using types = decltype(v)::types; \ auto lam = [&] (auto pObj) \ { \ (*pObj)->f(__VA_ARGS__); \ }; \ extractor<decltype(lam), decltype(v)>(); \ mpl::for_each<types>(ex);
#include <boost/variant.hpp> template<typename R, typename F> class delegating_visitor : public boost::static_visitor<R> { public: delegating_visitor(F&& f) : _f(std::forward<F>(f)) { } template<typename T> R operator () (T x) { return _f(x); } private: F _f; }; template<typename R, typename F> auto make_visitor(F&& f) { using visitor_type = delegating_visitor<R, std::remove_reference_t<F>>; return visitor_type(std::forward<F>(f)); } template<typename R, typename V, typename F> auto vcall(V&& vt, F&& f) { auto v = make_visitor<R>(std::forward<F>(f)); return vt.apply_visitor(v); } #define call_on_variant(val, fxn_expr) \ vcall<int>(val, [] (auto x) { return x-> fxn_expr; });
#include <iostream> #include <string> struct A { int foo(int i, double d, std::string s) const { std::cout << "A::foo(" << i << ", " << d << ", " << s << ")"; return 1; } }; struct B { int foo(int i, double d, std::string s) const { std::cout << "B::foo(" << i << ", " << d << ", " << s << ")"; return 2; } };
int main() { A a; B b; boost::variant<A*, B*> v = &a; auto res1 = call_on_variant(v, foo(42, 3.14, "Hello")); std::cout << std::endl<< res1 << std::endl; v = &b; auto res2 = call_on_variant(v, foo(1337, 6.28, "World")); std::cout << std::endl<< res2 << std::endl; }
A::foo(42, 3.14, Hello) 1 B::foo(1337, 6.28, World) 2