Pregunta

Having N difrent classes that have no public data fields, only methods (that do not overlap), how to create unifiing them all proxy class via boost preprocessor?

For example we had classes: A that had method do(); and class B had method data();. I wonder if there is a way (using Boost Preprocessor for example) to create a proxy class that would have all methods from A and B (here do() data()) and a constructor taking in that pointers to that classes instances - one for A and one for B?

So we would get api like such pseudocode:

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

Is it possible to create such thing in C++11 using boost::preprovcessor or may be such thing is already in boost?

Also if such thing is possible using external generator it'll be ok for me.

¿Fue útil?

Solución

If you don't mind listing all methods in A and B, we could do it with SFINAE. The essense here is that we define two methods C::data() which forward to each of A::data() and B::data(). The compiler will filter out the one which cannot be compiled, thus we could forward it to the correct member.

#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) \
    }

For example:

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

The idea could be generalized to more than 2 classes:

#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) \
    }

Usage:

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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top