Skip to content

Instantly share code, notes, and snippets.

@artivis
Last active November 2, 2018 11:58
Show Gist options
  • Save artivis/f104e6d7d37c11bef493148f400235c2 to your computer and use it in GitHub Desktop.
Save artivis/f104e6d7d37c11bef493148f400235c2 to your computer and use it in GitHub Desktop.
[c++] CRTP with default impl
#include <iostream>
#include <type_traits>
#define USE_DEFAULT
// 2 different traits to test if a class has the function 'process'
template<typename T>
struct HasProcess
{
template<typename U, void (U::*)(int)> struct SFINAE {};
template<typename U> static char Test(SFINAE<U, &U::process>*);
template<typename U> static int Test(...);
static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};
template<class, typename T> struct has_process_impl : std::false_type {};
template<typename T> struct
has_process_impl<decltype( void(std::declval<T>().process(int(0))) ), T> : std::true_type {};
template<typename T> struct has_process : has_process_impl<void, T> {};
// A base crtp class.
// It uses sfinae to evaluate whether Derived has the function 'process'.
// If it does, call the derived implementation
// If it does not, call the base implementation
template <typename Derived>
struct CrtpBase
{
#ifdef USE_DEFAULT
template <typename U=Derived>
typename std::enable_if<!has_process<U>::value, void>::type
process (int i)
{
std::cout << "[Base] value: " << i <<"\n";
}
template <typename U=Derived>
typename std::enable_if< has_process<U>::value, void>::type
process (int i)
{
return derived().process(i);
}
#else
void process (int i)
{
return derived().process(i);
}
#endif
protected:
Derived& derived() { return static_cast<Derived&>(*this); }
const Derived& derived() const { return static_cast<Derived&>(*this); }
};
struct DerivedA : CrtpBase<DerivedA>
{
void process (int i)
{
std::cout << "[DerivedA] value: " << i << "\n";
}
};
struct DerivedB : CrtpBase<DerivedB> {};
// Some dummy classes to test the traits
struct Foo
{
void process (int i)
{
std::cout << "Value: " << i << "\n";
}
};
struct Bar {};
template <typename Derived>
void caller(CrtpBase<Derived>& obj)
{
obj.process(1667);
}
template <typename T>
void caller2(T& obj)
{
obj.process(7);
}
int main() {
DerivedA da;
da.process(42);
caller(da);
caller2(da);
#ifdef USE_DEFAULT
DerivedB db;
// calling those without the default impl
// results in an infinite loop.
db.process(42);
caller(db);
caller2(db);
#endif
std::cout << "DerivedA HasProcess: " << HasProcess<DerivedA>::Has << "\n";
std::cout << "DerivedB HasProcess: " << HasProcess<DerivedB>::Has << "\n";
std::cout << "FooHasProcess: " << HasProcess<Foo>::Has << "\n";
std::cout << "Bar HasProcess: " << HasProcess<Bar>::Has << "\n";
std::cout << "DerivedA has_process: " << has_process<DerivedA>::value << "\n";
std::cout << "DerivedB has_process: " << has_process<DerivedB>::value << "\n";
std::cout << "Foo has_process: " << has_process<Foo>::value << "\n";
std::cout << "Bar has_process: " << has_process<Bar>::value << "\n";
/*
With USE_DEFAULT out is
[DerivedA] value: 42
[DerivedA] value: 1667
[DerivedA] value: 7
[Base] value: 42
[Base] value: 1667
[Base] value: 7
DerivedA HasProcess: 1
DerivedB HasProcess: 0
FooHasProcess: 1
Bar HasProcess: 0
DerivedA has_process: 1
DerivedB has_process: 0 <== Humm how come ?
Foo has_process: 1
Bar has_process: 0
Without USE_DEFAULT out is
[DerivedA] value: 42
[DerivedA] value: 1667
[DerivedA] value: 7
DerivedA HasProcess: 1
DerivedB HasProcess: 0
FooHasProcess: 1
Bar HasProcess: 0
DerivedA has_process: 1
DerivedB has_process: 1 <== Humm how come ?
Foo has_process: 1
Bar has_process: 0
*/
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment