Home > front end >  How to do actions on members of child class templates from an abstact base class?
How to do actions on members of child class templates from an abstact base class?

Time:09-25

I know this question gets asked a lot, but I have a specific use case, so I don't think it's a duplicate!

I have an abstract base class:

template<int N>
class Child;

class Base
{

public:
     // Factory-like generation of children as Base
     static Ptr<Base> New(int baseN)
     {
         if (baseN == 2) return new Child<2>(); 
         else if (baseN == 3) return new Child<3>()
     }

     // Update
     virtual void update() = 0;
};

And I'm writing some children of Base as class templates (on an int):

template<int N>
class Child
:
    public Base
{
     // Member, N is not the size of matrix, more like the size of a component in matrix
     Matrix<N> m_member;

public:
     // Implement update
     virtual void update();

     // Should call the passed callable on m_member
     virtual void execute(std::function<void(Matrix<N>&)>&);
};

// Force compilation of Child<N>  for some values of N (of interest, including 3) here

// Then,
int baseN = 3;
Ptr<Base> obj = Base::New(baseN); // will get me a Child<3> as a Base object


auto callable = [](Matrix<3>) ->void {};

// Can I access Child<3>::m_member ??
// Can't cast to Child<baseN> (baseN is not constexpr) and don't want to
// But want to do something like:
obj->execute(callable);
// Which forwards 'callable' to the method from concrete type, probably using a cast?

In short, I need to have some sort of access to m_member from the declared Base object. Preferably, a way to call Child<N>::execute from Base without making Base a template on N too.

Things I've tried/thought-of include:

  1. 'Type erasure' of Matrix<N> by hiding them behind an interface, but because Matrix<N>'s interface strongly depends on N, doing that renders the classes useless (think: Vector<N>& Matrix<N>::diag() for example)
  2. Can Base::New do anything to record what concrete type it creates? I doubt that because types are not objects.

CodePudding user response:

It seems you want inheritance to group not so related classes...

std::variant (C 17) might be more appropriate:

template<int N>
class Child
{
     // Member, N is not the size of matrix, more like the size of a component in matrix
     Matrix<N> m_member;

public:
     void update();

     void execute(std::function<void(Matrix<N>&)> f) { f(m_member); }
};

using Base = std::variant<Child<2>, Child<3>>;

and then:

void foo(Base& obj)
{
    struct Visitor {
        template <std::size_t N>
        void operator()(Child<N>& c) const
        {
            auto callable = [](Matrix<N>) -> void {/*..*/};
            c.execute(callable);
        }
    } visitor;
    std::visit(visitor, obj);
}
  • Related