Mixing Templates with Polymorphism

mixing templates with polymorphism

You have a few choices. I'll explain my preferred solution first.

1. Use dynamic dispatch

If you have an array of a base class type, why do you even want to do stuff with Var? That variable is specific to the child class. If you have a A somewhere, you shouldn't even care what B has or hasn't at that place.

Operations on the typed variable should be encapsulated in virtual function in the base class. If you want to do condition and stuff, maybe you could encapsulate that condition into a virtual function that returns a boolean.

2a. Drop the base class and use variant

Sometimes, you know in advance the amount of types that will go into that list. Using a variant and drop the base class is a good solution that may apply to your case.

Let's say you only have int, double and std::string:

using poly = std::variant<B<int>, B<double>, B<std::string>>;

std::array<poly, 3> arr;

arr[0] = B<int>{};
arr[1] = B<double>{};
arr[2] = B<std::string>{};
// arr[2] = B<widget>{}; // error, not in the variant type

std::visit(
[](auto& b) {
using T = std::decay_t<decltype(b)>;
if constexpr (std::is_same_v<B<int>, T>) {
b.Var = 2; // yay!
}
},
arr[0]
);

2b. Drop the base class and use generic functions

Drop the base class entirely, and template your functions that do operation on them. You can move all your function into an interface or many std::function. Operate on that instead of the function directly.

Here's an example of what I meant:

template<typename T>
void useA(T const& a) {
a.Var = 34; // Yay, direct access!
}

struct B {
std::function<void()> useA;
};

void createBWithInt() {
A<int> a;
B b;

b.useA = [a]{
useA(a);
};
};

This is fine for cases where you only have few operations. But it can quickly lead to code bloat if you have a lot of operations or if you have many types of std::function.

3. Use a visitor

You could create a visitor that dispatch to the right type.

This solution would be much close to what you except, but is quite combersome and can break easily when adding cases.

Something like this:

struct B_Details {
protected:
struct Visitor {
virtual accept(int) = 0;
virtual void accept(double) = 0;
virtual void accept(std::string) = 0;
virtual void accept(some_type) = 0;
};

template<typename T>
struct VisitorImpl : T, Visitor {
void accept(int value) override {
T::operator()(value);
}

void accept(double) override {
T::operator()(value);
}

void accept(std::string) override {
T::operator()(value);
}

void accept(some_type) override {
T::operator()(value);
}
};
};

template<typename T>
struct B : private B_Details {
template<typename F>
void visit(F f) {
dispatch_visitor(VisitorImpl<F>{f});
}

private:
virtual void dispatch_visitor(Visitor const&) = 0;
};

// later

B* b = ...;

b->visit([](auto const& Var) {
// Var is the right type here
});

Then of course, you have to implement the dispatch_visitor for each child class.

4. Use std::any

This is litteraly returning the variable with type erasure. You cannot do any operation on it without casting it back:

class A {
std::any GetVar()
};

I personnaly don't like this solution because it can break easily and is not generic at all. I would not even use polymorphism in that case.

C++ Combining Static and Dynamic Polymorphism to Create Hyper Polymorphism?

From reading the comments it looks like this technique is actually considered a "decorator pattern".

It can be used when you need to extend the functionality of multiple existing classes (for example adding a member variable z) when you can't modify the classes directly.

It's more flexible than regular inheritance because you can extend the functionality of many classes by only writing one new class.

Not exactly the same as "hyper polymorphism", but hey, maybe that will be a thing some day. ;)

Mixing Generic Programming with Polymorphism

Is the following design bad practice. Mixing Polymorphism and Generic Code?

No, that's sometimes the right thing to do.

Is there any pit falls with my code below.

  • The container with raw pointers std::vector<T*> pVT; looks a bit fishy.
  • The base class destructor should probably be pure virtual.
  • I would use the C++11 syntax for making the class non-copyable.
  • You only need to make the base class non-copyable.
  • I don't see the need for dynamic allocation in your main function.

For the rest I don't see any immediate errors.

It's not possible to say whether or not your design is good without knowing what you are trying to do.

Can template polymorphism be used in place of OO polymorphism?

Templates provide static polymorphism: you specify a template parameter at compile time implementing the strategy. They don't provide dynamic polymorphism, where you supply an object at runtime with virtual member functions that implement the strategy.

Your example template code will create three different classes, each of which contains all the Interpolator code, compiled using different template parameters and possibly inlining code from them. That probably isn't what you want from the POV of code size, although there's nothing categorically wrong with it. Supposing that you were optimising to avoid function call overhead, then it might be an improvement on dynamic polymorphism. More likely it's overkill. If you want to use the strategy pattern dynamically, then you don't need templates, just make virtual calls where relevant.

You can't have a variable of type MyTemplate<?> (except appearing in another template before it's instantiated). MyTemplate<X> and MyTemplate<Y> are completely unrelated classes (even if X and Y are related), which perhaps just so happen to have similar functions if they're instantiated from the same template (which they needn't be - one might be a specialisation). Even if they are, if the template parameter is involved in the signatures of any of the member functions, then those functions aren't the same, they just have the same names. So from the POV of dynamic polymorphism, instances of the same template are in the same position as any two classes - they can only play if you give them a common base class with some virtual member functions.

So, you could define a common base class:

class InterpolatorInterface {
public:
virtual Value GetValue(const double) = 0;
virtual void ConfigCache(const& ConfigObject) = 0;
virtual void ConfigDataSource(const& ConfigObject) = 0;
virtual ~InterpolatorInterface() {}
};

Then:

template <typename TCacheStrategy, typename TDataSource>
class Interpolator: public InterpolatorInterface {
...
};

Now you're using templates to create your different kinds of Interpolator according to what's known at compile time (so calls from the interpolator to the strategies are non-virtual), and you're using dynamic polymorphism to treat them the same even though you don't know until runtime which one you want (so calls from the client to the interpolator are virtual). You just have to remember that the two are pretty much completely independent techniques, and the decisions where to use each are pretty much unrelated.

Btw, this isn't template meta-programming, it's just using templates.

Edit. As for what TMP is, here's the canonical introductory example:

#include <iostream>

template<int N>
struct Factorial {
static const int value = N*Factorial<N-1>::value;
};

template<>
struct Factorial<0> {
static const int value = 1;
};

int main() {
std::cout << "12! = " << Factorial<12>::value << "\n";
}

Observe that 12! has been calculated by the compiler, and is a compile-time constant. This is exciting because it turns out that the C++ template system is a Turing-complete programming language, which the C preprocessor is not. Subject to resource limits, you can do arbitrary computations at compile time, avoiding runtime overhead in situations where you know the inputs at compile time. Templates can manipulate their template parameters like a functional language, and template parameters can be integers or types. Or functions, although those can't be "called" at compile time. Or other templates, although those can't be "returned" as static members of a struct.

C++ Templates polymorphism obstacle

OK, first, an explanation of the problem here. What's required is an interface, that defines a virtual method, used to get a value with a templated type. Since what we want is an interface, the get method has to be virtual. On the other hand, we would like to be able to return different types, so we want to templetize it. However, a virtual method can not be templetized, because the compiler wouldn't know which instantions of that method to include in the vtable.

One solution is to do what's done in the question, i.e. templetize the interface class. An important property of template types is that different instantiations of the same class are completely different types. They don't share a common base, and they're not convertible to each other. We simply can not have an Interface<Generic> pointer going around in regular functions, with their get() methods being called. Consider this: Every instantion of the Interface template type has a different signature for the get() method. This means that while that method is being called, different things have to happen on the stack. How could the compiler know which version of the get() method to call (how to prepare the stack for the function call) if all it has is a Interface<Generic> pointer.

I can think of two general solutions to that problem.

  1. Remove all template mumbo-jumbo and make the get() method return a type-erased object, such as boost::variant or boost::any. Correct me if I'm wrong here(*), but boost::variant is like a union that remembers which type of the union is assigned, while boost::any is like a void *, but it remembers what type it's pointing to. This solution path implies two things:
    a) The types of the returned objects will be resolved at runtime, and there will be some overhead while manipulating these types.
    b) The child classes of Interface will have to manage one of these type-erased objects, making them more complicated.

  2. Take the template mumbo-jumbo to the extreme and refer to Interface objects always in a templetized context, so that the compiler generates the right function calls during the instantiations of those contexts. I gave an example below which follows this path. The example creates a container for holding together different types of Interface<> objects, while enabling the application of templetized functionals (is it correct to call this generally "visitors"?) to them. Note that in that example, the Interface objects with different type parameters are actually kept in different std::lists in that container class, so in the runtime, there's no need to resolve their types.

Disclaimer: What follows is an overkill...

Here's how you can have a container of the "interface" template class with different template arguments. I've used an std::list to keep the instances, but you can change it.

#include<boost/fusion/container/vector.hpp>
#include<boost/fusion/algorithm.hpp>
#include<boost/mpl/transform.hpp>
#include<boost/mpl/contains.hpp>
#include<boost/utility/enable_if.hpp>
#include<boost/type_traits/add_reference.hpp>
#include<list>
#include<algorithm>
#include <iostream>

using namespace boost;

template <class T>
class Interface{
public:
typedef T Units;
virtual T get() = 0;
};

class Implementation1: public Interface<float> {
public:

float get() {
return 0.0f;
}

};

class Implementation2: public Interface<int> {
public:

int get() {
return 5;
}

};

template<class element>
struct to_list {
typedef std::list<Interface<element> *> type;
};

template<class elementVector>
struct to_containers {
typedef typename mpl::transform<elementVector,to_list<mpl::_1> >::type type;
};

class Container{
typedef fusion::vector<int,float> AllowedTypes;
typename to_containers<AllowedTypes>::type containers;

public:
template<class type> typename enable_if<mpl::contains<AllowedTypes,type>,void>::type
/*void*/ add(Interface< type/*included in AllowedTypes*/ > & floatGetter) {
fusion::deref(fusion::find<typename to_list<type>::type >(containers))
/*<type> container*/.push_back(&floatGetter);
}

template<class functional>
void apply(functional f) {
fusion::for_each(containers,applyFunctional<functional>(f));
}

private:
template<class functional>
struct applyFunctional {
functional f;
applyFunctional(functional f): f(f){}
template<class T> void operator()(T & in) const {
std::for_each(in.begin(), in.end(),f);
}
};

};

struct printValueFunctional {
template<class element>
void operator()(Interface<element> * in) const {
std::cout<<"Hi, my value is:"<<in->get()<<"\n";
}
};

int main() {

Implementation1 impl1;
Implementation2 impl2;
Interface<float> &myInterface1 = impl1;
Interface<int> &myInterface2 = impl2;
Container container;
container.add(myInterface1);
container.add(myInterface2);
container.apply(printValueFunctional());
return 0;
}

And the output is:

Hi, my value is:5
Hi, my value is:0

Well, this really is a huge overkill for most applications, but you asked for it :)

If you just want an interface, that can return different things, you could also consider boost.variant. The example above is truly valuable for all the static polymorphism it uses.

EDIT: David has pointed something important, it might be a pitfall, if you, for some reason, assume otherwise. This container doesn't really stay true to the order of the item insertions. The order of your functional calls might not happen in the order of the insertions of the items, i.e., assume that the iteration will be in a "random" order.

(*) boost::variant and boost::any are discussed here

Template specialization with polymorphism

The "Visitor" pattern comes to the rescue in this case. It enables polymorphic behavior in an algorithm implemented outside the class. Some support code is required inside the class, but new algorithms can later be added, existing algorithms modified, etc., without affecting the class.

How to achieve polymorphism with templated functions?

Here's a C# version of the behavior I'm trying to achieve:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace baseAbstractTemplates.NET {
interface IObjectTransformer {
TStructure ToStructure<TStructure>(ICollection<byte> bytes);
ICollection<byte> ToBytes<TStructure>(TStructure structure);
};

class ObjectTransformer1 : IObjectTransformer {

#region Implementation of IObjectTransformerBaseAbstractClass

public TStructure ToStructure<TStructure>(ICollection<byte> bytes) {
throw new NotImplementedException();
}

public ICollection<byte> ToBytes<TStructure>(TStructure structure) {
throw new NotImplementedException();
}

#endregion

}

class ObjectTransformer2 : IObjectTransformer {

#region Implementation of IObjectTransformerBaseAbstractClass

public TStructure ToStructure<TStructure>(ICollection<byte> bytes) {
throw new NotImplementedException();
}

public ICollection<byte> ToBytes<TStructure>(TStructure structure) {
throw new NotImplementedException();
}

#endregion

}

class Program {
public static void CoutStructureBytes(IObjectTransformer objectTransformer) {
var bytes = objectTransformer.ToBytes(3);
Console.WriteLine(bytes);
}

static void Main(string[] args) {
ObjectTransformer1 objectTransformer1 = new ObjectTransformer1();
ObjectTransformer2 objectTransformer2 = new ObjectTransformer2();
CoutStructureBytes(objectTransformer1);
CoutStructureBytes(objectTransformer2);
}
}
}

In C# it just works in "haha C# interfaces, templates, polymorphism go brrr" style. Even if you're not familiar with C# at all but have C++ knowledge, I'm sure you can follow that C# code just fine.

This compiles and runs just fine, throws NotImplementedException because not implemented.

But in C++, unlike in C#, I can't just have interfaces with templates, inheritance and polymorphism using usual tools: pure abstract functions(which I override in derived classes), templates, inheritance and method overriding. Because I can't mix method templates with virtual.

After a few days of research I finally found how it's done here:
C++ is Lazy: CRTP - ModernesCpp.com[^]

CRTP and Static Polymorphism. Finally, C++ version of the behavior I was trying to achieve:

// bastAbstractTemplates.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <vector>
#include <string>

template<typename DerivedClass>
class IObjectTransformer {
public:
template<typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes);
template<typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure);
private:
IObjectTransformer() = default;

friend DerivedClass;
};

template <typename DerivedClass>
template <typename TStructure>
TStructure IObjectTransformer<DerivedClass>::ToStructure(std::vector<unsigned char> bytes) {
return static_cast<DerivedClass*>(this)->ToStructure(bytes);
}

template <typename DerivedClass>
template <typename TStructure>
std::vector<unsigned char> IObjectTransformer<DerivedClass>::ToBytes(TStructure structure) {
return static_cast<DerivedClass*>(this)->ToBytes(structure);
}

class ObjectTransformer1 : public IObjectTransformer<ObjectTransformer1> {
public:
template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
unsigned char* bytePointer = &bytes[0];
TStructure structure = reinterpret_cast<TStructure>(*bytePointer);
return structure;
}

template <typename TStructure> std::vector<unsigned char> ToBytes(TStructure structure) {
char* bytesArray = reinterpret_cast<char*>(&structure);
auto byteVec = std::vector<unsigned char>(bytesArray, bytesArray + sizeof(TStructure));
return byteVec;
}
};

class ObjectTransformer2 : public IObjectTransformer<ObjectTransformer2> {
public:
template <typename TStructure> TStructure ToStructure(std::vector<unsigned char> bytes) {
TStructure structure{};
std::memcpy(&structure, &bytes[0], sizeof(TStructure));
return structure;
}
template <typename TStructure>
std::vector<unsigned char> ToBytes(TStructure structure) {
std::vector<unsigned char> bytes{};
bytes.resize(sizeof(TStructure));
std::memcpy(&bytes[0], &structure, sizeof(TStructure));
return bytes;
}
};

template <typename DerivedClass, typename TStructure>
void CoutStructureBytes(IObjectTransformer<DerivedClass> *objectTransformerBaseAbstractClass, TStructure structure) {
auto bytes = objectTransformerBaseAbstractClass->template ToBytes<TStructure>(structure);
for(auto byte : bytes) {
std::cout << std::to_string(byte) << ' ';
}
std::cout << std::endl;
}

int main() {
ObjectTransformer1 objectTransformer1{};
ObjectTransformer1 objectTransformer2{};

int integer = 5;
float someFloat = 9.79f;

CoutStructureBytes(&objectTransformer1, integer);
CoutStructureBytes(&objectTransformer2, someFloat);
}


Related Topics



Leave a reply



Submit