Enumerated attribute
====================

:audience:`developers`, :lang:`c++`

Since Tango release 9, enumerated attribute is supported using the new
data type DevEnum. This data type is not a real C++ enumeration because:

#. The enumerated value allways start with 0

#. Values are consecutive

#. It is transferred on the network as DevShort data type

One enumeration label is associated to each enumeration value. For the
Tango kernel, it is this list of enumeration labels which will define
the possible enumeration values. For instance if the enumeration has 3
labels, its value must be between 0 and 2. There are two ways to define
the enumeration labels:

#. At attribute creation time. This is the most common case when the
   list of possible enumeration values and labels are known at compile
   time. The Tango code generator Pogo generates for you the code needed
   to pass the enumeration labels to the Tango kernel.

#. In the user code when the enumeration values and labels are not known
   at compile time but retrieved during device startup phase. The user
   gives the possible enumeration values to the Tango kernel using the
   Attribute class *set\_properties()* method.

A Tango client is able to retrieve the enumeration labels in the
attribute configuration returned by instance by a call to the
*DeviceProxy::get\_attribute\_config()* method. Using the
*DeviceProxy::set\_attribute\_config()* call, a user may change the
enumeration labels but not their number.

Usage in a Tango class
----------------------

Within a Tango class, you set the attribute value with a C++ enum or a
DevShort variable. In case a DevShort variable is used, its value will
be checked according to the enumeration labels list given to Tango
kernel.

Setting the labels with enumeration compile time knowledge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In such a case, the enumeration labels are given to Tango at the
attribute creation time in the *attribute\_factory* method of the
XXXClass class. Let us take one example

.. code:: cpp
  :number-lines:

    enum class Card: short
    {
        NORTH = 0,
        SOUTH,
        EAST,
        WEST
    };

    struct TheEnumAttrib : Tango::Attr
    {
        ...

        // Required for enum attributes
        virtual bool same_type(const type_info& in_type) override { return typeid(Card) == in_type; }
        virtual std::string get_enum_type() override { return "Card"; }
    }

    void XXXClass::attribute_factory(vector<Tango::Attr *> &att_list)
    {
        .....
        TheEnumAttrib	*theenum = new TheEnumAttrib();
        Tango::UserDefaultAttrProp theenum_prop;
        vector<string> labels = {"North","South","East","West"};
        theenum_prop.set_enum_labels(labels);
        theenum->set_default_properties(theenum_prop);
        att_list.push_back(theenum);
        .....
     }

line 1-7 : The definition of the enumeration (C++11 in this example)

line 9-16 : The definition of the enumerated attribute 

line 23 : A vector of strings with the enumeration labels is created.
Because there is no way to get the labels from the enumeration
definition, they are re-defined here.

line 24 : This vector is given to the theenum\_prop object which
contains the user default properties

| line 26 : The user default properties are associated to the attribute
| In most cases, all this code will be automatically generated by the
  Tango code generator Pogo. It is given here for completness.

Setting the labels without enumeration compile time knowledge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In such a case, the enumeration labels are retrieved by the user in a
way specific to the device and passed to Tango using the Attribute class
*set\_properties()* method. Let us take one example

.. code:: cpp
  :number-lines:

    void MyDev::init_device()
    {
        ...

        Attribute &att = get_device_attr()->get_attr_by_name("TheEnumAtt");
        MultiAttrProp<DevEnum> multi_prop;
        att.get_properties(multi_prop);

        multi_prop.enum_labels = {....};
        att.set_properties(multi_prop);
        ....
     }

line 5 : Get a reference to the attribute object

line 7 : Retrieve the attribute properties

line 9 : Initialise the attribute labels in the set of attribute
properties

line 10 : Set the attribute properties

Setting the attribute value
~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is possible to set the attribute value using either a classical
DevShort variable or using a variable of the C++ enumeration. The
following example is when you have compile time knowledge of the
enumeration definition. We assume that the enumeration is the same than
the one defined above (Card enumeration)

.. code:: cpp
  :number-lines:

    enum Card points;

    void MyDev::read_TheEnum(Attribute &att)
    {
        ...
        points = SOUTH;
        att.set_value(&points);
    }

line 1 : One instance of the Card enum is created (named points)

line 6 : The enumeration is initialized

| line 7 : The value of the attribute object is set using the
  enumeration (by pointer)
| To get the same result using a classical DevShort variable, the code
  looks like

.. code:: cpp
  :number-lines:

    DevShort sh;

    void MyDev::read_TheEnum(Attribute &att)
    {
        ...
        sh = 1;
        att.set_value(&sh);
    }

line 1 : A DevShort variable is created (named sh)

line 6 : The variable is initialized

line 7 : The value of the attribute object is set using the DevShort
variable (by pointer)

Usage in a Tango client
-----------------------

Within a Tango client, you insert/extract enumerated attribute value
in/from DeviceAttribute object with a C++ enum or a DevShort variable.
The later case is for generic client which do not have compile time
knowledge of the enumeration. The code looks like

.. code:: cpp
  :number-lines:

    DeviceAttribute da = the_dev.read_attribute("TheEnumAtt");
    Card ca;
    da >> ca;

    DeviceAttribute db = the_dev.read_attribute("TheEnumAtt");
    DevShort sh;
    da >> sh;

line 2-3 : The attribute value is extracted in a C++ enumeration
variable

line 6-7 : The attribute value is extracted in a DevShort variable
