【问题标题】:How to create proxy class?如何创建代理类?
【发布时间】:2012-12-09 19:47:49
【问题描述】:

拥有N 不同的类,它们没有公共数据字段,只有方法(不重叠),如何通过 boost 预处理器创建统一它们的代理类?

例如,我们有类:A 有方法 do();,类 B 有方法 data();。我想知道是否有一种方法(例如使用 Boost Preprocessor)来创建一个代理类,该类将具有来自 A 和 B 的所有方法(此处为 do()data())以及一个接收指向该类实例的指针的构造函数 - 一个A 一个,B 一个?

所以我们会得到类似这样的伪代码的api:

JOIN(A, B, C);// or if needed JOIN("path_to_A.h", "path_to_B.h", C)
//...
A * a = new A();
B * b = new B();
C * c = new C(a, b);
c->data();
c->do();

是否可以在 C++11 中使用 boost::preproccessor 创建这样的东西,或者这样的东西可能已经在 boost 中了?

另外,如果使用外部生成器可以做到这一点,那对我来说没问题。

【问题讨论】:

  • 我怀疑您将需要使用解析和代码生成以自动方式完成此任务,但如果这实际上可以使用 C 预处理器,我对答案很感兴趣。

标签: c++ boost c++11 boost-preprocessor


【解决方案1】:

如果您不介意列出 A 和 B 中的所有方法,我们可以使用 SFINAE 来完成。这里的本质是我们定义了两个方法C::data(),分别转发给A::data()B::data()。编译器会过滤掉那些不能编译的,这样我们就可以把它转发给正确的成员。

#include <type_traits>
#include <boost/preprocessor/seq/for_each.hpp>

#define CALLER_NAME(method_name) BOOST_PP_CAT(BOOST_PP_CAT(_, method_name), _caller__)

#define GEN_CALLER(r, ignored, method_name) \
    template <typename K, typename... T> \
    static auto CALLER_NAME(method_name)(K* k, T&&... args) -> decltype(k->method_name(std::forward<T>(args)...)) { \
        return k->method_name(std::forward<T>(args)...); \
    } \
    template <typename... T> \
    auto method_name(T&&... args) -> decltype(CALLER_NAME(method_name)(_first__, std::forward<T>(args)...)) { \
        return CALLER_NAME(method_name)(_first__, std::forward<T>(args)...); \
    } \
    template <typename... T> \
    auto method_name(T&&... args) -> decltype(CALLER_NAME(method_name)(_second__, std::forward<T>(args)...)) { \
        return CALLER_NAME(method_name)(_second__, std::forward<T>(args)...); \
    }

#define JOIN(FIRST, SECOND, NAME, METHODS) \
    struct C { \
        FIRST* _first__; \
        SECOND* _second__; \
        NAME(FIRST* _first__, SECOND* _second__) : _first__(_first__), _second__(_second__) {} \
        BOOST_PP_SEQ_FOR_EACH(GEN_CALLER, , METHODS) \
    }

例如:

struct A {
    int x;

    void a() {
        std::cout << "an a! " << x << "\n";
    }
};

struct B {
    double x;

    double b(double k) {
        std::cout << "b! " << x << ", " << k << "\n";
        return x - k;
    }

    void b() {
        std::cout << "b! " << x << ", ?\n";
    }
};

JOIN(A, B, C, (a)(b));

int main() {
    A a {12};
    B b {24};

    C c (&a, &b);

    c.a();
    c.b();
    std::cout << c.b(2445) << std::endl;
}

这个想法可以推广到两个以上的类:

#include <type_traits>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>

#define CALLER_NAME(method_name) \
    BOOST_PP_CAT(BOOST_PP_CAT(_caller_, method_name), __)
#define FIELD_NAME(ClassName) \
    BOOST_PP_CAT(BOOST_PP_CAT(_field_, ClassName), __)
#define INVOKER_IMPL(method_name, ClassName) \
    CALLER_NAME(method_name)(FIELD_NAME(ClassName), std::forward<T>(args)...)
#define CALLER_IMPL(method_name) \
    k->method_name(std::forward<T>(args)...)
#define FORWARD(IMPL) -> decltype(IMPL) { return IMPL; }

#define GEN_INVOKER(r, method_name, i, ClassName) \
    template <typename... T> \
    auto method_name(T&&... args) \
        FORWARD(INVOKER_IMPL(method_name, ClassName))

#define GEN_CALLER(r, ALL_CLASSES, method_name) \
private: \
    template <typename K, typename... T> \
    static auto CALLER_NAME(method_name)(K* k, T&&... args) \
        FORWARD(CALLER_IMPL(method_name)) \
public: \
    BOOST_PP_SEQ_FOR_EACH_I_R(r, GEN_INVOKER, method_name, ALL_CLASSES)

#define GEN_FIELD(r, IGNORED, ClassName) \
    ClassName* FIELD_NAME(ClassName);
#define GEN_ARG(r, IGNORED, i, ClassName) \
    BOOST_PP_COMMA_IF(i) ClassName* FIELD_NAME(ClassName)
#define GEN_CTOR(r, IGNORED, i, ClassName) \
    BOOST_PP_COMMA_IF(i) FIELD_NAME(ClassName)(FIELD_NAME(ClassName))

#define JOIN(ALL_CLASSES, ClassName, METHODS) \
    struct ClassName { \
    private: \
        BOOST_PP_SEQ_FOR_EACH(GEN_FIELD, , ALL_CLASSES) \
    public: \
        ClassName(BOOST_PP_SEQ_FOR_EACH_I(GEN_ARG, , ALL_CLASSES)) \
            : BOOST_PP_SEQ_FOR_EACH_I(GEN_CTOR, , ALL_CLASSES) {} \
        BOOST_PP_SEQ_FOR_EACH(GEN_CALLER, ALL_CLASSES, METHODS) \
    }

用法:

struct A {
    int x;

    void a() {
        std::cout << "an a! " << x << "\n";
    }
};

struct B {
    double x;

    double b(double k) {
        std::cout << "b! " << x << ", " << k << "\n";
        return x - k;
    }

    void c() {
        std::cout << "b! " << x << ", ?\n";
    }
};

struct C {
    double x;

    double c(double k) {
        std::cout << "c! " << x << ", " << k << "\n";
        return x + k;
    }

    void b() {
        std::cout << "c! " << x << ", ?\n";
    }
};


JOIN((A)(B)(C), D, (a)(b)(c));

int main() {
    A a {12};
    B b {24};
    C c {36};

    D d {&a, &b, &c};

    d.a();
    d.b();
    d.c();
    std::cout << d.b(48) << std::endl;
    std::cout << d.c(64) << std::endl;
}

【讨论】:

  • 顺便说一句,如果您能展示如何为N 类类型扩展该定义,那就太棒了!请 - 对于我们这些对预处理器不那么感兴趣的人=)
  • 有没有办法迭代类方法(不包括父类方法)?
  • 我的主要问题是我无法编译它 - ideone.com/AlDoKA 也无法在 VS2012 中编译(在 '...' 之前输出大量缺失的 ',')
  • @myWallJSON: (1) N 类型:已更新; (2)类方法(假设你的意思是静态方法):已经有效; (3)ideone上的编译器老旧。它适用于 gcc 4.7 和 clang 3.1,例如liveworkspace.org/code/KKkbu$0。不知道 VS 2012 上的问题,但他们对 C++11 esp 的支持。可变参数模板不是那么好。
猜你喜欢
  • 2013-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 2020-01-13
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
相关资源
最近更新 更多