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
    
    模板
    类委托
    {
    //静态_断言(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);
    }
    };
    
    您可以像.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
    
    A类
    {
    公众:
    void M(){…}
    };
    B类
    {
    公众:
    void M(){…}
    };
    A A;
    B B;
    委托d=委托(&a,&a::M);
    d();//调用A::M
    d=委托(&b,&b::M);
    d();//调用B::M
    
    这适用于没有参数的方法。如果可以使用C++11,则可以修改它以使用可变模板来处理任意数量的参数。如果没有C++11,则需要添加更多委托专门化来处理特定数量的参数:

    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,…)
    时(或其任何等效形式)在运行时调用的正确成员函数
    Ai::f
    ,必须在给定variant对象
    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