Basically, I want to write code like this:
std::vector<float> a = { 54, 25, 32.5 }; std::vector<int> b = { 55, 65, 6 }; std::cout << a << b << std::string("lol");
This is not possible because there is no overload for operator<<(ostream&, vector)
So, I am writing a function that does the job:
template<template<typename...> typename T, typename ...Args> std::enable_if_t<is_iterable_v<T<Args...>>>, std::ostream> &operator<<(std::ostream &out, T<Args...> const &t) { for (auto const &e : t) out << e << " "; out << std::endl; return out; }
This works well, but I have a string problem. Since the strings are iterable and the strings have a HAVE operator<< function.
So, I tested with another sign, for example !is_streamable_out && _is_iterable , testing something like this: std::declval<std::ostream&>() << std::declval<T>() and if it has begin functions / end. It works well on MSVC, but not on Clang (I think this is because the compiler uses the function just created, so it found one overload available for all methods).
So, I am currently using !is_same_v<string, T> , but it is not perfect IMHO.
Is there a way to find out if a function exists without updating the function?
Basically, I want to do something like this
if function foo does not exist for this type. then function foo for this type is ...
This is not the same problem as Is it possible to write a template to verify the existence of a function? since in this other thread the function is not exactly (toString vs toOptionalString). In my case, the function is the same.
Here is my complete code:
template <class...> struct make_void { using type = void; }; template <typename... T> using void_t = typename make_void<T...>::type; // force SFINAE namespace detail { template<typename AlwaysVoid, template<typename...> typename Operator, typename ...Args> struct _is_valid : std::false_type {}; template<template<typename...> typename Operator, typename ...Args> struct _is_valid<void_t<Operator<Args...>>, Operator, Args...> : std::true_type { using type = Operator<Args...>; }; } template<template<typename ...> typename Operator, typename ...Args> using is_valid = detail::_is_valid<void, Operator, Args...>; #define HAS_MEMBER(name, ...)\ template<typename T>\ using _has_##name = decltype(std::declval<T>().name(__VA_ARGS__));\ \ template<typename T>\ using has_##name = is_valid<_has_push_back, T>;\ \ template<typename T>\ constexpr bool has_##name##_v = has_##name<T>::value HAS_MEMBER(push_back, std::declval<typename T::value_type>()); HAS_MEMBER(begin); HAS_MEMBER(end); template<typename T> using is_iterable = std::conditional_t<has_begin_v<T> && has_end_v<T>, std::true_type, std::false_type>; template<typename T> constexpr bool is_iterable_v = is_iterable<T>::value; template<class T, class Stream, class = void> struct can_print : std::false_type {}; template<class T, class Stream> struct can_print<T, Stream, void_t<decltype(std::declval<Stream&>() << std::declval<const T&>())>> : std::true_type {}; template<class T, class Stream = std::ostream> constexpr bool can_print_v = can_print<T, Stream>::value; template<typename T> std::enable_if_t<is_iterable_v<T> && !can_print_v<T>, std::ostream> &operator<<(std::ostream &out, T const &t) { for (auto const &e : t) out << e << " "; out << std::endl; return out; } template<typename A, typename B> std::ostream &operator<<(std::ostream &out, std::pair<A, B> const &p) { out << p.first << " " << p.second << std::endl; return out; } template<typename T> std::enable_if_t<has_push_back_v<T>, T> &operator<<(T &c, typename T::value_type const &e) { c.push_back(e); return c; }
and main:
#include <iostream> #include <vector> #include "Tools/stream.h" #include <string> #include <map> int main() { std::vector<float> a = { 54, 25, 32.5 }; std::vector<int> b = { 55, 65, 6 }; std::cout << a; std::cout << has_push_back<float>::value << " " << has_push_back<std::vector<float>>::value << std::endl; std::cout << is_iterable<std::vector<float>>::value << " " << is_iterable<float>::value << std::endl; getchar(); return 0; }