Boost logo

Boost Users :

Subject: [Boost-users] Needs advices on design ( mpl or processor )
From: Allan Nielsen (a_at_[hidden])
Date: 2012-01-06 11:19:57


Hi

This is not a specific questions directly related to Boost::mpl, but a request
for help if somebody has the time...

For the last couple of month I have been trying to learn to use the boost::mpl
library.

To learn to use the library I decided to use it for creating a
"compressed-enum" library. The library should make it possible to store several
enums in a single variable (for instance an unsigned int)

Here is a simple example to give en idea:

// example enums to play with
struct TagA {
    enum Size { size = 4 };
    enum Max { max = 3 };
    enum E { A0 = 0, A1 = 1, A2 = 2, A3 = 3, };
};
struct TagB {
    enum Size { size = 4 };
    enum Max { max = 3 };
    enum E { B0 = 0, B1 = 1, B2 = 2, B3 = 3, };
};
struct TagC {
    enum Size { size = 4 };
    enum Max { max = 3 };
    enum E { C0 = 0, C1 = 1, C2 = 2, C3 = 3, };
};

struct First{};
struct Second{};

int main() {
    // using the compressed enums library
    typedef unsigned short STORAGE;
    typedef CompressedEnums< STORAGE,
                             CompressedEnum<STORAGE, 1, TagA>,
                             CompressedEnum<STORAGE, 4, TagB, First>,
                             CompressedEnum<STORAGE, 16, TagB, Second>,
                             CompressedEnumArray<STORAGE, 64, TagC, 5>
> ENUMS;

    assert(sizeof(ENUMS) == 2));
    ENUMS enums;
    enums.set<TagA>(TagA::A2);
    enums.set<Second>(TagB::B2);
    enums.set<3, TagC>(TagC::C2);

    cout << enums.get<TagA>() << " " <<
            enums.get<Second>(TagB::B2) << " " <<
            enums.get<3, TagC>(TagC::C2) << endl;
}

CompressedEnums contains the storage variable, and inherit from all its
template arguments, and make their methods available through tags.
CompressedEnum and CompressedEnumArray only contains static methods which can
find the enum in the storage variable.

Here is a snippet of my implementation:

template < typename STORAGE_TYPE,
           int OFFSET,
           typename ENUM_CONTAINER,
           typename TAG = typename ENUM_CONTAINER::E
>
struct CompressedEnum
{
    typedef TAG ACCESS_TAG;
    typedef typename ENUM_CONTAINER::E INNER_TYPE;

    enum Offset { offset = OFFSET };
    enum Size { size = ENUM_CONTAINER::size };

    static INNER_TYPE get(const STORAGE_TYPE& data) {
        return (INNER_TYPE)((data / OFFSET) % size);
    }

----%<----%<----%<----%<----%<----%<----%<----%<--
}

template < typename STORAGE_TYPE,
           typename B00 = Dummy< 0>,
           typename B01 = Dummy< 1>,
           typename B02 = Dummy< 2>,
           typename B03 = Dummy< 3>
>
struct CompressedEnums : public B00, B01, B02, B03
{
    typedef boost::mpl::map<boost::mpl::pair<typename B00::ACCESS_TAG, B00>,
                            boost::mpl::pair<typename B01::ACCESS_TAG, B01>,
                            boost::mpl::pair<typename B02::ACCESS_TAG, B02>,
                            boost::mpl::pair<typename B03::ACCESS_TAG,
B03> > TYPE_MAP;

    template<typename TAG>
    typename boost::mpl::at<TYPE_MAP, TAG>::type::INNER_TYPE
    get() const {
        return boost::mpl::at<TYPE_MAP, TAG>::type::get(data);
    }

----%<----%<----%<----%<----%<----%<----%<----%<--
    STORAGE_TYPE data;
};

The code works and does what it is suppose to (as far as I know) but I'm not
very happy with the interface:

typedef CompressedEnums< STORAGE,

                         // 1 because it is the first
                         CompressedEnum<STORAGE, 1, TagA>,

                         // 4 because TagA::size == 4
                         CompressedEnum<STORAGE, 4, TagB, First>,

                         // 16 because TagA::size * TagB::size == 16
                         CompressedEnum<STORAGE, 16, TagB, Second>,

                         // 64 because TagA::size * TagB::size *
TagB::size == 64
                         CompressedEnumArray<STORAGE, 64, TagC, 5>

> ENUMS;

These magic numbers makes it very easy to make bugs when the code is
restructured or changed, and it should be possible to calculate them
automatic. If the magic numbers are replaced with their expresions the
code will still break of the arguemnts are reordered.

To accomplice this I have considered two solutions: macro or more templating...

Using macroes I imagin the interface could look something like this:

typedef CompressedEnums< STORAGE,
                         ENUM_PACK( STORAGE, 1,
                                    (CompressedEnum, TagA)
                                    (CompressedEnum, TagB, First)
                                    (CompressedEnum, TagB, Second)
                                    (CompressedEnumArray, TagC, 5) ) > ENUMS;
Which then would expand to the code manual entered above.

Alternative using template I imagin that CompressedEnums could change
CompressedEnum<STORAGE, 0, TagB, Second> ->
CompressedEnum<STORAGE, 16, TagB, Second>
automatic and then derive from that one automatic.

I tried both, but non of my idea worked out very well, which is why I
hope that one of you c++ experts could give me some hints.

Best regards
Allan W. Nielsen


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