class A
{
friend void foo();
virtual void print_Var() const{};
};// does not contain variable Var;
template<class T>
class B : public A
{
T Var;
public:
B(T x):Var(x){}
void print_Var() const override
{
std::cout<<Var<<std::endl;
}
};
void foo()
{
std::array<std::unique_ptr<A>, 3> Arr = {
std::make_unique<B<int>>(100),
std::make_unique<B<int>>(20),
std::make_unique<B<std::string>>("Hello Stackoverflow")
};
std::shuffle(Arr.begin(), Arr.end(), std::mt19937(std::random_device()())); // 3rd parameter generated by Clang-Tidy
for (auto &i: Arr)
{
i->print_Var(); // OK
// auto z = i->Var // no member named Var in A
// obviously base class does not contain such variable
// if (i->Var==20) {/* do something*/}
// if (i->Var=="Hello Stackoverflow") {/* do something*/}
}
}
Explanation:
I want to iterate over array of pointers to A, which is filled with pointers to classes derived from A, and depending on what type is variable Var, do some if( ) statement.
Problem is that i cannot access Var, cause its not member of base class. However, it's possible to cout those values by, for example, overloaded function returning void. Could i write function in A class that returns templated type? like:
class A
{
<class T> GetVar()
}
Besides, I feel like I'm dealing with this problem in totally improper way. Can i mix templates and inheritance like that? If not, how should it be designed?
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.
I think it will be the easiest way. Just move the comparison method to the interface and override it in derived classes. Add the following lines to yor example:
class A
{
/*..................................................*/
virtual bool comp(const int) const { return false; }
virtual bool comp(const std::string) const { return false; }
virtual bool comp(const double) const { return false; }
};
template<class T>
class B : public A
{
/*..................................................*/
virtual bool comp(const T othr) const override { return othr == Var; }
};
void foo()
{
/*..................................................*/
if (i->comp(20))
{
/* do something*/
}
if (i->comp("Hello Stackoverflow"))
{
/* do something*/
}
/*..................................................*/
}
Related
I am trying to pass a CRTP type parameter to a virtual method. Consequently, the virtual method would need to be a template. However, this is not allowed by C++ (yet?), because it would mean that the size of the vtable -- the common way how compilers implement dynamic dispatch -- is unknown until all sources have been compiled and are being linked. (I found this reasoning during my search on SO.)
In my particular setting, however, there is a finite and known amount of CRTP specializations. Hence, it is possible to define a virtual method overload per specialization and override these in the subclasses. I have prepared a small MWE to demonstrate my situation. Consider the following CRTP hierarchy:
template<typename Actual>
struct CRTPBase
{
using actual_type = Actual;
void foo() { static_cast<actual_type*>(this)->foo(); }
int bar(int i) const { return static_cast<const actual_type*>(this)->bar(i); }
};
struct A : CRTPBase<A>
{
void foo() { /* do something A would do */ }
int bar(int i) const { return i + 1; }
};
struct B : CRTPBase<B>
{
void foo() { /* do something B would do */ }
int bar(int i) const { return i - 1; }
};
Next, I want to define a virtual class hierarchy with a virtual method to handle all specializations of CRTPBase<T>. Because I know the particular specializations, I can do as follows:
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(const CRTPBase<A> &o) override { /* much logic to handle A */ }
void accept_crtp(const CRTPBase<B> &o) override { /* similar logic to handle B */ }
};
Observe that there is one virtual method per specialization of CRTPBase<T>, both in the purely virtual base and in all its derived classes. This overhead easily blows out of proportion with increasing number of specializations of CRTPBase<T> and more derived classes of VirtualBase.
What I would like to do, is roughly the following:
struct VirtualBase
{
virtual ~VirtualBase() { }
template<typename T> virtual void accept_crtp(const CRTPBase<T> &o) = 0;
}
struct VirtualDerived : VirtualBase
{
template<typename T> void accept_crtp(const CRTPBase<T> &o) override {
/* one logic to handle any CRTPBase<T> */
}
};
For the reason mentioned in the beginning, this is not possible. User Mark Essel has faced the same issue in another SO post (in an answer, not a question, though).
The user proposes to declare and define the virtual methods for each specialization, but in the derived classes implement the actual logic in an additional template, non-virtual method and then forward calls from the virtual methods to that template method:
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(const CRTPBase<A> &o) override { accept_any_crtp(o); }
void accept_crtp(const CRTPBase<B> &o) override { accept_any_crtp(o); }
private:
template<typename T>
void accept_any_crtp(const CRTPBase<T> &o) {
/* one logic to handle any CRTPBase<T> */
}
};
While this approach avoids code duplication of the logic to handle the CRTPBase<T> specializations, it still requires explicitly writing one method per specialization in the virtual base and all derived classes.
My question is: How can the implementation overhead be reduced?
I have considered using an X macro of the form
#define CRTP_SPECIALIZATIONS_LIST(X) X(A) X(B) // lists all specializations, here A and B
to generate the methods in the virtual base and derived classes. The problem with that is, if the CRTP hierarchy is defined in CRTP.hpp and the virtual base and derived classes are declared/defined in other source files, then the macro is "being leaked" by the header to all translation units that include it. Is there a more elegant way to solve this? Is there maybe a template way of achieving the same goal, perhaps with a variadic template type?
Your help is appreciated. Kind regards,
Immanuel
As all types are known, you might use std::variant to have a free visitor implementation:
using MyVariant =
std::variant<std::reference_wrapper<const CRTPBase<A>>,
std::reference_wrapper<const CRTPBase<B>>,
// ...
>
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(MyVariant) = 0;
};
struct VirtualDerived : VirtualBase
{
void accept_crtp(MyVariant var) override
{
std::visit([/*this*/](const auto& crtp){ /*...*/ }, var);
}
};
If you write a CRTP base with the different accept_crtp() overloads that all delegate to a derived class' method, that derived class' method can be a template. That CRTP base can also be used to implement a virtual base:
// declare virtual interface
struct VirtualBase
{
virtual ~VirtualBase() { }
virtual void accept_crtp(const CRTPBase<A> &o) = 0;
virtual void accept_crtp(const CRTPBase<B> &o) = 0;
};
// implement virtual interface by delegating to derived class generic method
template<typename DerivedType>
struct CRTPDerived : VirtualBase
{
using derived_type = DerivedType;
virtual void accept_crtp(const CRTPBase<A> &o)
{ static_cast<derived_type*>(this)->accept_any_crtp(o); }
virtual void accept_crtp(const CRTPBase<B> &o)
{ static_cast<derived_type*>(this)->accept_any_crtp(o); }
};
// implement generic method
struct VirtualDerived : CRTPDerived<VirtualDerived>
{
private:
template<typename T>
void accept_any_crtp(const CRTPBase<T> &o) {
/* one logic to handle any CRTPBase<T> */
}
};
I have found a convenient solution to my problem. It scales well, meaning that the amount of code grows linearly with the number of virtual methods (rather than having number of virtual methods times number of CRTP classes). Further, my solution resolves the actual type of CRTPBase<T> at compile time; no dynamic dispatch except for the virtual method call. Thanks to Ulrich Eckhardt for pointing me in the right direction with his idea of using CRTP in the class hierarchy of VirtualBase.
I will describe how to solve this for a single method. This process can then be repeated for each method. The idea is to generate a purely virtual method in the VirtualBase for each concrete type of CRTPBase<T> and to generate implementations of these methods in all derived classes. The problem with generating methods at compile time is that templates do not allow us to generate method names. The trick here is to exploit overloading semantics and use a tag type to perform tag dispatching.
Let me explain along the example. Given the CRTP hierarchy (note that i slightly changed it for demonstrational purpose)
template<typename Actual>
struct CRTPBase
{
using actual_type = Actual;
actual_type & actual() { return *static_cast<actual_type*>(this); }
const actual_type & actual() const {
return *static_cast<const actual_type*>(this);
}
void foo() const { actual().foo(); }
int bar(int i) const { return actual().bar(i); }
void baz(float x, float y) { actual().baz(x, y); }
};
struct A : CRTPBase<A>
{
void foo() const { }
int bar(int i) const { return i + 'A'; }
void baz(float x, float y) { }
};
struct B : CRTPBase<B>
{
void foo() const { }
int bar(int i) const { return i + 'B'; }
void baz(float x, float y) { }
};
we want to declare a virtual method bark() in the class hierarchy VirtualBase, that accepts any subclass of CRTPBase<T> as parameter. We create a helper tag type bark_t to enable overload resolution.
struct VirtualBase
{
private:
virtual void operator()(bark_t, const A&) const = 0;
virtual void operator()(bark_t, const B&) const = 0;
public:
template<typename T>
void bark(const T &o) const { operator()(bark_t{}, o); }
};
The template method is generic and calls to the proper operator() thanks to overload resolution. The tag type is used here to select the correct implementation. (We want to support multiple methods, not just bark().)
Next we define an implementation of operator() in the derived classes using CRTP:
template<typename Actual>
struct VirtualCRTP : VirtualBase
{
using actual_type = Actual;
actual_type & actual() { return *static_cast<actual_type*>(this); }
const actual_type & actual() const {
return *static_cast<const actual_type*>(this);
}
void operator()(bark_t{}, const A &o) const override { actual()(bark_t{}, o); }
void operator()(bark_t{}, const B &o) const override { actual()(bark_t{}, o); }
};
Note that the implementation calls to some method operator() of static type Actual. We need to implement this next in the implementations of VirtualBase:
struct VirtualDerivedX : VirtualCRTP<VirtualDerivedX>
{
template<typename T>
void operator()(bark_t, const T &o) { /* generic implementation goes here */ }
};
struct VirtualDerivedY : VirtualCRTP<VirtualDerivedY>
{
template<typename T>
void operator()(bark_t, const T &o) { /* generic implementation goes here */ }
};
At this point you might wonder "What did we gain here?". So far, we need to write one method operator() per actual type of CRTPBase<T>. Only in VirtualBase and VirtualCRTP, but still more than we want to write. The neat thing is, we can now generate methods operator(), both the purely virtual declarations in VirtualBase and the implementation in VirtualCRTP. To do so, I have defined a generic helper class. I put the full code with this helper class and the example on Godbolt.
We can use this helper class to declare new virtual methods that take as first parameter an instance of a list of types, as well as additional parameters. It also takes care of const-ness of the parameters and the methods.
struct bark_t : const_virtual_crtp_helper<bark_t>::
crtp_args<const A&, const B&>::
args<> { };
struct quack_t : virtual_crtp_helper<quack_t>::
crtp_args<const A&, const B&>::
args<int, float> { };
struct roar_t : const_virtual_crtp_helper<roar_t>::
crtp_args<A&, B&>::
args<const std::vector<int>&> { };
/*----- Virtual Class Hierarchy taking CRTP parameter ------------------------*/
struct VirtualBase : bark_t::base_type
, quack_t::base_type
, roar_t::base_type
{
virtual ~VirtualBase() { }
/* Declare generic `bark()`. */
using bark_t::base_type::operator();
template<typename T>
void bark(const T &o) const { operator()(bark_t{}, o); }
/* Declare generic `quack()`. */
using quack_t::base_type::operator();
template<typename T>
void quack(const T &o, int i, float f) { operator()(quack_t{}, o, i, f); }
/* Declare generic `roar()`. */
using roar_t::base_type::operator();
template<typename T>
void roar(T &o, const std::vector<int> &v) const { operator()(roar_t{}, o, v); }
};
template<typename Actual>
struct VirtualCRTP : VirtualBase
, bark_t::derived_type<Actual>
, quack_t::derived_type<Actual>
, roar_t::derived_type<Actual>
{ };
struct VirtualDerivedX : VirtualCRTP<VirtualDerivedX>
{
private:
/* Implement generic `bark()`. */
friend const_virtual_crtp_helper<bark_t>;
template<typename T>
void operator()(bark_t, const T&) const { /* generic bark() goes here */ }
/* Implement generic `quack()`. */
friend virtual_crtp_helper<quack_t>;
template<typename T>
void operator()(quack_t, const T&, int, float) { /* generic quack() goes here */ }
/* Implement generic `roar()`. */
friend const_virtual_crtp_helper<roar_t>;
template<typename T>
void operator()(roar_t, T&, const std::vector<int>&) const { /* generic roar() goes here */ }
};
struct VirtualDerivedY : VirtualCRTP<VirtualDerivedY>
{
private:
/* Implement generic `bark()`. */
friend const_virtual_crtp_helper<bark_t>;
template<typename T>
void operator()(bark_t, const T&) const { /* generic bark() goes here */ }
/* Implement generic `quack()`. */
friend virtual_crtp_helper<quack_t>;
template<typename T>
void operator()(quack_t, const T&, int, float) { /* generic quack() goes here */ }
/* Implement generic `roar()`. */
friend const_virtual_crtp_helper<roar_t>;
template<typename T>
void operator()(roar_t, T&, const std::vector<int>&) const { /* generic roar() goes here */ }
};
In the example I declare three helper types for the three methods I want to implement. The base_type introduces the purely virtual methods to VirtualBase and the derived_type<Actual> imports the implementations of these methods. To do so, I use virtual inheritance to resolve the occuring dreaded diamond ;)
One downside is that one has to declare virtual_crtp_helper types as friend in the derived classes. Maybe someone knows how to avoid that?
To sum up: To add a method to the class hierarchy, one has to
Declare a helper type for the method using virtual_crtp_helper<T> or const_virtual_crtp_helper<T>.
Have VirtualBase inherit from this type's base_type and define the method as generic template.
Have VirtualCRTP<Actual> inherit from the helper type's derived_type<Actual>.
For each derived class, implement the actual logic in templated and tagged operator().
I am happy to hear your thoughts and am looking forward to improvements.
Immanuel
class A
{
friend void foo();
virtual void print_Var() const{};
};// does not contain variable Var;
template<class T>
class B : public A
{
T Var;
public:
B(T x):Var(x){}
void print_Var() const override
{
std::cout<<Var<<std::endl;
}
};
void foo()
{
std::array<std::unique_ptr<A>, 3> Arr = {
std::make_unique<B<int>>(100),
std::make_unique<B<int>>(20),
std::make_unique<B<std::string>>("Hello Stackoverflow")
};
std::shuffle(Arr.begin(), Arr.end(), std::mt19937(std::random_device()())); // 3rd parameter generated by Clang-Tidy
for (auto &i: Arr)
{
i->print_Var(); // OK
// auto z = i->Var // no member named Var in A
// obviously base class does not contain such variable
// if (i->Var==20) {/* do something*/}
// if (i->Var=="Hello Stackoverflow") {/* do something*/}
}
}
Explanation:
I want to iterate over array of pointers to A, which is filled with pointers to classes derived from A, and depending on what type is variable Var, do some if( ) statement.
Problem is that i cannot access Var, cause its not member of base class. However, it's possible to cout those values by, for example, overloaded function returning void. Could i write function in A class that returns templated type? like:
class A
{
<class T> GetVar()
}
Besides, I feel like I'm dealing with this problem in totally improper way. Can i mix templates and inheritance like that? If not, how should it be designed?
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.
I think it will be the easiest way. Just move the comparison method to the interface and override it in derived classes. Add the following lines to yor example:
class A
{
/*..................................................*/
virtual bool comp(const int) const { return false; }
virtual bool comp(const std::string) const { return false; }
virtual bool comp(const double) const { return false; }
};
template<class T>
class B : public A
{
/*..................................................*/
virtual bool comp(const T othr) const override { return othr == Var; }
};
void foo()
{
/*..................................................*/
if (i->comp(20))
{
/* do something*/
}
if (i->comp("Hello Stackoverflow"))
{
/* do something*/
}
/*..................................................*/
}
class A
{
friend void foo();
virtual void print_Var() const{};
};// does not contain variable Var;
template<class T>
class B : public A
{
T Var;
public:
B(T x):Var(x){}
void print_Var() const override
{
std::cout<<Var<<std::endl;
}
};
void foo()
{
std::array<std::unique_ptr<A>, 3> Arr = {
std::make_unique<B<int>>(100),
std::make_unique<B<int>>(20),
std::make_unique<B<std::string>>("Hello Stackoverflow")
};
std::shuffle(Arr.begin(), Arr.end(), std::mt19937(std::random_device()())); // 3rd parameter generated by Clang-Tidy
for (auto &i: Arr)
{
i->print_Var(); // OK
// auto z = i->Var // no member named Var in A
// obviously base class does not contain such variable
// if (i->Var==20) {/* do something*/}
// if (i->Var=="Hello Stackoverflow") {/* do something*/}
}
}
Explanation:
I want to iterate over array of pointers to A, which is filled with pointers to classes derived from A, and depending on what type is variable Var, do some if( ) statement.
Problem is that i cannot access Var, cause its not member of base class. However, it's possible to cout those values by, for example, overloaded function returning void. Could i write function in A class that returns templated type? like:
class A
{
<class T> GetVar()
}
Besides, I feel like I'm dealing with this problem in totally improper way. Can i mix templates and inheritance like that? If not, how should it be designed?
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.
I think it will be the easiest way. Just move the comparison method to the interface and override it in derived classes. Add the following lines to yor example:
class A
{
/*..................................................*/
virtual bool comp(const int) const { return false; }
virtual bool comp(const std::string) const { return false; }
virtual bool comp(const double) const { return false; }
};
template<class T>
class B : public A
{
/*..................................................*/
virtual bool comp(const T othr) const override { return othr == Var; }
};
void foo()
{
/*..................................................*/
if (i->comp(20))
{
/* do something*/
}
if (i->comp("Hello Stackoverflow"))
{
/* do something*/
}
/*..................................................*/
}
As far as I know, templated virtual functions aren't allowed/possible due to the undefined size of the vtable.
On the other hand, virtual functions inside a class template which don't use the template type seem to be allowed, right?
What about a virtual function that doesn't use the template type as parameter or return type but works on data of the template type? Would that be valid C++?
I have already done some testing and it seems to work.
My Code looks like this:
(Note: For reasons of readability this is only the basic structure, not the real code).
template<typename T>
class Base {
public:
virtual bool compare(void) {
// Basic implementation
return ((value1 + value2) < value3);
}
protected:
T value1, value2, value3;
}
/**
* Derived from Base<ComplexClass> where
* ComplexClass is a Class providing
* a int Value through .getInt()
**/
class Derived : Base<ComplexClass> {
bool compare(void) {
return ((value1.getInt() + value2.getInt()) < value3.getInt());
}
}
main {
Base<int> *intBase = new Base<int>();
Base<double> *doubleBase = new Base<double>();
Base<ComplexClass> *complexBase = new Derived();
intBase->compare(); // Should call base function
doubleBase->compare(); // Should also call base function
complexBase->compare(); // Should call the compare function of Derived
}
As far as i can tell this works like I excepted. Is this just a lucky coincidence or is this valid/good C++ style?
If it's valid, could someone please explain what's happening inside and why some people say it's forbidden/bad practice to derive from class templates and use virtual functions inside of class templates?
Thank you in advance!
PS: I know something similar could have been done by template specialization but I'd like to know if it's also possible this way.
Q As far as I know, templated virtual functions aren't allowed/possible due to the undefined size of the vtable.
A You can have virtual function in class templates.
Example code that compiles and links:
template <typename T>
struct Base
{
virtual T doSomething(T const& in) = 0;
Base(T const& data) : data_(data) {}
T data_;
};
struct Concrete : public Base<int>
{
Concrete(int d) : Base(d) {}
virtual int doSomething(int const& in)
{
return data_*in;
}
};
int main()
{
Concrete a(20);
int b = a.doSomething(10);
}
Q On the other hand, virtual functions inside a class template which don't use the template type seem to be allowed, right?
A The virtual functions of a class template can use anything -- not restricted to not using the template tye.
My example should make that clear.
Q What about a virtual function that doesn't use the template type as parameter or return type but works on data of the template type? Would that be valid C++?
A Yes, it will.
Again, my example should make that clear.
EDIT: Extended example
template <typename T>
struct Base
{
virtual T fun1(T const& in) = 0;
virtual T fun2(int in) = 0;
virtual int fun3(T const& in) = 0;
virtual int fun4(int in) = 0;
Base(T const& data) : data_(data) {}
T data_;
};
struct Concrete : public Base<int>
{
Concrete(int d) : Base(d) {}
virtual int fun1(int const& in)
{
return data_*in;
}
virtual int fun2(int in)
{
return fun1(in);
}
virtual int fun3(int const& in)
{
return fun1(in);
}
virtual int fun4(int in)
{
return fun1(in);
}
};
int main()
{
Concrete a(20);
int b = a.fun1(10);
int c = a.fun2(10);
int d = a.fun3(10);
int e = a.fun4(10);
}
This is perfectly valid. However, here you can have the same behaviour with specialization or just overloading, e.g.
template<typename T>
struct Base
{
bool compare() const { return val(value1) + val(value2) < val(value3); }
protected:
T value1, value2, value3;
private:
template<typename U>
static U val(U a) { return a; }
static int val(const ComplexClass& a) { return a.getInt(); }
};
Better keep virtual functions for when it's really needed.
And try to gather as much as possible shared code in a single place, minimizing what is to be specialized.
Please consider the following (simplified) class hierarchy and processing functions:
struct msgBase
{
virtual int msgType() const=0;
};
struct msgType1:public msgBase
{
virtual int msgType() const{return 1;}
};
struct msgType2:public msgBase
{
virtual int msgType() const {return 2;}
};
void process(const msgType1& mt1)
{
// processing for message type 1
}
void process(const msgType2& mt2)
{
// processing for message type 2
}
void process(const msgBase& mbase)
{
switch(mbase.msgType())
{
case 1:
process(static_cast<const msgType1&>(mbase));
break;
case 2:
process(static_cast<const msgType2&>(mbase));
break;
}
}
In an integrated design, msgBase would be given a virtual "process" method, to avoid needing to iterate over the types.
If it's not possible or desirable to modify any of the classes, are there any alternatives to iterating over the types?
I've experimented with a decorator/factory pattern where a parallel hierarchy of classes encapsulates the given classes, and implements the necessary virtual functions, but this results in an awful lot of boilerplate, and the factory function still needs to iterate over the types!
I could replace the switch statement with a series of dyamic_casts, but that still leaves the same weaknesses.
As requested by Simon, here is what I mean by CRTP:
typedef <class Derived>
struct msgBase
{
virtual void process(){
// redirect the call to the derived class's process()
static_cast<Derived*>(this) -> process();
};
struct msgType1:public msgBase<msgType1>
{
void process(){
// process as per type-1
}
};
struct msgType2:public msgBase<msgType1>
{
void process(){
// process as per type-2
}
};
What's happening here? Consider this case:
msgBase* msg = new msgType1();
msg->process();
normally (without CRTP) this would only call msgBase::process(). But now, msgBase "knows" about msgType1 using the template, so it is redirected to msgType1::process at compile time.
Something like this could work:
These classes are used to do the casting automatically:
struct dispatcher_base {
virtual void process(const msgBase&) = 0;
};
template <class T>
struct dispatcher_impl : dispatcher_base {
void process(const msgBase& b) override {
::process(static_cast<const T&>(b));
}
};
We'll store them in a map:
auto g_table = std::map<int, std::unique_ptr<dispatcher_base>>{};
But now you have to initialize this table somewhere:
template <class T>
void register_msg() {
g_table[T{}.msgType()].reset(new dispatcher_impl<T>{});
}
...
register_msg<msgType1>();
register_msg<msgType2>();
You can add an assert to register_msg to make sure that msgTypes are unique.
Your process function will look like this:
void process(const msgBase& b) {
assert(g_table.find(b.msgType()) != g_table.end());
g_table[b.msgType()]->process(b);
}
You can replace assert with any other logic of course.
If you can't modify the classes then you can use decorators to get polymorphic type deduction.
struct DecorBase {
DecorBase(msgBase& b) : b_(b) {}
virtual ~DecorBase() {}
virtual void process() = 0;
msgBase& b_;
};
struct DecorType1 : public DecorBase {
DecorType1(msgType1& t1) : DecorBase(t1) {}
void process() override {
std::cout << "Processing Type 1" << std::endl;
}
};
struct DecorType2 : public DecorBase {
DecorType2(msgType2& t2) : DecorBase(t2) {}
void process() override {
std::cout << "Processing Type 2" << std::endl;
}
};
And use it like this:
msgType1 t1;
msgType2 t2;
DecorType1 dt1(t1); // Wrap objects in respective decorator.
DecorType2 dt2(t2);
DecorBase& base = dt2;
base.process(); // Uses polymorphism to call function in derived type.
This will require you to write a decorator for every derived type but at least you don't have to iterate over all types during the function call.