Reputation: 21
I'm trying to come up with a way to define dynamic mixins at compile time. I currently have a very hacky solution that only partially does what I want, but I'm not sure how to improve it.
I'm aware of some more C++ like solutions using typelist's, but they require all of the types to be defined statically which I'm trying to avoid.
This is mostly just a thought exercise to learn C++ better, and I'm sure that my current implementation is not very good C++. Any suggestions for improvements or different ideas to try would be welcome.
The main problems with my current implementation are:
PrintOnce
to be able to have a method which returns a PrintTwice<PrintOnce<Printer<int > > >
object.make_mixed
function could be defined. Currently any mixin defined after the make_mixed
function is ignored. I apologize for the length, but this is the most simplified example I could come up with.
Thanks for any help.
#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();
}
Upvotes: 2
Views: 811
Reputation: 360
Here's a cleaner solution without macros:
#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 ();
}
Unfortunately, it still doesn't accomplish your requirement of having each mixin class being aware of all other mixin classes. I'm not sure if that's even possible at compile-time, though.
Upvotes: 1