# How to create inheritance link between Tango classes (C++)

Intended audience: developers, Programming language: c++

This HowTo explains how it is possible to create a new Tango class which inherits from an already existing Tango class.

In Tango’s vocabulary, a Tango class is not a computer language class. A Tango class has at least 2 computer language classes and very often even more than 2. This HowTo explains how you can create a new Tango class which “inherits” from an already existing class. In this case, inheritance means:

1. The high level class supports all the commands defined in the low level class on top of its own commands (if any),

2. The high level class supports all the attributes defined by the low leval class on top of its own attributes (if any),

3. The device(s) available for the external world is(are) an instance(s) of the high level class.

Warning

Once modified, Pogo will not be able to understand the high level Tango class and therefore can’t be used for further changes in this class

## The example case

Let’s say that we already have a Tango class called LowLevel with one command called LowLevelCmd and one attribute called LowLevelAttr. We want to create a new Tango class called HighLevel with its own command called HighLevelCmd and its own attribute called HighLevelAttr. Both classes has been generated using Pogo. The following is a description of what has to be modified in the code generated by Pogo for the HighLevel Tango class in order to create this “inheritance” link.

All modified code in the following code snippets is in bold font.

## The HighLevelClass.h file

In this file, we will modify the HighLevelClass declaration in order to inherit from the LowLevelClass class instead of inheriting from the Tango::DeviceClass class but we first need to add an extra include file at the file beginning:

    1#ifndef _HIGHLEVELCLASS_H
    2#define _HIGHLEVELCLASS_H
    3
    4#include <tango.h>
    5#include <HighLevel.h>
    6
    7#include <LowLevelClass.h>

Then, we change the HighLevelClass inheritance declaration:

     1//
     2// The HighLevelClass singleton definition
     3//
     4
     5class
     6#ifdef WIN32
     7    __declspec(dllexport)
     8#endif
     9HighLevelClass : public LowLevel_ns::LowLevelClass
    10{
    11public:
    12//  properties member data
    13
    14//  add your own data members here
    15//------------------------------------

## The HighLevelClass.cpp file

In this file, we have to modify:

1. The HighLevelClass definition (due to the change in its inheritance),

2. The HighLevelClass command_factory() method,

3. The HighLevelClass attribute_factory() method.

     1//+----------------------------------------------------------------------------
     2//
     3// method :         HighLevelClass::HighLevelClass(string &s)
     4//
     5// description :    constructor for the HighLevelClass
     6//
     7// in : - s : The class name
     8//
     9//-----------------------------------------------------------------------------
    10HighLevelClass::HighLevelClass(string &s):LowLevel_ns::LowLevelClass(s)
    11{
    12
    13    cout2 << "Entering HighLevelClass constructor" << endl;
    14    set_default_property();
    15    get_class_property();
    16    write_class_property();
    17
    18    cout2 << "Leaving HighLevelClass constructor" << endl;
    19
    20}

Then, the changes in the command_factory() method which needs to call the LowLevelClass command_factory() method:

     1//+----------------------------------------------------------------------------
     2//
     3// method :         HighLevelClass::command_factory
     4//
     5// description :    Create the command object(s) and store them in the
     6//          command list
     7//
     8//-----------------------------------------------------------------------------
     9void HighLevelClass::command_factory()
    10{
    11    LowLevel_ns::LowLevelClass::command_factory();
    12
    13    command_list.push_back(new HighLevelCmdClass("HighLevelCmd",
    14        Tango::DEV_VOID, Tango::DEV_VOID,
    15        "",
    16        "",
    17        Tango::OPERATOR));
    18
    19    //  add polling if any
    20    for (unsigned int i=0 ; i<command_list.size(); i++)
    21    {
    22    }
    23}

Finally, the changes in the attribute_factory() method which needs to call the LowLevelClass attribute_factory() method:

     1//+----------------------------------------------------------------------------
     2//  Method: HighLevelClass::attribute_factory(vector<Tango::Attr *> &att_list)
     3//-----------------------------------------------------------------------------
     4void HighLevelClass::attribute_factory(vector<Tango::Attr *> &att_list)
     5{
     6    LowLevel_ns::LowLevelClass::attribute_factory(att_list);
     7
     8    //  Attribute : HighLevelAttr
     9    HighLevelAttrAttrib *high_level_attr = new HighLevelAttrAttrib();
    10    att_list.push_back(high_level_attr);
    11
    12    //  End of Automatic code generation
    13    //-------------------------------------------------------------
    14}

## The HighLevel.h file

This file has to be modified in order to:

1. Change the HighLevel class inheritance from Tango::Device_3Impl to

     LowLevel_ns::LowLevel

2. Add a new data member in the HighLevel class in order to correctly

     implement the device Init command (a boolean is enough)

3. Modify the class destructor for a correct management of the device

     Init command

First, we have to add a new include file:

    1#ifndef _HIGHLEVEL_H
    2#define _HIGHLEVEL_H
    3
    4#include <tango.h>
    5#include <LowLevel.h>

Then, the change in the HighLevel class inheritance:

    1class HighLevel: public LowLevel_ns::LowLevel
    2{
    3public :
    4    //  Add your own data members here
    5    //-----------------------------------------

The addition of the new data member at the end of the HighLevel class declaration:

    1protected :
    2    //  Add your own data members here
    3    //-----------------------------------------
    4    bool device_constructed;
    5}

And finally, the change in the HighLevel class destructor:

    1/**
    2 * The object desctructor.
    3 */
    4    ~HighLevel() {device_constructed=false;delete_device();}

## The HighLevel.cpp file

In this file, we have to modify

1. The HighLevel class constructors to reflect the change in its inheritance and to initialize the new data member (device_constructed)

2. The HighLevel class delete_device() and init_device() to correctly handle the device Init command

3. The HighLevel class always_executed_hook() and read_attr_hardware() methods in order that they call the corresponding LowLevel class method

Let’s start with the changes in the HighLevel class constructors:

     1//+----------------------------------------------------------------------------
     2//
     3// method :         HighLevel::HighLevel(string &s)
     4//
     5// description :    constructor for simulated HighLevel
     6//
     7// in : - cl : Pointer to the DeviceClass object
     8//      - s : Device name
     9//
    10//-----------------------------------------------------------------------------
    11HighLevel::HighLevel(Tango::DeviceClass *cl,string &s)
    12:LowLevel_ns::LowLevel(cl,s.c_str()),device_constructed(false)
    13{
    14    init_device();
    15    device_constructed = true;
    16}
    17
    18HighLevel::HighLevel(Tango::DeviceClass *cl,const char *s)
    19:LowLevel_ns::LowLevel(cl,s),device_constructed(false)
    20{
    21    init_device();
    22    device_constructed = true;
    23}
    24
    25HighLevel::HighLevel(Tango::DeviceClass *cl,const char *s,const char *d)
    26:LowLevel_ns::LowLevel(cl,s,d),device_constructed(false)
    27{
    28    init_device();
    29    device_constructed = true;
    30}

Now, the modified HighLevel class init_device() and delete_device() methods:

     1//+----------------------------------------------------------------------------
     2//
     3// method :         HighLevel::delete_device()
     4//
     5// description :    will be called at device destruction or at init command.
     6//
     7//-----------------------------------------------------------------------------
     8void HighLevel::delete_device()
     9{
    10    INFO_STREAM << "In HighLevel::delete_device()" << endl;
    11
    12    //  Delete device's allocated object
    13
    14//    Your specific code (if any)
    15if (device_constructed == true)
    16        LowLevel_ns::LowLevel::delete_device();
    17}
    18
    19//+----------------------------------------------------------------------------
    20//
    21// method :         HighLevel::init_device()
    22//
    23// description :    will be called at device initialization.
    24//
    25//-----------------------------------------------------------------------------
    26void HighLevel::init_device()
    27{
    28    if (device_constructed == true)
    29        LowLevel_ns::LowLevel::init_device();
    30
    31    INFO_STREAM << "HighLevel::HighLevel() create device " << device_name << endl;
    32
    33    // Initialise variables to default values
    34    //--------------------------------------------

And finally, the HighLevel class always_executed_hook() and read_attr_hardware() methods:

     1//+----------------------------------------------------------------------------
     2//
     3// method :         HighLevel::always_executed_hook()
     4//
     5// description :    method always executed before any command is executed
     6//
     7//-----------------------------------------------------------------------------
     8void HighLevel::always_executed_hook()
     9{
    10    LowLevel_ns::LowLevel::always_executed_hook();
    11    INFO_STREAM << "In HighLevel::always_executed_hook()" << endl;
    12//     Your code here (if any)
    13}
    14
    15//+----------------------------------------------------------------------------
    16//
    17// method :         HighLevel::read_attr_hardware
    18//
    19// description :    Hardware acquisition for attributes.
    20//
    21//-----------------------------------------------------------------------------
    22void HighLevel::read_attr_hardware(vector<long> &attr_list)
    23{
    24    LowLevel_ns::LowLevel::read_attr_hardware(attr_list);
    25        DEBUG_STREAM << "HighLevel::read_attr_hardware(vector<long> &attr_list) entering... "<< endl;
    26    //  Add your own code here
    27}

Don’t forget to also modify the Makefile in order to link the three LowLevel Tango class object files (Lowlevel.o, LowLevelClass.o and LowLevelStateMachine.o) to your executable.

## Defining the class in the Tango control system

Once the executable is linked, the device server process instance has to be created in the Tango database (using Jive for instance).

## Running your class

Nothing special here. Simply start your device server as usual. Connecting to the device using a “Test Panel” shows that the device now has:

- two commands (one from the LowLevel class and the other from the HighLevel class),

- two attributes (from LowLevel class and from HighLevel class).

## Conclusion

With these relatively simple changes in the HighLevel class, we now have a device instance of a Tango class which “inherits” from another Tango class. The drawback of this method is that once the file has been modified, Pogo will not be able to understand the HighLevel class any more and should not be used for further changes in this class!

With a couple of “virtual” methods, it is also possible in the HighLevel class to overwrite a command or an attribute defined in the Lowlevel class.