动态编译时混合

时间:2011-03-23 20:18:11

标签: c++ c++11 mixins

我正试图想出一种在编译时定义动态混合的方法。我目前有一个非常hacky的解决方案,只能部分地做我想要的,但我不知道如何改进它。

我知道更多使用类型列表的C ++解决方案,但它们需要静态定义所有类型,我正在努力避免。

这主要是为了更好地学习C ++,我确信我目前的实现不是很好的C ++。任何改进建议或尝试不同的想法都会受到欢迎。

我目前实施的主要问题是:

  • 每个mixin类只知道自身,并且类在层次结构中低于自身。我希望每个mixin类能够返回一个具有不同底层类型的新mixin。在下面的示例代码中,我希望PrintOnce能够拥有一个返回PrintTwice<PrintOnce<Printer<int > > >对象的方法。
  • 这个想法的任何使用都需要包含头部顺序。开头的所有锅炉板代码都需要在一个头中,然后需要定义所有的mixin类,最后可以定义make_mixed函数。目前忽略make_mixed函数后定义的任何mixin。
  • 实施的宏和一般的hackyness。

我为长度道歉,但这是我能想到的最简单的例子。

感谢您的帮助。

#include <string>
#include <vector>
#include <iostream>

using namespace std;

template<class Underlying>
struct Printer
{
    Printer(const Underlying &val) : val_(val) {}

    Underlying get_val() { return val_; }

  private:
    Underlying val_;
};

#define CURRENT_NUMBER_MIXED_IN_CLASSES() \
    MixinCount<0, __LINE__>::value

#define INCREMENT_MIXIN_CLASS_COUNTER() \
    template<int id>                                               \
    struct MixinClassCounter< CURRENT_NUMBER_MIXED_IN_CLASSES(), id>    \
    {                                                                   \
        static const bool is_defined = true;                            \
    }

template< bool b, typename i, typename j >
struct select_value;

template<class i, class j>
struct select_value<true, i, j>
{
    static const int value = i::value;
};

template<class i, class j>
struct select_value<false, i, j>
{
    static const int value = j::value;
};

template<int i>
struct IntToVal
{
    static const int value = i;
};

namespace
{
    template<int count, int id>
    struct MixinClassCounter
    {
        static const bool is_defined = false;
    };

    template<int count, int id>
    struct MixinCount
    {
        static const int value = select_value<MixinClassCounter<count, id>::is_defined,
                                              MixinCount<count + 1, id>,
                                              IntToVal<count> >::value;
    };

    template<class Underlying, int i>
    struct MixinBuilder {};

    template<class Underlying>
    struct MixinBuilder<Underlying, 0>
    {
        typedef Printer<Underlying> type;
    };
    INCREMENT_MIXIN_CLASS_COUNTER();
}

#define DECLARE_MIXIN_BEGIN(name) \
    template<class Base> \
    struct name : Base   \
    {                    \
        template<class Underlying>                  \
        name(const Underlying &val) : Base(val) {}

#define DECLARE_MIXIN_END(name)                 \
    };                                          \
    namespace                                   \
    {                                               \
        template<class Underlying>                                         \
        struct MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES()> \
        {                                                                   \
            typedef name< typename MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type > type; \
        };                                                                  \
        INCREMENT_MIXIN_CLASS_COUNTER();                                    \
    }                                                                       \


DECLARE_MIXIN_BEGIN(PrintOnce)
void print_once()
{
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintOnce)

DECLARE_MIXIN_BEGIN(PrintTwice)
void print_twice()
{
    cout << Base::get_val() << endl;
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintTwice)

template<class T>
typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type make_mixed(const T &val)
{
    return typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type(val);
}

int main()
{
    string test("this is a test");
    auto printable_string = make_mixed(test);

    printable_string.print_once();
    printable_string.print_twice();
}

1 个答案:

答案 0 :(得分:1)

这是一个没有宏的更清洁的解决方案:

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

template <typename T>
struct RefWrapper
{
    T *self;
    RefWrapper () : self (nullptr) {abort ();} // should never be called
    RefWrapper (T &self) : self (&self) {}
};


template <typename T>
struct PrintOnce : virtual RefWrapper<T>
{
    PrintOnce () {}             // workaround gcc 4.6 bug
    void print_once () {cout << *RefWrapper<T>::self << endl;}
};


template <typename T>
struct PrintTwice : virtual RefWrapper<T>
{
    PrintTwice () {}            // workaround gcc 4.6 bug
    void print_twice ()
    {
        cout << *RefWrapper<T>::self << endl;
        cout << *RefWrapper<T>::self << endl;
    }
};


template <typename T, typename... Args>
struct Mixed : Args...
{
    Mixed (T &self) :
        RefWrapper<T> (self),
        Args ()... {}

    Mixed (const Mixed &copy) :
        RefWrapper<T> (*copy.self),
        Args ()... {}
};


template <template <typename U> class Mixin, typename T>
Mixed<T, Mixin<T> > add_mixin (T &original)
{
    return Mixed<T, Mixin<T> > (original);
}


template <template <typename U> class Mixin, typename T,
          typename... OtherMixins>
Mixed<T, OtherMixins..., Mixin<T> >
add_mixin (Mixed<T, OtherMixins...> &original)
{
    return Mixed<T, OtherMixins..., Mixin<T> > (*original.self);
}


int main ()
{
    string foo = "test";

    auto p1 = add_mixin<PrintOnce> (foo);
    p1.print_once ();

    auto p2 = add_mixin<PrintTwice> (p1);
    p2.print_once ();
    p2.print_twice ();
}

不幸的是,它仍然没有达到让每个mixin类都知道所有其他mixin类的要求。我不确定在编译时是否可以这样做。