Skip to content

Instantly share code, notes, and snippets.

@v4hn
Last active April 9, 2020 18:39
Show Gist options
  • Save v4hn/d3bbe8db3a8f2940002551b88b991033 to your computer and use it in GitHub Desktop.
Save v4hn/d3bbe8db3a8f2940002551b88b991033 to your computer and use it in GitHub Desktop.
SFINAE solutions to call a method if it exists *but simply omitting the call otherwise*
/* Given two classes, the challenge is to find a nice way to call
* its `run()` function if it exists, but omit the call otherwise */
struct HasRun {
void run(){};
};
struct HasNoRun {
//void run(){};
};
/* All reasonable solutions I found involve void_t which is standard only in C++17, but trivial to define */
#include <type_traits>
/* c++17 defines std::void_t */
template< class ... > using void_t = void;
/* 1. branch based on void_t and overload resolution */
template<typename, typename = void> struct has_run : std::false_type {};
template<typename T> struct has_run<T, void_t<decltype(&T::run)> > : std::true_type {};
template <typename T> inline auto LOOP_RUN(T& loop, std::true_type){ loop.run(); };
template <typename T> inline void LOOP_RUN(T& loop, std::false_type){};
/* 2. Get rid of the auxiliary function, as this is specialized and does not need to return a boolean */
template<typename T, typename = void> struct run { void operator()(T&){}; };
template<typename T> struct run<T, void_t<decltype(&T::run)> > { void operator()(T& t){t.run();} };
/* 3. Provide a mix-in template to do the same job */
template <typename T, typename = void> struct HasRunPatched : public T { using T::T; void run(){}; };
template<typename T> struct HasRunPatched<T, void_t<decltype(&T::run)> > : public T { using T::T; };
int main(int argc, char** argv)
{
// usage:
HasRun hr;
HasNoRun hnr;
// 1.
LOOP_RUN(hr, has_run<decltype(hr)>());
LOOP_RUN(hnr, has_run<decltype(hnr)>());
// 2.
run<HasRun>{}(hr);
run<HasNoRun>{}(hnr);
// 3.
HasRunPatched<HasRun> hrphr;
hrphr.run();
HasRunPatched<HasNoRun> hrphnr;
hrphr.run();
return 0;
}
@v4hn
Copy link
Author

v4hn commented Apr 9, 2020

Coming up with the void_t solution out of an overwhelming amount of discussions online, and implementing it in reduced form took way longer than it should have...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment