Default fallback for C++ template functions using enable_if - c++

I want to write a C++ mechanism, where different instantiations of a function are called if a given class Param is derived from a certain base class.
This works pretty nicely with std::is_base_of and std::enable_if.
However, I would like to have a "default version" of this doStuff() function that is called for "every other class".
This would basically work by doing something like "if Param is not derived from A and if not derived from B", but I wonder whether there is a more elegant solution.
#include <iostream>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() {std::cout << "Base A" << std::endl;};
template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() {std::cout << "Base X" << std::endl;};
int main()
{
doStuff<B>();
doStuff<Y>();
// doStuff<Other>(); this is neither derived from B and Y, so call the default case
}
The solution should work with C++14.

When using std:::enable_if, you will have to provide a 3rd SFINAE'd overload that handles the default conditions which are not handled by the other overloads, eg:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() { std::cout << "Base A" << std::endl; }
template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() { std::cout << "Base X" << std::endl; }
template <typename Param, std::enable_if_t<!(std::is_base_of<A, Param>::value || std::is_base_of<X, Param>::value), bool> = true>
void doStuff() { std::cout << "Something else" << std::endl; }
int main()
{
doStuff<B>(); // prints "Base A"
doStuff<Y>(); // prints "Base X"
doStuff<Other>(); // prints "Something else"
}
Online Demo
That being said, in C++17 and later, you can use if constexpr instead, which is cleaner than using SFINAE in this situation, eg:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param>
void doStuff() {
if constexpr (std::is_base_of_v<A, Param>)
std::cout << "Base A" << std::endl;
else if constexpr (std::is_base_of_v<X, Param>)
std::cout << "Base X" << std::endl;
else
std::cout << "Something else" << std::endl;
}
int main()
{
doStuff<B>(); // prints "Base A"
doStuff<Y>(); // prints "Base X"
doStuff<Other>(); // prints "Something else"
}
Online Demo

Or if you can use C++17 or later use constexpr, a lot more readable then SFINAE
#include <type_traits>
#include <iostream>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template<typename type_t>
void doStuff()
{
if constexpr (std::is_base_of_v<A,type_t>)
{
std::cout << "Base A\n";
}
else
if constexpr (std::is_base_of_v<X, type_t>)
{
std::cout << "Base X\n";
}
else
{
std::cout << "Other\n";
}
};
int main()
{
doStuff<B>();
doStuff<Y>();
doStuff<Other>(); //this is neither derived from B and Y, so call the default case
return 0;
}

Related

Statically get base class in C++

Is there a way to statically (i.e. at compile time) get the base class of a given class?
That is, given:
class Base {};
class Derived1: public Base {};
class Derived2: public Derived1 {};
class Derived3: public Derived2 {};
For example is_base_of<Base,Derived3> gives true.
I'd like to know if there is something like base_of<Derived3> that would give Base.
In case, there is a solution (which I doubt), I'd also like to know if there is a possibility to get the direct child of Base. For example something like base_child_of<Derived3> would give Derived1.
ADDENDUM
Here is a not so beautiful solution but it does the job. Especially, I do not like to have to write two time the class parent name (for example: class Derived2 : public Derived1 { HIERARCHY(Derived1) }; but I didn't find any solution that doesn't impair the curly brackets in most IDE editors, by adding one in the define):
#include <iostream>
#include <type_traits>
#define HIERARCHY(PARENT) public: using inherited = PARENT; using base = inherited::base;
class Base { public: using base = Base; };
class Derived1 : public Base { HIERARCHY(Base ) };
class Derived2 : public Derived1 { HIERARCHY(Derived1) };
class Derived3 : public Derived2 { HIERARCHY(Derived2) };
// Expands to:
// class Base { public: using base = Base; };
// class Derived1 : public Base { public: using inherited = Base; using base = inherited::base; };
// class Derived2 : public Derived1 { public: using inherited = Derived1; using base = inherited::base; };
// class Derived3 : public Derived2 { public: using inherited = Derived2; using base = inherited::base; };
// Not really needed anymore, now CLASS::base does the stuff
// (I leave it here to illustrate how a static loop/recursion may be performed)
template <typename CLASS>
struct base_of {
using type = typename std::conditional<
std::is_same<CLASS, Base>::value,
CLASS,
typename std::conditional<
std::is_base_of<typename CLASS::base, CLASS>::value,
typename base_of<typename CLASS::inherited>::type,
CLASS
>::type
>::type;
};
template<>
struct base_of<Base> {
using type = Base;
};
template <typename CLASS>
struct direct_child_of_base_of {
using type = typename std::conditional<
std::is_same<typename CLASS::inherited, typename CLASS::base>::value,
CLASS,
typename direct_child_of_base_of<typename CLASS::inherited>::type
>::type;
};
template<>
struct direct_child_of_base_of<Base> {
using type = void;
};
int main() {
std::cout << "dbase_of<Derived3>: " << typeid(base_of<Derived3>::type).name() << std::endl;
std::cout << "dbase_of<Derived2>: " << typeid(base_of<Derived2>::type).name() << std::endl;
std::cout << "dbase_of<Derived1>: " << typeid(base_of<Derived1>::type).name() << std::endl;
std::cout << "dbase_of<Base >: " << typeid(base_of<Base >::type).name() << std::endl;
std::cout << std::endl;
// This is equivalent
std::cout << "Derived3::base: " << typeid(Derived3::base).name() << std::endl;
std::cout << "Derived2::base: " << typeid(Derived2::base).name() << std::endl;
std::cout << "Derived1::base: " << typeid(Derived1::base).name() << std::endl;
std::cout << "Base ::base: " << typeid(Base ::base).name() << std::endl;
std::cout << std::endl;
std::cout << "direct_child_of_base_of<Derived3>: " << typeid(direct_child_of_base_of<Derived3>::type).name() << std::endl;
std::cout << "direct_child_of_base_of<Derived2>: " << typeid(direct_child_of_base_of<Derived2>::type).name() << std::endl;
std::cout << "direct_child_of_base_of<Derived1>: " << typeid(direct_child_of_base_of<Derived1>::type).name() << std::endl;
std::cout << "direct_child_of_base_of<Base >: " << typeid(direct_child_of_base_of<Base >::type).name() << std::endl;
return 0;
}
The output is (with GCC):
base_of<Derived3>: 4Base
base_of<Derived2>: 4Base
base_of<Derived1>: 4Base
base_of<Base >: 4Base
Derived3::base: 4Base
Derived2::base: 4Base
Derived1::base: 4Base
Base ::base: 4Base
direct_child_of_base_of<Derived3>: 8Derived1
direct_child_of_base_of<Derived2>: 8Derived1
direct_child_of_base_of<Derived1>: 8Derived1
direct_child_of_base_of<Base >: v
This is possible, with some limitations, by using stateful metaprogramming to record the type deduced for a universal conversion operator used to construct the subobjects of the derived type in aggregate initialization (assuming it is an aggregate). For example, we can write:
template<class T>
struct first_direct_base {
template<class U>
struct Y {
friend auto f(first_direct_base*) { return U(); }
};
friend auto f(first_direct_base*);
struct A {
template<class U, int = sizeof(Y<U>)>
operator U();
};
using type = decltype(T{A{}}, f((first_direct_base*)0));
};
template<class T>
using first_direct_base_t = first_direct_base<T>::type;
Demo.
For a fuller explanation of this technique, see http://alexpolt.github.io/type-loophole.html
If you're happy with using manual reflection, I'd recommend Boost.Describe. For example, with the above you might write:
class Base { BOOST_DESCRIBE_CLASS(Base, (), (), (), ()) };
class Derived1: public Base { BOOST_DESCRIBE_CLASS(Derived1, (Base), (), (), ()) };
template<class T>
using first_direct_base_t = boost::mp11::mp_front<
boost::describe::describe_bases<T, boost::describe::mod_public>>;
Currently no, not in standard C++.
There are reflection proposals that are being worked on. The original plan was to have them published in c++23, but between an improved syntax proposal and the Covid-19 pandemic, it was postponed.
When reflection arrives, this will be possible. The syntax was still in flux last I checked. Either c++26 or c++29.

How to call function in primary template for a specialization of the class template?

If I have a normal function in a class template, when I specialize the class template, could I call the normal function as well?
Such as I have a template class
template<int i>
class A
{
public:
void func() {
std::cout << "A" << std::endl;
}
};
And its specialization
template<>
class A<1>
{
public:
void func1() {
cout << "A1" << endl;
}
};
Now in main
int main()
{
auto a1 = A<1>();
a1.func1();
a1.func(); //<--- here
}
How could I call a1.func()?
The class A<1> doesn't have a member func. The primary template is ignored when you specialise. If you want it to behave like inheritance, you could use inheritance.
class BaseA
{
public:
void func() {
std::cout << "A" << std::endl;
}
};
template<int>
class A : public BaseA {};
template<>
class A<1> : public BaseA
{
public:
void func1() {
cout << "A1" << endl;
}
};
Now A<1> inherits func from BaseA, and A<2> doesn't have func1, but does have func.

Virtual function with non-shared method

I'm on a personal project and I need to do something unusual. My code is kinda long but the problem comes from the structure so I'll use a very simplified version of the problem.
I have two classes (A and B), with B derived from A.
B uses every attributes and methods of A, including one which creates a modified
clone of the instance of the class.
The problem is that I need to be able to use a method of B after cloning (moo in this case) that doesn't exists in A. I tried to make my methods virtual but it doesn't fix the problem. Is there any way to do this without CRTP ?
I really don't want to use CRTP because it would be really complicated.
(In my real code I have a chain of 4 class inheritances, and all 4 are already templated)
#include <iostream>
class A
{
public:
/*Common function*/
virtual void foo(){
std::cout << "call from A" << std::endl;
}
A* clone(){
/*
...
... Code I don't want to write again for B
...
*/
return this;
}
};
class B: public A
{
public:
/*Common function*/
virtual void foo(){
std::cout << "call from B" << std::endl;
}
/*Not derived from A*/
void moo(){ //Doesn't work even with virtual keyword
std::cout << "only exist in B" << std::endl;
}
};
int main(int argc, char const *argv[])
{
auto tB = new B();
tB->foo();
tB->moo();
tB->clone()->foo();
tB->clone()->moo();
return 0;
}
Compilator:
error: 'class A' has no member named 'moo'; did you mean 'foo'?
38 | tB->clone()->moo();
| ^~~
| foo
I'm not English so sorry if it's unclear.
Well, according to the comments, my research and how I think c++ works, I give up finding something that looks like virtual methods and still be satisfying.
So I resolved to use the CRTP, for those who are interested here's the code of my model of a 3 (I deleted one) inherited class CRTP with an additional type template argument :)
#include <iostream>
//CRTPI = "CRTP Interface", used for inheritances between CRTPs
template<typename Derived, typename T>
class CRTPI_A
{
protected:
T x = 0;
public:
T getX(){
return x;
}
Derived* clone(){
/*
...
... Code I don't want to write again for B and it's childs
...
*/
return new Derived();
}
};
template<typename Derived, typename T>
class CRTPI_B: public CRTPI_A<Derived, T>
{
public:
//Only for B and its childs
void iwd(){
std::cout << "Hi, i'm a B child !" << std::endl;
}
};
template<typename Derived, typename T>
class CRTPI_C: public CRTPI_B<Derived, T>{};
template<typename T>
class A: public CRTPI_A<A<T>, T>
{
public:
A(){};
A(T z){
this->x = z;
}
void foo(){
std::cout << "call from A" << std::endl;
}
};
template<typename T>
class B: public CRTPI_B<B<T>, T>
{
public:
B(){};
B(T z){
this->x = z;
}
void foo(){
std::cout << "call from B" << std::endl;
}
//Not in CRTP interface so won't be inherited by C
void UwU(){
std::cout << "I'm exclusive to B" << std::endl;
}
};
template<typename T>
class C: public CRTPI_C<C<T>, T>
{
public:
C(){};
C(T z){
this->x = z;
};
void foo(){
std::cout << "call from C" << std::endl;
}
};
int main(int argc, char const *argv[])
{
auto tA = new A<char>('A');
auto tB = new B<int>(2);
auto tC = new C<float>(420.69);
tA->foo();
tA->clone()->foo();
printf("\n");
tB->foo();
tB->iwd();
tB->clone()->foo();
tB->clone()->iwd();
tB->UwU();
printf("\n");
tC->foo();
tC->iwd();
// tC->UwU(); //Won't work but that's planned
std::cout << "\n" << tA->getX() << ":" << tB->getX() <<":" << tC->getX() << std::endl;
return 0;
}
Note that, here, the CRTP interface for C is optional because it has no child class and no exclusive methods, so we can just write:
template<typename T>
class C: CRTPI_B<C<T>, T>
{
...
}

SFINAE : Derived class hide base class function depend on T

There are 2 SFINAE snippets that I coded.
They do exactly the same thing.
However, the first one works, while the second doesn't.
Why? (The second is more similar to my real program.)
This code works ( http://coliru.stacked-crooked.com/a/50e07af54708f076 )
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
template<EN T1> class B{
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN1,void>::type test(){ std::cout<<"1"<<std::endl;}
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN2,void>::type test(){ std::cout<<"2"<<std::endl; }
};
int main(){
B<EN1> b;
b.test();
}
But this code is uncompilable (http://coliru.stacked-crooked.com/a/28b6afd443b36c7e) :-
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
class Base{
public: void test(){
std::cout<<"1"<<std::endl;
};
};
template<EN T1> class B : public Base{
public: template<EN enLocal=T1>
std::enable_if_t< enLocal==EN2,void > test(){
std::cout<<"2"<<std::endl;
}
};
int main(){
B<EN1> bEn1; bEn1.test(); //should print 1
//B<EN2> bEn2; bEn2.test(); //print 2
}
I am very new to SFINAE and still learning it via https://stackoverflow.com/a/50562202/.
This code has two issues:
When invoking bEn1.test(); or bEn2.test(); compiler will figure out that name test refers to the function in class B and the set of overloaded functions will include only B::test. This can be fixed by brining name from the base class into derived class:
template<EN T1> class B : public Base{
public: using Base::test;
However now non-template function will be preferred over template function (even when enable_if works) so B<EN2> bEn2; bEn2.test(); will print 1.
In order to make this work again you can introduce yet another overload similar to one in the first example that will invoke function from the base class instead of bringing Base::test name into derived class:
public: template<EN enLocal=T1>
std::enable_if_t< enLocal!=EN2,void > test(){
return Base::test();
}
Another possible C++17-style workaround utilizing if constexpr instead of type traits or SFINAE:
public: template<EN enLocal = T1> void
test()
{
if constexpr(EN2 == enLocal)
{
std::cout<<"2"<<std::endl;
}
else
{
Base::test();
}
}
Depending on what is the real use case, you could also consider some form of tag dispatching:
enum class En {
base, a, b, c
};
template<En Type> void test_impl()
{
if constexpr (Type == En::base)
std::cout << "Base\n";
else if constexpr (Type == En::a)
std::cout << "1\n";
else if constexpr (Type == En::b)
std::cout << "2\n";
else
std::cout << "Default\n";
}
struct Base {
void test() {
std::cout << "Base - ";
test_impl<En::base>();
}
};
template<En Type>
struct Derived : public Base {
void test() {
std::cout << "Derived - ";
test_impl<Type>();
}
};
int main()
{
Base b;
b.test(); // -> "Base - Base"
Derived<En::a> b1;
b1.test(); // -> "Derived - 1"
Derived<En::b> b2;
b2.test(); // -> "Derived - 2"
Derived<En::base> b3;
b3.test(); // -> "Derived - Base"
Derived<En::c> b4;
b4.test(); // -> "Derived - Default"
}

Template specialization and inheritance

Suppose I have a template class with a lot of functions and I want to specialize them to change only a few of them and keep the other ones exactly as specified in the base template class.
How can I do that?
Below is what I want to achieve, but the solution isn't good, because it does not allow me to refer to the specialization for int as Base<int> – I need to use IntSpec for that.
#include <iostream>
using namespace std;
template<typename T>
struct Base
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
struct IntSpec : public Base<int>
{
void print2() {cout << "Base<int>::print2()" << endl;};
};
int main()
{
Base<double> d;
// Base<int> i; <-- I want this kind of instantiation
IntSpec i;
d.print1();
d.print2();
i.print1();
i.print2();
}
The output is:
Base::print1
Base::print2
Base::print1
Base<int>::print2()
Nicol's solution works fine, but this is an alternative:
template<typename T>
struct Base
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
template<>
void Base<int>::print2() {cout << "Base<int>::print2()" << endl;};
That way you can specialize only specific member functions and still use those that you haven't specialized(in this case, print1) without any problem. So now you'd use it just like you wanted:
Base<int> i;
i.print1();
i.print2(); // calls your specialization
Demo here.
You just have to use two template classes:
template<typename T>
struct CommonBase
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
template<typename T>
struct Base : public CommonBase<T>
{
};
template<>
struct Base<int> : public CommonBase<int>
{
void print2() {cout << "Base::print2" << endl;};
};
You always use Base, rather than CommonBase.
Another solution would be to add a level of indirection in the function you want to redefine, i.e.
template<typename T>
struct foo
{
template<typename T2>
void bar_impl()
{
//generic function
}
void bar()
{
bar_impl<T>();
}
};
Then you can specialize each function individually for each type or specialize the whole type as wanted.