Enum.cpp

// Enum.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2009

#include "Ceda/cxUtils/Tracer.h"
#include "Ceda/cxUtils/Archive.h"
@import "ExampleUtils.h"

void TraceInfoAboutEnum(const ceda::ReflectedEnum& re)
{
    Tracer() << "GetName() = " << re.GetName() << '\n'
             << "ValuesRedefined() = " << re.ValuesRedefined() << '\n'
             << "GetCount() = " << re.GetCount() << '\n';
    for (int i=0 ; i < re.GetCount() ; ++i)
    {
        Tracer() << "GetValue(" << i << ") = " << re.GetValue(i) << ' '
                 << "GetEnable(" << i << ") = " << re.GetEnable(i) << ' '
                 << "GetString(" << i << ") = " << re.GetString(i) << '\n';
    }         
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
A C++ enumerated type can be reflected using $enum

Labels can be marked as deprecated by preceding with '-'.
*/
namespace Enum1
{
    // enums can be forward declared.  
    // The forward declaration must not use '+'
    // The declaration must use 'class' if and only if the definition uses 'class'
    $enum Colour;

    $enum+ Colour
    {
        COL_R,
        COL_G,
        COL_B,
    };

    $enum+ Status
    {
        warn,
        -failed,            // '-' means deprecated
        error,
        success,
    };

    $enum+ Chip
    {
        red,
        green,
        blue,
        black = 10,
        white 
    };

    void Run()
    {
        ceda::TraceGroup g("Enum example 1");
        
        int x = COL_R;
        int y = COL_R + 10;
        Colour c = COL_G;

        // Note that deprecated labels are still available!
        Status s = failed;

        const ceda::ReflectedEnum& reColour = ceda::FindReflectedEnum("Enum1::Colour");
        TraceInfoAboutEnum(reColour);
        Tracer() << '\n';
        
        const ceda::ReflectedEnum& reStatus = ceda::FindReflectedEnum("Enum1::Status");
        TraceInfoAboutEnum(reStatus);
        Tracer() << '\n';

        const ceda::ReflectedEnum& reChip = ceda::FindReflectedEnum("Enum1::Chip");
        TraceInfoAboutEnum(reChip);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
A $enum class definition causes xcpp to generate a class that represents the enum. 
*/
namespace Enum2
{
    // Forward declaration.  Note that it uses 'class' consistently with the definition
    $enum class Bool;
    
    $enum+ class Bool
    {
        False,
        True
    };
        
    $enum+ class Colour  // 'class' causes a class for the enum to be generated by xcpp
    {
        R,
        default G,  // Default used for the default constructor
        B,
        -M,         // '-' indicates that this label is flagged as deprecated
        C,
        Y,
    };
    
    $enum+ class Strange
    {
        x,
        default -y      // Making a deprecated label the default is supported!
    };

    void f1(int) {}
    void f2(bool) {}
    void f3(Colour c = Colour::G) {}
    void f4(Colour) {} 

    void Run()
    {
        ceda::TraceGroup g("Enum example 2");

        // FindReflectedEnum() looks for the reflected enum in a global registry
        TraceInfoAboutEnum(ceda::FindReflectedEnum("Enum2::Bool"));
        Tracer() << '\n';

        // GetReflectedEnum<T>() gets the ReflectedEnum of type T.
        TraceInfoAboutEnum(ceda::GetReflectedEnum<Colour>());
        Tracer() << '\n';

        TraceInfoAboutEnum(ceda::GetReflectedEnum<Strange>());
        Tracer() << '\n';
        
        /*
        The default value is normally the first value that appears in the enum.
        */
        Bool b;
        cxAssert(b == Bool::False);

        // Colour::LEN is a compile time const that for example can be used to declare the size of 
        // an array
        int A[Colour::LEN]; A[0] = 10;
        
        Colour c(Colour::G);  // construction from enum value
        c = Colour::B;        // assignment from enum value
        f4(Colour::R);
        
        // error C2679: binary '=' : no operator found which takes a right-hand operand of type 'int' 
        // (or there is no acceptable conversion)
        // c = 1;

        // error C2676: binary '++' : 'Colour' does not define this operator or a conversion to a type 
        // acceptable to the predefined operator
        // c++;

        // warning C4800: 'Colour::Enum' : forcing value to bool 'true' or 'false' (performance warning)
        // bool b = c;
        
        // error C2440: 'initializing' : cannot convert from 'Colour' to 'const char *'
        // const char* str = c;

        /*
        We follow the normal convention of allowing implicit conversion to int.  This could be regarded
        as risky,  but would set too big a precedent to disallow it.
        */
        int x = c;             // Implicit conversion to int
        int y = c+1;
        int z = c * 10;
        f1(c);

        // All possible comparisons between colours and ints
        f2(10 == c); f2(10 != c);     
        f2(10 < c);  f2(10 <= c);     
        f2(10 > c);  f2(10 >= c);
        f2(c == 10); f2(c != 10);
        f2(c < 10);  f2(c <= 10);
        f2(c > 10);  f2(c >= 10);

        // All possible comparisons between colours and enum values
        f2(Colour::R == c); f2(Colour::R != c);     
        f2(Colour::R < c);  f2(Colour::R <= c);     
        f2(Colour::R > c);  f2(Colour::R >= c);
        f2(c == Colour::R); f2(c != Colour::R);
        f2(c < Colour::R);  f2(c <= Colour::R);
        f2(c > Colour::R);  f2(c >= Colour::R);

        Colour c1;              // Default ctor
        cxAssert(c1 == Colour::G);
        Colour c2(c1);          // Copy ctor
        c2 = c1;                // Assignment
        
        // All possible comparisons between colours
        f2(c1 == c2); f2(c1 != c2); 
        f2(c1 < c2);  f2(c1 <= c2); 
        f2(c1 > c2);  f2(c1 >= c2);

        // error C2440: 'initializing' : cannot convert from 'int' to 'Colour'
        // Colour c3 = 1;

        Colour::Enum e1;        // Cannot predict value of e1!
        e1 = Colour::R;

        // error C2440: 'initializing' : cannot convert from 'int' to 'Colour::Enum'
        // Colour::Enum e2 = 1;

        ceda::TracerX os;
        
        // Forwards iteration
        for (Colour::iterator i = Colour::begin() ; i != Colour::end() ; ++i)
        {
            os << *i << ' ';
        }
        os << '\n';

        for (Colour::iterator i = Colour::G ; i != Colour::end() ; ++i)
        {
            os << *i << ' ';
        }
        os << '\n';
        
        // Reverse iteration
        for (Colour::reverse_iterator i = Colour::rbegin() ; i != Colour::rend() ; ++i)
        {
            os << *i << ' ';
        }
        os << '\n';
        
        /*
        if (false)
        {
            ceda::Archive ar;
            ar << c;
        }    
        */

        if (false)
        {
            ceda::InputArchive ar(0);
            ar >> c;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
A $enum class definition causes xcpp to generate a class that represents the enum. 
*/
namespace Enum3
{
    $enum+ class Colour  // 'class' causes a class for the enum to be generated by xcpp
    {
        R,
        default G,  // Default used for the default constructor
        B,
        -M,         // '-' indicates that this label is flagged as deprecated
        C = 10,     // Causes a mismatch between 'index' and 'value'
        Y,          
    };
    
    void f1(int x) {}
    void f2(bool x) {}

    void Run()
    {
        ceda::TraceGroup g("Enum example 3");

        TraceInfoAboutEnum(ceda::FindReflectedEnum("Enum2::Colour"));
        Tracer() << '\n';
        
        ceda::TracerX os;
        
        // Prints 'C = 10'
        // Note that Colour(Colour::C).c_str() involves a scan for the index that maps to value Colour::C = 10
        Tracer() << Colour(Colour::C) << " = " << Colour::C << '\n';
        
        // This initialisation actually involves a scan for the index that maps to value Colour::C = 10
        Colour::iterator j = Colour::C;
        os << "j = " << *j << '\n';

        // This initialisation actually involves a scan for the index that maps to value Colour::C = 10
        Colour::iterator k = Colour(Colour::C);
        os << "k = " << *k << '\n';
        
        // Forwards iteration
        os << '\n';
        for (Colour::iterator i = Colour::begin() ; i != Colour::end() ; ++i)
        {
            os << i.c_str() << ' ';     // Fast - iterator directly indexes into array of strings
            os << *i << ' ';            // Slow - to display string the index is mapped to a value by linear search!
            os << (int)(*i) << '\n';    // Provides access to the value as an int
        }
        os << '\n';
        
        // Reverse iteration
        for (Colour::reverse_iterator i = Colour::rbegin() ; i != Colour::rend() ; ++i)
        {
            os << i.c_str() << ' ';
            os << *i << ' ';
            os << (int) (*i) << '\n';
        }
        os << '\n';
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Enum
{
    void Run()
    {
        Enum1::Run();
        Enum2::Run();
        Enum3::Run();
    }
}