Durneata Dan
Durneata Dan

Reputation: 1

C++ class template with inheritance

I'm struggling with a problem regarding the linking between two derived classes from a template one. Let's say that we have a template base class Param_B which take as parameter one of it's children (Param_S or Param_M). The template parameter of the class Param_B is used further for definition of other local variables( _parent and a callback function _func). For me is necessary to have the Param_S or Param_M for dynamically call of the function.

The problem is that after I create the pointers to the children's and add timers and functions to them I want to have a _link Variable which will enable to exchange information between both children types. E.g. from the Param_S I want to change the timeout of a Param_M timer by using the _link variable. I

#include <stdio.h>
#include <memory>
#include <vector>

class Param_S;
class Param_M;

template <class T>
class Functionn{
    Functionn(){};
    ~Functionn(){};
private:
    std::function<void(std::shared_ptr<T>)> _callback;
};


template <class T>
class Timer
{
public:
    Timer(){ timeout = 0; };
    ~Timer(){};

    void setTimeout(const int time){    timeout = time; };
    std::weak_ptr<Functionn<T>>& getFunction(){ return _func; };
    std::weak_ptr<T> getParent(){   return _parent;};

private:
    int timeout;
    std::weak_ptr<T> _parent;
    std::weak_ptr<Functionn<T>> _func;
};



template <class T>
class Param_B {
public:
    Param_B(){};
    ~Param_B(){};

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }
    std::weak_ptr<Param_B> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B> link){
            _link = link;
    }

private:
    std::weak_ptr<Param_B> _link;
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};



class Param_S : public Param_B<Param_S>
{
public:
    Param_S(){ paramS = -2; };
    ~Param_S(){};

    void setValue(){
        paramS = 33;
    }

    int getValue(){ return paramS;};

private:
    float paramS;
};


class Param_M : public Param_B<Param_M>
{
public:
    Param_M(){ parammM = -4; };
    ~Param_M(){};
    int getValue(){ return parammM; };

private:
    int parammM;

};


class sys{
public:

    std::vector<std::shared_ptr<Param_S>> getParams(){
        return _params;
    }

private:
    std::vector<std::shared_ptr<Param_S>> _params;
};

class module{
public:
    std::vector<std::shared_ptr<Param_M>> getParams(){
        return _params;
    }
private:
    std::vector<std::shared_ptr<Param_M>> _params;
};

void main(){

    std::shared_ptr<sys> _sys = std::make_shared<sys>();
    _sys->getParams().push_back(std::make_shared<Param_S>());
    _sys->getParams()[0]->addTimer(std::make_shared<Timer<Param_S>>());


    std::shared_ptr<module> _mod = std::make_shared<module>();
    _mod->getParams().push_back(std::make_shared<Param_M>());
    _mod->getParams()[0]->addTimer(std::make_shared<Timer<Param_M>>());


    _sys->getParams()[0]->setLink(_mod->getParams()[0]); // this not work , the conversion failed 
}

Thank you for your help.

Upvotes: 0

Views: 96

Answers (2)

izzy18
izzy18

Reputation: 804

I would suggest making the link between the two derived classes type-agnostic (i.e. make the link a std::function rather than a pointer to an instance of the other type.

If you would like to keep it as a pointer, your problem is in your use of Param_B within the Param_B template class.

void setLink(std::weak_ptr<Param_B> link)

In the above line, Param_B will refer to the Param_B<Param_M> when the method is called on an instance of Param_B<Param_M>, and it will refer to Param_B<Param_S> when the method is called on an instance of Param_B<Param_S>.

What you need to do is add a second template parameter to Param_B to tell it what the linked derived type is.

template <class T, class L>
class Param_B {
public:
    Param_B(){};
    ~Param_B(){};

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }
    std::weak_ptr<Param_B> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B<L, T>> link){
            _link = link;
    }

private:
    std::weak_ptr<Param_B<L, T>> _link;
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};

Then you'll need a forward declaration of Param_M so it can be a template parameter for Param_S.

class Param_M;

class Param_S : public Param_B<Param_S, Param_M>
{
...
};
class Param_M : public Param_B<Param_M, Param_S>
{
...
};

Edit:

If you know how the derived types will interact, don't use functions as links; see the answer from @Caleth.

Upvotes: 0

Caleth
Caleth

Reputation: 62636

You need to have a non-template base for Param_B. It can't involve anything related to the template parameter, but you could have virtual functions here

class Param_B_Base {
public:
    virtual ~Param_B_Base() = default;

    std::weak_ptr<Param_B_Base> getLink(){
            return _link;
    }
    void setLink(std::weak_ptr<Param_B_Base> link){
            _link = link;
    }

    // an example of possible behaviour
    virtual void setTimeouts(const int time) = 0;

    // another example of possible behaviour
    virtual void invokeHandlers() = 0;

private:
    std::weak_ptr<Param_B_Base> _link;
};

template <class T>
class Functionn{
    void operator()(std::shared_ptr<T> ptr) { _callback(ptr); }
private:
    std::function<void(std::shared_ptr<T>)> _callback;
};

template <class T>
class Timer
{
public:

    void setTimeout(const int time){    timeout = time; };
    void invoke() { if (auto func = _func.lock()) { if (auto parent = parent.lock() { (*func)(parent); } } }

private:
    int timeout = 0;
    std::weak_ptr<T> _parent;
    std::weak_ptr<Functionn<T>> _func;
};

Then you can have a template for subclasses

template <class T>
class Param_B : public Param_B_Base {
public:

    std::vector<std::shared_ptr<Timer<T>>>& getTimers(){
        return _timers;
    }
    void addTimer(std::shared_ptr<Timer<T>> _t){
        _timers.push_back(std::move(_t));
    }

    // The implementation of the virtual methods can know about T
    void setTimeouts(const int timeout) override {
        for (std::shared_ptr<Timer<T>> timer : _timers) {
            timer->setTimeout(timeout);
        }
    }

    void invokeHandlers() override {
        for (std::shared_ptr<Timer<T>> timer : _timers) {
            timer->invoke();
        }
    }
private:
    std::vector<std::shared_ptr<Timer<T>>> _timers;
};

Upvotes: 1

Related Questions