EnumBase.h

// EnumBase.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

#include "Ceda/cxUtils/BasicTypes.h"
#include "Ceda/cxUtils/CedaAssert.h"
#include "Ceda/cxUtils/xchar.h"

namespace ceda
{
    class xostream;

    /*
    Helps to allow xcpp to generate a version of an enum using a class

        -   allowing the labels to be inside the enum type, rather than polluting the containing 
            namespace.
        
        -   supporting forwards and reverse iterators
    
        -   allowing for validation when converted from an int value.
    
        -   providing conversion to a string representation, and writing to an ostream
    
        -   serialisation to an archive as an int32.

    For example

        $enum+ Colour
        {
            R, G, B, M, C, Y 
        };

    expands into

        class Colour : public ceda::EnumBase<Colour>
        {
        public:
            enum Enum
            {
                R,G,B,M,C,Y
            };
            Colour(Enum v) : EnumBase(v) {}
            explicit Colour(int v = 0) : EnumBase(v) {}
            enum { LEN = 6 };
            static ConstStringZ const* strings()
            {
                static ConstStringZ s[] = {"R","G","B","M","C","Y"};
                return s;
            }
        };
    */

    template <typename E>
    class EnumBase
    {
    protected:
        explicit EnumBase(int v) 
        {
            cxAssert(E::IsValidValue(v));
            value = v;
        }

    public:
        operator int() const { return value; }

        // Index values are always in the range [0,LEN), even when the enum values are not 0,1,2,...
        static bool IsValidIndex(int i) { return 0 <= i && i <= E::LEN-1; }

        class iterator
        {
        public:
            iterator(typename E::Enum e) : index(E::ValueToIndex(e)) {}
            iterator(E v) : index(E::ValueToIndex(v.value)) {}
            explicit iterator(int i) : index(i) {}

            ConstStringZ c_str() const { return E::stringArray()[index]; }
            int Index() const { return index; }
            E operator*() const { return E(E::IndexToValue(index)); }

            iterator& operator++() { ++index; return *this; }
            const iterator operator++(int) { iterator it = *this; ++(*this); return it; }

            iterator& operator--() { --index; return *this; }
            const iterator operator--(int) { iterator it = *this; --(*this); return it; }

            bool operator==(const iterator& rhs) const { return index == rhs.index; }
            bool operator!=(const iterator& rhs) const { return index != rhs.index; }

        private:
            int32 index;
        };
        static iterator begin() { return iterator(0); }
        static iterator end() { return iterator(E::LEN); }

        class reverse_iterator
        {
        public:
            reverse_iterator(typename E::Enum e) : index(E::ValueToIndex(e)) {}
            reverse_iterator(E v) : index(E::ValueToIndex(v.value)) {}
            explicit reverse_iterator(int i) : index(i) {}
        
            ConstStringZ c_str() const { return E::stringArray()[index]; }
            int Index() const { return index; }
            E operator*() const { return E(E::IndexToValue(index)); }

            reverse_iterator& operator++() { --index; return *this; }
            const reverse_iterator operator++(int) { reverse_iterator it = *this; ++(*this); return it; }

            reverse_iterator& operator--() { ++index; return *this; }
            const reverse_iterator operator--(int) { reverse_iterator it = *this; --(*this); return it; }

            bool operator==(const reverse_iterator& rhs) const { return index == rhs.index; }
            bool operator!=(const reverse_iterator& rhs) const { return index != rhs.index; }

        private:
            int32 index;
        };
        static reverse_iterator rbegin() { return reverse_iterator(E::LEN-1); }
        static reverse_iterator rend() { return reverse_iterator(-1); }

        ConstStringZ c_str() const
        {
            return E::stringArray()[E::ValueToIndex(value)];
        }

        ////////////// By default we assume value == index.  Derived classes must override these where required

        static int IndexToValue(int i) { return i; }
        static int ValueToIndex(int v) 
        { 
            cxAssert(0 <= v && v < E::LEN);
            return v; 
        }
        static bool IsValidValue(int v) { return 0 <= v && v < E::LEN; }

        ////////////// 
    
        int32 value;
    };

    template <typename E>
    inline xostream& operator<<(xostream& os,EnumBase<E> x) { os << x.c_str(); return os; }

    template <typename Archive, typename E> 
    inline void Serialise(Archive& ar, EnumBase<E> x)
    {
        ar << x.value;
    }

    template <typename Archive, typename E> 
    inline void Deserialise(Archive& ar, EnumBase<E>& x)
    {
        ar >> x.value;
    }

    // To be used when E defines the valueArray() - a mapping from index to value
    template <typename E>
    class EnumBaseForRefinedLabels : public EnumBase<E>
    {
    protected:
        explicit EnumBaseForRefinedLabels(int v) : EnumBase<E>(v) {}

    public:
        static int IndexToValue(int i) 
        { 
            cxAssert(EnumBase<E>::IsValidIndex(i));
            return E::valueArray()[i]; 
        }
        static int ValueToIndex(int v) 
        { 
            for (int i=0 ; i < E::LEN ; ++i)
            {
                if (E::valueArray()[i] == v) return i;
            }
            cxAssert(0);
            return -1;
        }
        static bool IsValidValue(int v) 
        { 
            for (int i=0 ; i < E::LEN ; ++i)
            {
                if (E::valueArray()[i] == v) return true;
            }
            return false;
        }
    };
} // namespace ceda