# How to create inheritance link between Tango classes

```{tags} audience:developers, lang:c++
```

This howto explains how to create a new Tango {term}`device class`
which inherits from an already existing one.

We use the usual definition of [inheritance](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) from object oriented programming:

1. The high level class supports all the commands/attributes defined in the low level class on top of its own
   (if any),
3. The device(s) available for the external world are an instances 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 have 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 the
inheritance link.

## The HighLevelClass.h file

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

```{code-block} cpp
:linenos: true

#ifndef _HIGHLEVELCLASS_H
#define _HIGHLEVELCLASS_H

#include <tango.h>
#include <HighLevel.h>

#include <LowLevelClass.h>
```

Then, we change the HighLevelClass inheritance declaration:

```{code-block} none
:linenos: true

//
// The HighLevelClass singleton definition
//

class
#ifdef WIN32
    __declspec(dllexport)
#endif
HighLevelClass : public LowLevel_ns::LowLevelClass
{
public:
//  properties member data

//  add your own data members here
//------------------------------------
```

## The HighLevelClass.cpp file

In this file, we have to modify:

1. The HighLevelClass constructor
2. The HighLevelClass command_factory() method
3. The HighLevelClass attribute_factory() method

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//
// method :         HighLevelClass::HighLevelClass(std::string &s)
//
// description :    constructor for the HighLevelClass
//
// in : - s : The class name
//
//-----------------------------------------------------------------------------
HighLevelClass::HighLevelClass(std::string &s)
: LowLevel_ns::LowLevelClass(s)
{
    TANGO_LOG_INFO << "Entering HighLevelClass constructor" << std::endl;
    set_default_property();
    get_class_property();
    write_class_property();

    TANGO_LOG_INFO << "Leaving HighLevelClass constructor" << std::endl;
}
```

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

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//
// method :         HighLevelClass::command_factory
//
// description :    Create the command object(s) and store them in the
//          command list
//
//-----------------------------------------------------------------------------
void HighLevelClass::command_factory()
{
    LowLevel_ns::LowLevelClass::command_factory();

    command_list.push_back(new HighLevelCmdClass("HighLevelCmd",
        Tango::DEV_VOID, Tango::DEV_VOID,
        "",
        "",
        Tango::OPERATOR));

    // add polling if any
    for(size_t i = 0 ; i < command_list.size(); i++)
    {
      // ...
    }
}
```

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

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//  Method: HighLevelClass::attribute_factory(std::vector<Tango::Attr *> &att_list)
//-----------------------------------------------------------------------------
void HighLevelClass::attribute_factory(std::vector<Tango::Attr *> &att_list)
{
    LowLevel_ns::LowLevelClass::attribute_factory(att_list);

    //  Attribute : HighLevelAttr
    HighLevelAttrAttrib *high_level_attr = new HighLevelAttrAttrib();
    // ...
    att_list.push_back(high_level_attr);
}
```

## The HighLevel.h file

This file has to be modified in order to:

1. Change the `HighLevel` class inheritance from `TANGO_BASE_CLASS` 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:

```{code-block} cpp
:linenos: true

#ifndef _HIGHLEVEL_H
#define _HIGHLEVEL_H

#include <tango.h>
#include <LowLevel.h>
```

Then, the change in the HighLevel class inheritance:

```{code-block} cpp
:linenos: true

class HighLevel: public LowLevel_ns::LowLevel
{
public :
    //  Add your own data members here
    //-----------------------------------------
```

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

```{code-block} cpp
:linenos: true

protected :
    //  Add your own data members here
    //-----------------------------------------
    bool device_constructed{false};
}
```

And finally, the change in the HighLevel class destructor:

```{code-block} cpp
:linenos: true

/**
 * The object desctructor.
 */
    ~HighLevel()
    {
      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:

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//
// method :         HighLevel::HighLevel(string &s)
//
// description :    constructor for HighLevel
//
// in : - cl : Pointer to the DeviceClass object
//      - s  : Device name
//      - d  : Description
//
//-----------------------------------------------------------------------------
HighLevel::HighLevel(Tango::DeviceClass *cl, std::string &s)
  :LowLevel_ns::LowLevel(cl, s.c_str())
{
    init_device();
    device_constructed = true;
}

HighLevel::HighLevel(Tango::DeviceClass *cl, const char *s)
  :LowLevel_ns::LowLevel(cl, s)
{
    init_device();
    device_constructed = true;
}

HighLevel::HighLevel(Tango::DeviceClass *cl, const char *s, const char *d)
  :LowLevel_ns::LowLevel(cl, s, d)
{
    init_device();
    device_constructed = true;
}
```

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

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//
// method :         HighLevel::delete_device()
//
// description :    will be called at device destruction or at init command.
//
//-----------------------------------------------------------------------------
void HighLevel::delete_device()
{
    TANGO_LOG_INFO << "HighLevel::delete_device()" << std::endl;

    // Delete device's allocated object

    if(device_constructed)
    {
       LowLevel_ns::LowLevel::delete_device();
    }
}

//+----------------------------------------------------------------------------
//
// method :         HighLevel::init_device()
//
// description :    will be called at device initialization.
//
//-----------------------------------------------------------------------------
void HighLevel::init_device()
{
    if(device_constructed)
    {
      LowLevel_ns::LowLevel::init_device();
    }

    TANGO_LOG_INFO << "HighLevel::HighLevel() create device " << device_name << std::endl;
    // ...
```

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

```{code-block} cpp
:linenos: true

//+----------------------------------------------------------------------------
//
// method :         HighLevel::always_executed_hook()
//
// description :    method always executed before any command is executed
//
//-----------------------------------------------------------------------------
void HighLevel::always_executed_hook()
{
    LowLevel_ns::LowLevel::always_executed_hook();
    TANGO_LOG_INFO << "HighLevel::always_executed_hook()" << std::endl;
    // ...
}

//+----------------------------------------------------------------------------
//
// method :         HighLevel::read_attr_hardware
//
// description :    Hardware acquisition for attributes.
//
//-----------------------------------------------------------------------------
void HighLevel::read_attr_hardware(std::vector<long> &attr_list)
{
    LowLevel_ns::LowLevel::read_attr_hardware(attr_list);
    TANGO_DEBUG_LOG << "HighLevel::read_attr_hardware(std::vector<long> &attr_list) entering... " << std::endl;
    // ...
}
```

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

The `HighLevel` class can be registered and started with the usual approach by
[Jive](#howto-start-device-server) or [tango_admin](tango-admin-add-device-server).

## 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.
