c++ - 如何在不相关的类型上实现动态多态性(运行时调用调度)?

标签 c++ c++11 runtime polymorphism boost-variant

目标:

我想在不相关的类型上实现类型安全的动态多态性(即函数调用的运行时调度)在不相关的类型上 - 即在执行的类型上没有共同的基类。在我看来,这是可以实现的,或者至少在理论上是合理的。我会尝试更正式地定义我的问题。

问题定义:

鉴于以下情况:

  • 两个或多个不相关类型A1, ..., An , 每个都有一个名为 f 的方法,可能具有不同的签名,但具有相同的返回类型 R ;和
  • 一个 boost::variant<A1*, ..., An*>对象 v (或任何其他类型的变体)可以且必须在任何时候采用任何这些类型的一个值;

我的目标是编写概念上等同于 v.f(arg_1, ..., arg_m); 的指令这将在运行时调度以运行Ai::f如果v 中包含的值的实际类型Ai .如果调用参数与each函数的形参不兼容Ai ,编译器应该引发错误。

当然我不需要拘泥于 v.f(arg_1, ..., arg_m) 的语法。 : 例如,call(v, f, ...)也可以接受。

我试图在 C++ 中实现这一点,但到目前为止我还没有提出一个好的解决方案(我确实有很多不好的解决方案)。下面我澄清一下“好的解决方案”是什么意思。

约束:

好的解决方案是任何能让我模仿 v.f(...) 的东西。成语,例如call_on_variant(v, f, ...); ,并且满足以下约束:

  1. 每个函数都不需要任何类型的单独声明 f必须以这种方式调用(例如 ENABLE_CALL_ON_VARIANT(f) )或任何不相关类型列表 A1, ..., An可以在代码的其他地方进行多态处理(例如 ENABLE_VARIANT_CALL(A1, ..., An) ),尤其是在全局范围内;
  2. 在调用时不需要明确命名输入参数的类型(例如 call_on_variant<int, double, string>(v, f, ...) )。命名 return 类型是可以的,例如 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); \
}();

如果本地类中只允许使用模板成员,那将非常有效(参见 this question)。有没有人知道如何解决这个问题,或建议一种替代方法?

最佳答案

一段时间过去了,C++14 即将完成,编译器正在添加对新功能的支持,例如通用 lambda。

通用 lambdas 与下面显示的机制一起,允许使用不相关的类实现所需的(动态)多态性:

#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;
    }
};

可以通过这种方式多态地调用 foo():

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

该程序已在 VC12 上使用 2013 年 11 月的 CTP 进行了测试。不幸的是,我不知道任何支持通用 lambda 的在线编译器,所以我无法发布一个实时示例。

关于c++ - 如何在不相关的类型上实现动态多态性(运行时调用调度)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14288050/

相关文章:

c++ - 从 'template class' 类型 'inherit class' 的引用初始化无效

c++ - 如何从 C-Header 导入 "disable"宏

c++ - 如何在 C++ 中将不可复制的局部变量移出 lambda?

c# - .NET 中的 JIT 编译器和事件处理程序

c++ - const auto 和 auto const 如何应用于指针?

c++ - QT:私有(private)成员而不是继承?是什么原因?这是一个具体的概念吗?

C++双指针数组到 float 的转换

c++11 - 使用捆绑属性 boost 图形库

java - 将 JRE 与我的 JAR 封装在一个 EXE 文件中

maven - 如何编译在运行时生成的java文件