Last active
September 9, 2020 06:06
-
-
Save district10/aa36e68b2e95ccd90f6d7de3334f886e to your computer and use it in GitHub Desktop.
sfinae-example-test-is-member-function-is-static-function-is-vector-is-map-etc.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#error "you should download from https://raw.githubusercontent.com/sharkdp/dbg-macro/master/dbg.h" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://wandbox.org/permlink/OfcEGD7GuzDbuZcp | |
// compile with: | |
// $ g++ prog.cc -std=gnu++2a -w | |
#include <vector> | |
#include <map> | |
#include <string> | |
#include <iostream> | |
#define HAS_GTEST 0 | |
#if HAS_GTEST | |
#include <gmock/gmock.h> | |
#include <gtest/gtest.h> | |
#define MAIN_BEGIN | |
#define MAIN_END | |
#else | |
#define DBG_MACRO_NO_WARNING | |
#include "dbg.h" | |
#define ASSERT_EQ(a, b) do {dbg("------------------------------");dbg(a);dbg(b);dbg((a) == (b));} while(0) | |
#define TEST(a, b) if (true) | |
#define MAIN_BEGIN int main(void) { | |
#define MAIN_END return 0;} | |
#endif | |
namespace detail | |
{ | |
template <typename T> struct is_vector_like_impl : std::false_type | |
{ | |
}; | |
template <typename T, typename A> | |
struct is_vector_like_impl<std::vector<T, A>> : std::true_type | |
{ | |
}; | |
template <typename...> struct voider | |
{ | |
using type = void; | |
}; | |
template <typename... T> using void_t = typename voider<T...>::type; | |
template <typename T, typename U = void> | |
struct is_map_like_impl : std::false_type | |
{ | |
}; | |
template <typename T> | |
struct is_map_like_impl<T, | |
void_t<typename T::key_type, typename T::mapped_type>> | |
: std::true_type | |
{ | |
}; | |
} // namespace detail | |
template <typename T> | |
struct is_vector_like : detail::is_vector_like_impl<T>::type | |
{ | |
}; | |
template <typename T> struct is_map_like : detail::is_map_like_impl<T>::type | |
{ | |
}; | |
template <typename T, std::enable_if_t<is_vector_like<T>{}, int> = 0> | |
bool vector_or_not(const T &items) | |
{ | |
return true; | |
} | |
template <typename T, std::enable_if_t<!is_vector_like<T>{}, int> = 0> | |
bool vector_or_not(const T &items) | |
{ | |
return false; | |
} | |
template <typename T, std::enable_if_t<is_map_like<T>{}, int> = 0> | |
bool map_or_not(const T &items) | |
{ | |
return true; | |
} | |
template <typename T, std::enable_if_t<!is_map_like<T>{}, int> = 0> | |
bool map_or_not(const T &items) | |
{ | |
return false; | |
} | |
struct X | |
{ | |
X(int v = 0) : v(v) {} | |
int answer() const { return 42; } | |
static X build(int v) { return X(v); } | |
int v; | |
}; | |
struct Y | |
{ | |
Y(int v = 0) : v(v) {} | |
Y build(int v) const { return Y(v); } | |
int v; | |
}; | |
template <typename T> struct HasAnswer | |
{ | |
template <typename U, int (U::*)() const> struct SFINAE | |
{ | |
}; | |
template <typename U> static char Test(SFINAE<U, &U::answer> *); | |
template <typename U> static int Test(...); | |
static const bool Has = sizeof(Test<T>(0)) == sizeof(char); | |
}; | |
template <typename T, std::enable_if_t<HasAnswer<T>::Has, int> = 0> | |
bool has_answer(const T &t) | |
{ | |
return true; | |
} | |
template <typename T, std::enable_if_t<!HasAnswer<T>::Has, int> = 0> | |
bool has_answer(const T &t) | |
{ | |
return false; | |
} | |
template <typename T> struct HasFactory | |
{ | |
template <typename U, std::enable_if_t< | |
// | |
std::is_same<U, decltype(U::build(0))>::value | |
// | |
, | |
int> = 0> | |
static char Test(int); | |
template <typename U> static int Test(...); | |
static const bool Has = sizeof(Test<T>(0)) == sizeof(char); | |
}; | |
template <typename T, std::enable_if_t<HasFactory<T>::Has, int> = 0> | |
bool has_factory(const T &t) | |
{ | |
return true; | |
} | |
template <typename T, std::enable_if_t<!HasFactory<T>::Has, int> = 0> | |
bool has_factory(const T &t) | |
{ | |
return false; | |
} | |
MAIN_BEGIN | |
TEST(SfinaeTest, DistinguishVectorMap) | |
{ | |
std::vector<int> nums{4, 2}; | |
std::vector<std::string> strs{"forty", "two"}; | |
ASSERT_EQ(vector_or_not("the answer is"), false); | |
ASSERT_EQ(vector_or_not(42), false); | |
ASSERT_EQ(vector_or_not(nums), true); | |
ASSERT_EQ(vector_or_not(strs), true); | |
std::map<int, double> kvs{{4, 2.0}}; | |
std::map<int, std::string> kvs2{{4, "2.0"}}; | |
ASSERT_EQ(map_or_not("the answer is"), false); | |
ASSERT_EQ(map_or_not(42), false); | |
ASSERT_EQ(map_or_not(nums), false); | |
ASSERT_EQ(map_or_not(strs), false); | |
ASSERT_EQ(map_or_not(kvs), true); | |
ASSERT_EQ(map_or_not(kvs2), true); | |
} | |
TEST(SfinaeTest, DoYouHaveAnswer) | |
{ | |
X x; | |
Y y; | |
ASSERT_EQ(has_answer(x), true); | |
ASSERT_EQ(has_answer(y), false); | |
} | |
TEST(SfinaeTest, DoYouHaveFactory) | |
{ | |
auto x2 = X::build(4); | |
ASSERT_EQ(x2.v, 4); | |
decltype(X::build(0)) x3(2); | |
ASSERT_EQ(x3.v, 2); | |
X x; | |
Y y; | |
ASSERT_EQ(has_factory(x), true); | |
ASSERT_EQ(has_factory(y), false); | |
} | |
MAIN_END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On wandbox: https://wandbox.org/permlink/OfcEGD7GuzDbuZcp
Reference