Boost logo

Boost Users :

Subject: Re: [Boost-users] Needs advices on design ( mpl or processor )
From: Alex Perry (Alex.Perry_at_[hidden])
Date: 2012-01-07 18:27:57


On Sat 07/01/2012 08:42 Allan Nielsen <a_at_[hidden]> wrote:-
>I will try to illustrate my design problem in a more simple example:
>
>struct TagA {
> enum Size { size = 4 };
> enum E { A0 = 0, A1 = 1, A2 = 2, A3 = 3, }; };
> ...

I've been following this thread despite being a total novice with mpl (and template meta-programming in general). However I'm not quite sure why you are trying to pass the offset through the templates in the 1st place - can't you just use standard : syntax and let the compiler work out the offsets?

Eg

enum A { A0,A1,A2,A3,A4 };
enum B { B0,B1,B2,B3,B4 };

template <typename type1, int t1_numbits, typename type2, int t2_numbits>
struct packed
{
    type1 first : t1_numbits;
    type2 second : t2_numbits;
};

Obviously accessing members via a name like first is far from ideal and specifying the size in the template would make this really unusable.

However at this point my lack of knowledge of mpl kicks in - I'm sure there is some clever way of coding this up so that packed<A,B,C> is defined like mpl::vector<> which could provide a clean way of accessing the members.

My total naive first attempt using partial specialisations on member functions sort of works under MSVC but fails totally on gcc - not sure if this is because this is a MS extension or whether its C++11 and so would work on a newer gcc - it works with VS2010 and VS2008 (I don't think there are any C++0x extensions in 2008) on windows 7 and fails to compile with gcc 4.1.2 on Linux. Linux errors start with :-
test.cpp:53: error: explicit specialization in non-namespace scope 'struct packed<type1, type2, type3, type4>'
test.cpp:53: error: enclosing class templates are not explicitly specialized

It's also relying on the (probably nearly always true) assumption that the enum types packed together would always be unique so that using the type of the enum can find the unique member - this assumption should at least have some sort of compile assertion on it (mpl again I guess).

It seems like an awful lot of boiler plate code would have to live behind the scenes to make this anything like useable though so not sure its worth exploring as a possible method but just in case it helps :-

#include <iostream>

//An enum we want to be able to compress
enum A{A0,A1,A2,A3,A4};
//The bit size information for enum as per previous example code (NB this is actually wrong 3 is big enough to hold 0-7 but heck ...)
struct tagA
{
    typedef enum A enum_type;
    enum { size=4 };
};

//Wrapper for above tag info just to save typeing having shown it done manually above
template <typename enumType,int size_ = 4> struct tag
{
    typedef enumType enum_type;
    enum { size=size_ };
};

//Another couple of enums and tag structures for bit sizes
enum B{B0,B1,B2,B3,B4};
typedef tag<B,4> tagB;

enum C{ C0,C1,C2,C3,C4 };
typedef tag<C,3> tagC; //Just to check that size is actually doing something

enum D{ D0,D1,D2,D3,D4 };
typedef tag<D,4> tagD;

//Our packed structure
template <typename type1,typename type2, typename type3, typename type4 >
struct packed
{
    //Just for clarity
    typedef typename type1::enum_type first_type;
    typedef typename type2::enum_type second_type;
    typedef typename type3::enum_type third_type;
    typedef typename type4::enum_type fourth_type;
    typedef packed<type1,type2,type3,type4> thistype;

    //Our packed enums using the specified bit sizes
    first_type first : type1::size;
    second_type second : type2::size;
    third_type third : type3::size;
    fourth_type fourth : type4::size;

    //All following is just to provide a simple get/set syntax rather than accessing via member names
    struct fail {}; //Only used for general case - fail will not convert to T hence compile error when used
    template <typename T> T get() const
    {
        return fail;
    };
    //Specialisations to provide access to the (unique) members based on their types
    template<> first_type get<first_type>() const { return first;}
    template<> second_type get<second_type>() const { return second;}
    template<> third_type get<third_type>() const { return third;}
    template<> fourth_type get<fourth_type>() const { return fourth;}
    //May as well define == using the get() - note extra specialisation for whole structure equality
    template <typename T> bool operator == (T const & other ) const
    {
        return get<T>() == other;
    }
    template <> bool operator ==<thistype>(thistype const & other ) const
    {
        return other==get<type1::enum_type>() &&
               other==get<type2::enum_type>() &&
               other==get<type3::enum_type>() &&
               other==get<type4::enum_type>();
    }

    template <typename T> void set(T val)
    {
        return fail;
    };
    //Specialisations to provide access to the (unique) members based on their types
    template<> void set<first_type>(first_type val) { first = val;}
    template<> void set<second_type>(second_type val) { second = val;}
    template<> void set<third_type>(third_type val) { third = val;}
    template<> void set<fourth_type>(fourth_type val) { fourth = val;}
    //May as well define operator = for convenient syntax l
    template <typename T> T operator =(T val)
    {
        set<T>(val);
        return val;
    };
};

//Oh yuk but it would be nice to pass the actual enum names rather than the tag structure name into our template
//I couldn't think of any easy way other than a macro but surely there is some neater solution.
#define make_packed_type(A,B,C,D) packed<tag##A,tag##B,tag##C,tag##D>

int main( int , char** )
{
    packed<tagA,tagB,tagC,tagD> test;
    make_packed_type(A,B,C,D) test2; //Try the macro version

    test.first = A3; //Using the specific member name
    test.set<B>( B4 ); //Using template specialisation of member function
    test.set( C1 ); //Same but using ADL
    test = D3; //ADL on operator =

    test2 = A3;
    test2 = B4;
    test2 = C1;
    test2 = D3; //Hmm having 2nd thoughts on this syntax - it's probably too confusing and unexpected to allow

    std::cout << "In test A is " << test.first << " B is " << test.get<B>() << " C is " << test.get<C>() << " D is " << test.get<D>() << std::endl;
    std::cout << "Sizeof of test is " << sizeof( test ) << std::endl;

    std::cout << "Piecewise equality check is"
              << " ==A3 " << ((test == A3)?"true":"false")
              << " ==B4 " << ((test == B4)?"true":"false")
              << " ==C1 " << ((test == C1)?"true":"false")
              << " ==D3 " << ((test == D3)?"true":"false")
              << std::endl;
    std::cout << "Overall equality check test == test2 is "
              << (( test == test2 )?"true":"false")
              << std::endl;

    return 0;
}

Which returns (on windows)

In test A is 3 B is 4 C is 1 D is 3
Sizeof of test is 4
Piecewise equality check is ==A3 true ==B4 true ==C1 true ==D3 true
Overall equality check test == test2 is true

As I said before don't know if this is of any use whatsoever to you (but it allowed me to avoid doing some real work while playing!) - As shown in the output the size of the structure is 4 bytes so is packing the 4 enums down into 1 int (without the : syntax used the sizeof this structure is 16 bytes)

Alex


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net