1

拥有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();

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

此外,如果使用外部生成器可以实现这样的事情,那对我来说没问题。

4

1 回答 1

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;
}
于 2012-12-09T21:34:01.780 回答