Skip to content

Instantly share code, notes, and snippets.

@panzi
Created August 9, 2014 23:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panzi/869728c9879dcd4fffa8 to your computer and use it in GitHub Desktop.
Save panzi/869728c9879dcd4fffa8 to your computer and use it in GitHub Desktop.
Match arrays and classes that implement methods needed to be iterable.
// $ g++ -Wall -Wextra -Werror -pedantic -std=c++1y is_iterable.cpp -O3 -o is_iterable
#include <type_traits>
#include <iterator>
#include <iostream>
#include <iomanip>
#include <vector>
#include <list>
#include <array>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <deque>
#include <forward_list>
template<typename T>
struct is_iterator {
private:
template<typename I> static constexpr auto test(void*)
-> decltype(
*std::declval<const I>(),
std::declval<const I>() == std::declval<const I>(),
std::declval<const I>() != std::declval<const I>(),
++ (*std::declval<I*>()),
(*std::declval<I*>()) ++,
std::true_type()) { return std::true_type(); }
template<typename I> static constexpr std::false_type test(...) { return std::false_type(); }
public:
static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
namespace impl {
// implementation details
template<typename T>
struct has_iterable_methods {
private:
template<typename C> static constexpr auto test(void*)
-> decltype(
std::declval<C>().begin(),
std::declval<C>().end(),
std::true_type()) { return std::true_type(); }
template<typename C> static constexpr std::false_type test(...) { return std::false_type(); }
public:
static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
template<typename T, bool HasIterableMethods>
struct returns_iterators : public std::false_type {};
template<typename T>
struct returns_iterators<T, true> {
typedef decltype(std::declval<T>().begin()) begin_type;
typedef decltype(std::declval<T>().end()) end_type;
static constexpr const bool value =
std::is_same<begin_type, end_type>::value &&
is_iterator<begin_type>::value;
};
}
template<typename T>
struct is_iterable : public std::integral_constant<
bool,
impl::returns_iterators<
typename std::remove_const<T>::type,
impl::has_iterable_methods<typename std::remove_const<T>::type>::value>::value> {};
template<typename T, std::size_t N>
struct is_iterable<T[N]> : public std::true_type {};
template<typename T>
struct is_iterable<T*> : public std::false_type {};
// test types
struct MyIterable {
int* begin() { return 0; }
int* end() { return 0; }
};
struct MyIterableConst {
const int* begin() const { return 0; }
const int* end() const { return 0; }
};
struct MyIterableBoth {
int* begin() { return 0; }
int* end() { return 0; }
const int* begin() const { return 0; }
const int* end() const { return 0; }
};
struct NotIterable {};
struct OnlyBegin {
const int* begin() const { return 0; }
};
struct OnlyEnd {
const int* end() const { return 0; }
};
struct WrongReturnTypes {
void begin() const {}
void end() const {}
};
struct TypeMissmatch {
int* begin() const { return 0; }
float* end() const { return 0; }
};
int main () {
int array[] = {1, 2, 3};
const int carray[] = {1, 2, 3};
typedef int A[3];
typedef std::vector<int> B;
std::cout << std::boolalpha
<< "typedef int A[3];\n"
<< "typedef std::vector<int> B;\n"
<< "int array[] = {1, 2, 3};\n"
<< "const int carray[] = {1, 2, 3};\n"
<< "\n"
<< "Iterable:\n"
<< "=========\n"
<< "is_iterable<std::vector<int>>::value = " << is_iterable<std::vector<int>>::value << "\n"
<< "is_iterable<std::list<int>>::value = " << is_iterable<std::list<int>>::value << "\n"
<< "is_iterable<std::array<int,3>>::value = " << is_iterable<std::array<int,3>>::value << "\n"
<< "is_iterable<std::set<int>>::value = " << is_iterable<std::set<int>>::value << "\n"
<< "is_iterable<std::map<int,int>>::value = " << is_iterable<std::map<int,int>>::value << "\n"
<< "is_iterable<std::unordered_set<int>>::value = " << is_iterable<std::unordered_set<int>>::value << "\n"
<< "is_iterable<std::unordered_map<int,int>>::value = " << is_iterable<std::unordered_map<int,int>>::value << "\n"
<< "is_iterable<std::deque<int>>::value = " << is_iterable<std::deque<int>>::value << "\n"
<< "is_iterable<std::forward_list<int>>::value = " << is_iterable<std::forward_list<int>>::value << "\n"
<< "is_iterable<decltype(array)>::value = " << is_iterable<decltype(array)>::value << "\n"
<< "is_iterable<decltype(carray)>::value = " << is_iterable<decltype(carray)>::value << "\n"
<< "is_iterable<std::string>::value = " << is_iterable<std::string>::value << "\n"
<< "is_iterable<MyIterable>::value = " << is_iterable<MyIterable>::value << "\n"
<< "is_iterable<const MyIterable>::value = " << is_iterable<const MyIterable>::value << "\n"
<< "is_iterable<MyIterableConst>::value = " << is_iterable<MyIterableConst>::value << "\n"
<< "is_iterable<const MyIterableConst>::value = " << is_iterable<const MyIterableConst>::value << "\n"
<< "is_iterable<MyIterableBoth>::value = " << is_iterable<MyIterableBoth>::value << "\n"
<< "is_iterable<const MyIterableBoth>::value = " << is_iterable<const MyIterableBoth>::value << "\n"
<< "is_iterable<int[3]>::value = " << is_iterable<int[3]>::value << "\n"
<< "is_iterable<const int[3]>::value = " << is_iterable<const int[3]>::value << "\n"
<< "is_iterable<int[]>::value = " << is_iterable<int[]>::value << "\n"
<< "is_iterable<int*>::value = " << is_iterable<int*>::value << "\n"
<< "is_iterable<const int*>::value = " << is_iterable<const int*>::value << "\n"
<< "is_iterable<int>::value = " << is_iterable<int>::value << "\n"
<< "is_iterable<const int>::value = " << is_iterable<const int>::value << "\n"
<< "is_iterable<NotIterable>::value = " << is_iterable<NotIterable>::value << "\n"
<< "is_iterable<OnlyBegin>::value = " << is_iterable<OnlyBegin>::value << "\n"
<< "is_iterable<OnlyEnd>::value = " << is_iterable<OnlyEnd>::value << "\n"
<< "is_iterable<WrongReturnTypes>::value = " << is_iterable<WrongReturnTypes>::value << "\n"
<< "is_iterable<TypeMissmatch>::value = " << is_iterable<TypeMissmatch>::value << "\n"
<< "\n"
<< "Iterator:\n"
<< "=========\n"
<< "is_iterator<std::vector<int>::const_iterator>::value = " << is_iterator<std::vector<int>::const_iterator>::value << "\n"
<< "is_iterator<std::vector<int>::iterator>::value = " << is_iterator<std::vector<int>::iterator>::value << "\n"
<< "is_iterator<decltype(std::begin(std::declval<const A>()))>::value = " << is_iterator<decltype(std::begin(std::declval<const A>()))>::value << "\n"
<< "is_iterator<decltype(std::begin(std::declval<const B>()))>::value = " << is_iterator<decltype(std::begin(std::declval<const B>()))>::value << "\n"
<< "is_iterator<int*>::value = " << is_iterator<int*>::value << "\n"
<< "is_iterator<const char*>::value = " << is_iterator<const char*>::value << "\n"
<< "is_iterator<int>::value = " << is_iterator<int>::value << "\n"
<< "is_iterator<void>::value = " << is_iterator<void>::value << "\n"
<< "is_iterator<void*>::value = " << is_iterator<void*>::value << "\n"
<< "is_iterator<int[3]>::value = " << is_iterator<int[3]>::value << "\n"
<< "is_iterator<int[]>::value = " << is_iterator<int[]>::value << "\n";
return 0;
}
typedef int A[3];
typedef std::vector<int> B;
int array[] = {1, 2, 3};
const int carray[] = {1, 2, 3};
Iterable:
=========
is_iterable<std::vector<int>>::value = true
is_iterable<std::list<int>>::value = true
is_iterable<std::array<int,3>>::value = true
is_iterable<std::set<int>>::value = true
is_iterable<std::map<int,int>>::value = true
is_iterable<std::unordered_set<int>>::value = true
is_iterable<std::unordered_map<int,int>>::value = true
is_iterable<std::deque<int>>::value = true
is_iterable<std::forward_list<int>>::value = true
is_iterable<decltype(array)>::value = true
is_iterable<decltype(carray)>::value = true
is_iterable<std::string>::value = true
is_iterable<MyIterable>::value = true
is_iterable<const MyIterable>::value = true
is_iterable<MyIterableConst>::value = true
is_iterable<const MyIterableConst>::value = true
is_iterable<MyIterableBoth>::value = true
is_iterable<const MyIterableBoth>::value = true
is_iterable<int[3]>::value = true
is_iterable<const int[3]>::value = true
is_iterable<int[]>::value = false
is_iterable<int*>::value = false
is_iterable<const int*>::value = false
is_iterable<int>::value = false
is_iterable<const int>::value = false
is_iterable<NotIterable>::value = false
is_iterable<OnlyBegin>::value = false
is_iterable<OnlyEnd>::value = false
is_iterable<WrongReturnTypes>::value = false
is_iterable<TypeMissmatch>::value = false
Iterator:
=========
is_iterator<std::vector<int>::const_iterator>::value = true
is_iterator<std::vector<int>::iterator>::value = true
is_iterator<decltype(std::begin(std::declval<const A>()))>::value = true
is_iterator<decltype(std::begin(std::declval<const B>()))>::value = true
is_iterator<int*>::value = true
is_iterator<const char*>::value = true
is_iterator<int>::value = false
is_iterator<void>::value = false
is_iterator<void*>::value = false
is_iterator<int[3]>::value = false
is_iterator<int[]>::value = false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment