#include #include #include #include #include #include #include #include #include template void print(std::ostream& os, T const& data); namespace details { /* specialization for std::string since we don't want to print it as a container (even though it is one) */ void print(std::ostream& os, std::string const& str, int) { os << str; } void print(std::ostream& os, char const* str, int) { os << str; } template void print(std::ostream& os, char const (&str)[N], int) { os << str; } /* general printing functionality for printing an iterator range, used by the container and std::array overloads */ template void print_range(std::ostream& os, It start, It last) { os << '{'; if (start != last) { ::print(os, *start++); while (start != last) { os << ", "; ::print(os, *start++); } } os << '}'; } /* std::array is both a tuple-type and a container so to remove ambiguities we must make a specific overload for it */ template void print(std::ostream& os, std::array const& data, int) { print_range(os, std::begin(data), std::end(data)); } /* print anything that is a container. Here we check for the existence of iterators to deduce that it is a container */ template auto print(std::ostream& os, T const& data, int) -> decltype(std::begin(data), std::declval()) { print_range(os, std::begin(data), std::end(data)); } template void print_tuple(std::ostream& os, T const& data, std::integer_sequence) { ::print(os, std::get(data)); } template void print_tuple(std::ostream& os, T const& data, std::integer_sequence) { ::print(os, std::get(data)); os << ' '; print_tuple(os, data, std::integer_sequence{}); } /* print tuple-types (std::pair, std::tuple and std::array). We are using std::integer_sequence to generate a sequence of integers. Thanks to this we can iterate over the tuple during compile-time with variadic recursion. We are using std::tuple_size to deduce how many elements are stored in the tuple, and we are accessing elements with std::get */ template auto print(std::ostream& os, T const& data, int) -> decltype(std::get<0>(data), std::tuple_size::value, std::declval()) { os << '('; print_tuple(os, data, std::make_integer_sequence::value>{}); os << ')'; } /* Sink function, note the last parameter; this is to make sure that it has lower priority during overload resolution */ template void print(std::ostream& os, T const& data, double) { os << data; } } /* General print function which will call appropriate helper functions based of T */ template void print(std::ostream& os, T const& data) { details::print(os, data, 0); } int main() { print(std::cout, 5); std::cout << std::endl; std::vector v {1, 2, 3}; print(std::cout, v); std::cout << std::endl; std::map m { {1, 1}, {2, 2}, {3, 3} }; print(std::cout, m); std::cout << std::endl; std::tuple t { 5, 3.14 }; print(std::cout, t); std::cout << std::endl; std::string s[] { "hello", "world" }; print(std::cout, s); std::cout << std::endl; std::array, 2> a { std::vector{ "ab", "c" }, std::vector{"def", "g", "hi"} }; print(std::cout, a); std::cout << std::endl; char const* str {"SFINAE"}; print(std::cout, str); std::cout << std::endl; print(std::cout, "string literal"); std::cout << std::endl; }