0

I am looking for a workaround to the lack of virtual template functions in C++. What I want ideally is to be able to store my derived classes in a vector, iterate over those and call the correct function, so in pseudo-code:


template<typename T>
struct Output
{
    ...
};

struct Base
{
    template<typename T>
    virtual void doSomething(Output<T>& out) = 0;
};

struct DerivedA : public Base
{
    DerivedA(const char* filename) {...}
    template<typename T>
    void doSomething(Output<T>& out) final
    {
        ...
    }
};

struct DerivedB : public Base
{
    DerivedB(const char* filename) {...}
    template<typename T>
    void doSomething(Output<T>& out) final
    {
        ...
    }
};

int main()
{
    std::vector<Base*> vec;
    vec.push_back(new DerivedA("data1.bin"));
    vec.push_back(new DerivedB("data2.bin"));
    vec.push_back(new DerivedA("data3.bin"));
    vec.push_back(new DerivedA("data4.bin"));

    Output<float> outF;
    Output<double> outD;
    Output<int> outI;
    for (auto e : vec)
    {
        e->doSomething(outF);
        e->doSomething(outD);
        e->doSomething(outI);
    }

    return 0;
}

I would prefer it if the workaround is as "painless" and non-verbose as possible (since I am using the templates to avoid redefining the same function n times for n different types in the first place). What I had in mind was making myself a vtable with std::map, and doing some dynamic_casts. I am looking for any better ideas, or even for a concise implementation of that idea if you consider it the best in this scenario. I am looking for a solution that is ideally the least intrusive, and that is very easy to add new classes to.

Edit: I figured a workaround, but it includes some verbosity (but at least avoids non-trivial code duplication):

struct Base
{
    virtual void doSomething(Output<int>& out) = 0;
    virtual void doSomething(Output<float>& out) = 0;
    virtual void doSomething(Output<double>& out) = 0;

private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "Base doSomething called with: " << typeid(T).name() << "\n";
    }
};

struct DerivedA : public Base
{
    void doSomething(Output<int>& out) final
    {
        doSomething<int>(out);
    }
    void doSomething(Output<float>& out) final
    {
        doSomething<float>(out);
    }
    void doSomething(Output<double>& out) final
    {
        doSomething<double>(out);
    }
private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "DerivedA doSomething called with: " << typeid(T).name() << "\n";
    }
};

struct DerivedB : public Base
{
    void doSomething(Output<int>& out) final
    {
        doSomething<int>(out);
    }
    void doSomething(Output<float>& out) final
    {
        doSomething<float>(out);
    }
    void doSomething(Output<double>& out) final
    {
        doSomething<double>(out);
    }
private:
    template<typename T>
    void doSomething(Output<T>& out)
    {
        std::cout << "DerivedB doSomething called with: " << typeid(T).name() << "\n";
    }
};

Does anybody have any better idea how I can go about this without having to redefine the same functions over and over? Ideally it would be defined once in the base class, CRTP doesn't seem to help. Dynamic casts seem like the other sane option.

lightxbulb
  • 1,251
  • 12
  • 29
  • 1
    Looks like a typical case of https://en.wikipedia.org/wiki/Visitor_pattern – bipll Apr 07 '19 at 22:32
  • @bipll Could you elaborate on that? I read through the explanation, but it works with different classes, not functions. My template is not on the class, it's on the function. The uses cases outlined also do not seem to agree with what I am doing. I am basically trying to avoid code duplication, while having virtual functions. I could very well define doSomethingFloat, doSomethingInt, doSomethingDouble and use virtual functions, but then I'll have to modify the code at 3 different places each time. A macro may do, but seems inellegant. – lightxbulb Apr 08 '19 at 06:59
  • Possible duplicate of [C++ Virtual template method](https://stackoverflow.com/questions/7968023/c-virtual-template-method) – Nellie Danielyan Apr 08 '19 at 14:07
  • @NellieDanielyan Doesn't really address my problem. I am not looking to store data. I am looking to reduce code duplication and verbosity, refer to my second edit (which btw was available before you decided it was a duplicate, which leads me to believe you didn't even read it). – lightxbulb Apr 08 '19 at 14:20

1 Answers1

0

Try something like this:

struct OutputBase
{
    virtual void doSomething() = 0;
};

template<class T >
struct Output : public OutputBase
{
    virtual void doSomething()
    {
        std::cout << typeid(T).name();
    }
};


struct Base
{
    virtual void doSomething(OutputBase* out) = 0;
};

struct DerivedA : public Base
{
    virtual void doSomething(OutputBase* out)
    {
        std::cout << "DerivedA doSomething called with: ";
        out->doSomething();
        std::cout<< std::endl;
    }
};

struct DerivedB : public Base
{
    virtual void doSomething(OutputBase* out)
    {
        std::cout << "DerivedB doSomething called with: ";
        out->doSomething();
        std::cout << std::endl;
    }
};
int main()
{
    OutputBase* out_int = new Output < int > ;
    OutputBase* out_double = new Output < double >;
    Base* a = new DerivedA;
    a->doSomething(out_int);
    a->doSomething(out_double);
    Base* b = new DerivedB;
    b->doSomething(out_int);
    b->doSomething(out_double);

    return 0;
}

You can use a wrapper around Output if you don't want to change it.

Nellie Danielyan
  • 1,001
  • 7
  • 19
  • That's not what I am trying to achieve. Output is simply a container for computations performed by the respective doSomething methods, as you may notice it has no methods in my original implementation, I could even pass it as a void* if I wanted to. It's the doSomething in the derived classes that perform the majority of the work and they need to be aware of the type T. But say all the computations for int8,int16,int32,int64 are exactly the same, rather than duplicating code 4 times, I wanted to template it and be done with it, but then it breaks the virtual functions. I am aware this can be.. – lightxbulb Apr 08 '19 at 18:13
  • ... done with macros, but it's probably the least elegant variant, and also fairly prone to errors. – lightxbulb Apr 08 '19 at 18:14
  • @lightxbulb what exactly is the type T used for inside the doSomething methods? – Nellie Danielyan Apr 08 '19 at 18:46
  • Mathematical computations for the most part. So T can be double, float, int8/16/32/64, uint8/16/32/64, but also complex numbers maybe. It's literally a mathematical algorithm implementation. In some cases the function may need to be split between integers and floating point numbers etc. Either way I am looking for a language/design construction that can remove some verbosity off the code in the edit. I believe it's a good minimal example that doesn't really require you to know the internals. What people seem to want to do is modify the initial formulation of my problem. – lightxbulb Apr 08 '19 at 18:54