我正试图想出一种在编译时定义动态混合的方法。我目前有一个非常hacky的解决方案,只能部分地做我想要的,但我不知道如何改进它。
我知道更多使用类型列表的C ++解决方案,但它们需要静态定义所有类型,我正在努力避免。
这主要是为了更好地学习C ++,我确信我目前的实现不是很好的C ++。任何改进建议或尝试不同的想法都会受到欢迎。
我目前实施的主要问题是:
PrintOnce
能够拥有一个返回PrintTwice<PrintOnce<Printer<int > > >
对象的方法。make_mixed
函数。目前忽略make_mixed
函数后定义的任何mixin。 我为长度道歉,但这是我能想到的最简单的例子。
感谢您的帮助。
#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();
}
答案 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 ©) :
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类的要求。我不确定在编译时是否可以这样做。