Basically, I want to write code like that :
std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };
std::cout << a << b << std::string("lol");
It is not possible because there is no overload for operator<<(ostream&, vector)
So, I write a function that do 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;
}
That works good, but I have a problem with string. Because strings are iterable and strings HAVE operator<< function.
So I tested with another trait like !is_streamable_out && _is_iterable testing something like that : std::declval<std::ostream&>() << std::declval<T>() and if it has begin / end functions. It works good on MSVC, but not on Clang (I think it is because the compiler use the function I just create as well, so it founds 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 know if a function exists without redeclaring the function?
Basically, I want to do something like that
if function foo does not exist for this type.
then function foo for this type is ...
It is not the same problem as Is it possible to write a template to check for a function's existence? because in this other thread, the function is not exactyly the same (toString vs toOptionalString). In my case, the function are the same.
Here is my full 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 the 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;
}
How to avoid this sentence is false in a template SFINAE? provides an answer that solves your problem -- overload <<(ostream&, Ts...), which will be found with lower priority than any other << overload.
At the same time I'd say your plan is a poor one. Overloading operators for std types is a bad plan for 2 reasons.
First, you should be reluctant to overload operators for types you do not own unless there is a great reason.
Second, if you do so, you should do it in the namespace of the type, and you cannot inject your << into namespace std without making your program ill-formed.
Operators overloaded in a namespace different than the types in question can only be found in the namespace where you did the overload. Operators overloaded in a namespace related to the arguments can be found anywhere.
This results in fragile << that only works in one namespace.
So, instead, do this:
struct print_nothing_t {};
inline std::ostream& operator<<(std::ostream& os, print_nothing_t) {
return os;
}
template<class C, class Sep=print_nothing_t>
struct stream_range_t {
C&& c;
Sep s;
template<class T=print_nothing_t>
stream_range_t( C&& cin, T&& sin = {} ):
c(std::forward<C>(cin)),
s(std::forward<T>(sin))
{}
friend std::ostream& operator<<(std::ostream& os, stream_range_t&& self) {
bool first = true;
for (auto&& x:self.c) {
if (!first) os << self.s;
os << decltype(x)(x);
first = false;
}
return os;
}
};
template<class C, class Sep = print_nothing_t>
stream_range_t<C, Sep> stream_range( C&& c, Sep&& s={} ) {
return {std::forward<C>(c), std::forward<Sep>(s)};
}
Now, if you want nested vectors to work, you'd have to implement a stream range that applies an adapter to its contents.
Live example with this test code:
std::vector<int> v{1,2,3,4};
std::cout << stream_range(v, ',') << "\n";
output is 1,2,3,4.
How to make this recursive:
We can write a adapt_for_streaming function that dispatches to either identity (for things already streamable), and to stream_range for things iterable but not already streamable.
Users can then add new adapt_for_streaming overloads for other types.
stream_range_t then streams using adapt_for_streaming on its contents.
Next, we can add tuple/pair/array overloads to adapt_for_streaming and suddenly std::vector< std::vector< std::tuple<std::string, int> > > can be streamed.
End users can call stream_range directly, or adapt_for_streaming, to string an iterable container. You can even call stream_range directly on a std::string to treat it like a streamable collection of char instead of a string.
You could write a small detection idiom that tests if the expression stream << value is well formed*.
Here it is using std::ostream:
template<class...>
using void_t = void;
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;
Now your can write your overload for operator<< like so:
template<template<class...> class C, class...T>
std::enable_if_t<!can_print_v<C<T...>>, std::ostream>& operator<<(std::ostream &out, C<T...> const &t) {
for (auto const &e : t)
out << e << " ";
out << std::endl;
return out;
}
and a test
std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };
std::cout << a;
std::cout << b;
std::cout << std::string("lol") << std::endl;
Demo
Yakk points out an interesting thing in their answer. This sentence is false.
Basically, by using !can_print_v to enable an overload for operator<<, the can_print_v should subsequently be true, but it's false because the first instantiation of the template resulted in the struct deriving from std::false_type. Subsequent tests for can_print_v are therefore invalid.
I'm leaving this answer as a cautionary tale. The trait itself is okay, but using it to SFINAE something that invalidates the trait is not okay.
*It appears OP has copied this code into their own codebase and then modified the question to include it, in case you were wondering why it looks the other way around
Related
Consider this function:
template<typename T>
void f(T c) {
std::cout<<c<<std::endl;
}
You see that it will not compile for types which does not have an operator<< overload.
Now I want to write a function that acts like a fallback for this case.
/*Fallback*/
template<>
void f(T c) {
std::cout<<"Not Printing"<<std::endl;
}
How must this function be defined to do the job?
Pre-C++20
To have these overloads work in a fallback way, we can start by defining a trait that detects the validity of the expression involving operator <<
namespace detail {
template<typename T, typename = void>
struct streamable : std::false_type{};
template<typename T>
struct streamable<T, decltype(std::declval<std::ostream&>() << std::declval<T&>(), void())> : std::true_type {};
}
It's just your typical use of the detection idiom with as little extra library support as possible. Depending on the standard you are building against, this may be written in other ways (for instance std::void_t can be used, if available).
Now, the two overloads can be specified rather simply:
template<typename T>
auto f(T c) -> std::enable_if_t<detail::streamable<T>::value, void> {
std::cout<<c<<std::endl;
}
template<typename T>
auto f(T c) -> std::enable_if_t<!detail::streamable<T>::value, void> {
/// other code
}
Post C++20, concepts and constraints make it a whole lot easier. It can even be written ad-hoc:
template<typename T>
requires requires(std::ostream& os, T& c) { os << c; }
void f(T c) {
std::cout<<c<<std::endl;
}
template<typename T> // No extra step, subsumed by the above when possible
void f(T c) {
// other code
}
With concepts (C++20), we can achieve this like so:
template<typename T>
concept Streamable = requires(T t){std::declval<std::ostream&>() << t; };
template<Streamable T>
void f(T c) { std::cout << c << std::endl; }
/*Fallback*/
template<typename T>
void f(T c) { std::cout << "fallback" < <std::endl; }
Demo
Test:
struct Foo{};
int main()
{
Foo foo;
f(foo); // prints "fallback"
int a = 42;
f(a); // prints "42"
}
If you want to make doubly sure that your fallback will only happen if your type is not Streamable, you can constrain it, too:
template<typename T> requires (!Streamable<T>)
void f(T c) { /*...*/ }
You have several options of doing this. Arguably the most elegant way is to define your own type trait (similar to the ones in type_traits).
Let's define a is_streamable type trait. It takes two template arguments: S is the data type of the file stream (e.g. std::ostream or std::fstream or any other type that defines a custom streaming operator that is compatible with T) and secondly the data type of the object to be streamed into this file stream T:
template<typename S, typename T, typename = void>
struct is_streamable : std::false_type {
};
template<typename S, typename T>
struct is_streamable<S, T, decltype(std::declval<S&>() << std::declval<T&>(), void())> : std::true_type {
};
So far this type trait compiles with C++11 and onwards. For C++14 and later we can create a convenient alias for it similar to other type traits in C++17:
template <typename S, typename T>
static constexpr is_streamable_v = is_streamable<S,T>::value;
This type trait will now be the basis for the next step which will make use of SFINAE (C++11 onwards), constexpr if (C++17 onwards) or concepts (C++20).
In C++11 you could achieve this with either by putting the different implementations into partial specialisations of the same struct and call it with a helper function:
class f_imp {
};
template <typename T>
class f_imp<T,true> {
public:
static constexpr void imp(T c) {
std::cout << "streamable: " << c << std::endl;
}
};
template <typename T>
class f_imp<T,false> {
public:
static constexpr void imp(T c) {
std::cout << "not streamable" << std::endl;
}
};
template <typename T>
void f(T c) {
return f_imp<T,is_streamable<std::ostream,T>::value>::imp(c);
}
Try it here!
Alternatively you could apply SFINAE either by adding a second input parameter or applying it to the return type:
template<typename T, typename std::enable_if<is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "streamable" << std::endl;
}
template<typename T, typename std::enable_if<!is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "not streamable" << std::endl;
}
Try it here!
In C++17 you can actually use a constexpr if to avoid adding a second template argument and overloading of the function altogether. You can insert all the code inside the function and use if constexpr in combination with std::is_same_v and our is_streamable_v to decide at compile time which branch of our code each template type should take. This is in particular convenient if adding two specialisations would result in duplicate code but it might be harder to read.
template<typename T>
void f(T c) {
if constexpr (is_streamable_v<std::ostream,T>) {
std::cout << "streamable:" << c << std::endl;
} else {
// Fallback
std::cerr << "not streamable" << std::endl;
}
return;
}
Try it here!
Finally in C++20 you could use this type trait to define a concepts such as streamable and not_streamable:
template <typename T>
concept streamable = is_streamable_v<std::ostream,T>;
template <typename T>
concept not_streamable = !streamable<T>;
Then you can go on to apply them to your two overloads of the functions
template <streamable T>
void f(T c) {
std::cout << "streamable: " << c << std::endl;
}
template <not_streamable T>
void f(T c) {
std::cout << "not streamable" << std::endl;
}
Try it here!
Be aware that you will have to also apply the same logic to any custom streaming operator of a templated class, e.g. of a templated vector. Instead of declaring the operator for any template parameter typename T you would have to only declare it for streamable element types only. In C++20 for example with said streamable concept:
template <streamable T>
std::ostream& operator << (std::ostream& os, std::vector<T> const& vec) {
for (auto const& v: vec) {
os << v << " ";
}
return os;
}
Otherwise - as the template argument to the is_streamable operator is std::vector<T> as a whole - the compiler sees the operator << for std::vector<T> without checking if it would result in a compilation error for an unstreamable type T which does not define the operator << itself.
Try it here!
I would want to make a template of a << operator in C++, that would show a Object that is a "range" (by that i mean any object like : std::vector, std::set, std::map, std::deque). How can i achieve this? I've been googling and looking in docs for a few days now, but without any effect. I've been doing few templates and been overriding few operators before, but these were inside of a certain class that was representing a custom vector class. I cant seem to find a good way of implementing this, because it collides with a standard cout. How do i do it then, inside of a class that can pass a vector,set,map,deque as an argument, and operator inside? I would also want this operator to return the begin() and end() iterator of an object. By now i have this code:
template <typename T>
ostream& operator<<(ostream& os, T something)
{
os << something.begin() << something.end();
return os;
}
it doesnt really work, and i think that experienced C++ programmer can explain me why.
Thanks in advance for any answer for that problem.
Your overload will match on pretty much everything causing ambiguity for the types for which operator<< already has an overload.
I suspect that you want to print all elements in the container here: os << something.begin() << something.end();. This will not work because begin() and end() return iterators. You could dereference them
if(something.begin() != something.end())
os << *something.begin() << *std::prev(something.end());
but you'd only get the first and last element printed. This would print all of them:
for(const auto& v : something) os << v;
To solve the ambiguity problem, you could use template template parameters and enable the operator<< overload for the containers you'd like to support.
Example:
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <type_traits>
#include <vector>
// helper trait - add containers you'd like to support to the list
template <typename T> struct is_container : std::false_type {};
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::list<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::deque<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::map<Ts...>> : std::true_type{};
// C is the container template, like std::vector
// Ts... are the template parameters used to create the container.
template <template <typename...> class C, typename... Ts>
// only enable this for the containers you want to support
typename std::enable_if<is_container<C<Ts...>>::value, std::ostream&>::type
operator<<(std::ostream& os, const C<Ts...>& something) {
auto it = something.begin();
auto end = something.end();
if(it != end) {
os << *it;
for(++it; it != end; ++it) {
os << ',' << *it;
}
}
return os;
}
An alternative could be to make it generic but to disable the overload for types that already supports streaming.
#include <iostream>
#include <iterator>
#include <type_traits>
// A helper trait to check if the type already supports streaming to avoid adding
// an overload for std::string, std::filesystem::path etc.
template<typename T>
class is_streamable {
template<typename TT>
static auto test(int) ->
decltype( std::declval<std::ostream&>() << std::declval<TT>(), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T,
typename U = decltype(*std::begin(std::declval<T>())), // must have begin
typename V = decltype(*std::end(std::declval<T>())) // must have end
>
// Only enable the overload for types not already streamable
typename std::enable_if<not is_streamable<T>::value, std::ostream&>::type
operator<<(std::ostream& os, const T& something) {
auto it = std::begin(something);
auto end = std::end(something);
if(it != end) {
os << *it;
for(++it; it != end; ++it) {
os << ',' << *it;
}
}
return os;
}
Note: The last example works in clang++ and MSVC but it fails to compile in g++ (recursion depth exceeded).
For containers with a value_type that is in itself not streamable, like the std::pair<const Key, T> in a std::map, you need to add a separate overload. This needs to be declared before any of the templates above:
template <typename Key, typename T>
std::ostream &operator<<(std::ostream &os, const std::pair<const Key, T>& p) {
return os << p.first << ',' << p.second;
}
Your code has the right idea but is missing a few things.
template <typename T>
ostream& operator<<(ostream& os, T something)
{
os << something.begin() << something.end();
return os;
}
Iterable containers (like std::map and such) should be outputted by iterating through all their elements, and outputting each one-by-one. Here, you're only outputting the beginning and end iterators, which aren't the same as elements themselves.
We can instead use *it to get an element from its iterator in the container. So, the code below will output all elements in a standard container of type T. I also include some additional pretty-printing.
template <typename T>
std::ostream &operator<<(std::ostream &os, const T &o) {
auto it = o.begin();
os << "{" << *it;
for (it++; it != o.end(); it++) {
os << ", " << *it;
}
return os << "}";
}
If we just use
template <typename T>
ahead of this function declaration, then it will conflict with existing << operator declarations. That is, when we writestd::cout << std::string("hello world");, does this call our function implementation, or does this call the function implementation from <string>? Of course, we want to use the standard operator<< implementations if available. We do this by limiting the template so that it only works for standard containers with begin() and end() members, but not for std::string, which has begin() and end() but also has an existing operator<< implementation that we want to use.
template <typename T,
typename std::enable_if<is_iterable<T>::value, bool>::type = 0,
typename std::enable_if<!std::is_same<T, std::string>::value, bool>::type = 0>
The second std::enable_if is straightforward: the template should cover types as long as they aren't std::string. The first std::enable_if checks if the type T is iterable. We need to make this check ourselves.
template <typename T>
class is_iterable {
private:
typedef char True[1];
typedef char False[2];
template <typename Q,
typename std::enable_if<
std::is_same<decltype(std::declval<const Q &>().begin()),
decltype(std::declval<const Q &>().begin())>::value,
char>::type = 0>
static True &test(char);
template <typename...>
static False &test(...);
public:
static bool const value = sizeof(test<T>(0)) == sizeof(True);
};
is_iterable has two versions of the function test. The first version is enabled if begin() and end() exist on type T, and their return types are the same (there are more precise ways to do checks, but this suffices for now). The second version is called otherwise. The two versions' return types are different, and by checking the size of the return type, we can set value, which will be true if and only if T is iterable (in our case, if T defines begin() and end() and their return types are the same).
Finally, we note that std::map<T1, T2>'s elements are actually of type std::pair<T1, T2>, so we need to additionally overload operator<< for templated pairs.
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &o) {
return os << "(" << o.first << ", " << o.second << ")";
}
Putting it all together, we can try this. Note that it even works for nested iterator types like listUnorderedSetTest.
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <type_traits>
#include <unordered_set>
#include <vector>
template <typename T>
class is_iterable {
private:
typedef char True[1];
typedef char False[2];
template <typename Q,
typename std::enable_if<
std::is_same<decltype(std::declval<const Q &>().begin()),
decltype(std::declval<const Q &>().begin())>::value,
char>::type = 0>
static True &test(char);
template <typename...>
static False &test(...);
public:
static bool const value = sizeof(test<T>(0)) == sizeof(True);
};
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &o) {
return os << "(" << o.first << ", " << o.second << ")";
}
template <typename T,
typename std::enable_if<is_iterable<T>::value, bool>::type = 0,
typename std::enable_if<!std::is_same<T, std::string>::value, bool>::type = 0>
std::ostream &operator<<(std::ostream &os, const T &o) {
auto it = o.begin();
os << "{" << *it;
for (it++; it != o.end(); it++) {
os << ", " << *it;
}
return os << "}";
}
int main() {
std::vector<std::string> vectorTest{"hello", "world", "!"};
std::cout << vectorTest << std::endl;
std::set<const char *> setTest{"does", "this", "set", "work", "?"};
std::cout << setTest << std::endl;
std::map<std::string, std::size_t> mapTest{
{"bob", 100}, {"alice", 16384}, {"xavier", 216}};
std::cout << mapTest << std::endl;
std::list<std::unordered_set<std::string>> listUnorderedSetTest{
{"alice", "abraham", "aria"},
{"carl", "crystal", "ciri"},
{"november", "nathaniel"}};
std::cout << listUnorderedSetTest << std::endl;
return 0;
}
This outputs:
{hello, world, !}
{does, this, set, work, ?}
{(alice, 16384), (bob, 100), (xavier, 216)}
{{alice, abraham, aria}, {carl, crystal, ciri}, {november, nathaniel}}
There's a lot of additional related discussion at Templated check for the existence of a class member function? which you might find helpful. The downside of this answer is a check against std::string instead of a check for existing operator<< implementations, which I think can be solved with a bit more work into type checking with decltype.
The code below comes in handy for fast debugging. It can pretty print containers like vectors from the STL while also being able to provide the printing-ness property for fundamental and streamable types using SFINAE techniques.
I would like to replicate the same behaviour without the use of decltype. Any other neat techniques of replicating this behaviour will be very helpful implementation-wise for a few projects I have in mind. The current thread may also be considered as a continuation of this thread (also by me). I managed to get a neat working implementation with concepts in C++20 (GCC 10.2.0) without the use of decltype but I would like to be able to do so in C++17 (GCC 9.2.0) and lower (obviously, without concepts, and decltype as mentioned).
namespace debugging {
template <typename T>
class range {
public:
T begin, end;
};
template <typename T>
auto make_range (const T& b, const T& e) -> range <T> {
return {b, e};
}
template <typename T>
auto is_streamable_object (T *x) -> decltype(std::cerr << *x, std::true_type());
template <typename T>
auto is_streamable_object (...) -> decltype(std::false_type());
class view {
private:
std::ostream& stream;
public:
view (std::ostream& os = std::cerr) : stream (os)
{ };
~view ()
{ stream << std::endl; };
#ifdef LOST_IN_SPACE
template <typename T>
std::enable_if_t <std::is_same <decltype(is_streamable_object <T> (nullptr)), std::true_type>::value, view&>
operator << (const T& t) {
stream << std::boolalpha << t; return *this;
}
template <typename T>
std::enable_if_t <std::is_same <decltype(is_streamable_object <T> (nullptr)), std::false_type>::value, view&>
operator << (const T& t) {
return *this << make_range(begin(t), end(t));
}
template <typename T>
view& operator << (const range <T>& r) {
stream << "[";
for (auto i = r.begin, j = i; i != r.end; ++i)
*this << *i << (++j == r.end ? "]" : ", ");
return *this;
}
template <typename A, typename B>
view& operator << (const std::pair <A, B>& p) {
stream << '(' << p.first << ", " << p.second << ')';
return *this;
}
#else
template <typename T> view& operator << (const T&)
{ return *this; }
#endif
};
} // namespace debugging
Test code in case you'd like to try it quickly yourself:
#define print(x) " [" << #x << ": " << x << "] "
using view = debugging::view;
view debug (std::cerr);
auto test () -> void {
std::vector <int> v {1,2,3,4,5};
std::map <int, int> m {{1,2}, {3,4}};
debug << print(v) print(m) << "\nHello World";
}
Sure, here's an implementation using sizeof:
template<class T, class = std::false_type>
struct is_streamable_object : std::false_type {};
template<class T>
struct is_streamable_object<T,
std::bool_constant<sizeof(std::cerr << std::declval<T>()) == 0>> : std::true_type {};
Example.
Other keywords you could use include alignof, noexcept, typeid, and explicit (the last since C++20).
Essentially, you want to perform expression SFINAE, which requires constructing an expression that is not to be evaluated. The operators that allow constructing unevaluated expressions are those mentioned above plus decltype (which you do not want to use) and requires (C++20).
I'm trying to differentiate between user types and primitive types in a variadic template.
I have tried overloading binary operator, but that only says that there's no fitting overload for 'user types'...
template <typename T>
void PrintParams(T t)
{
if (IsAUserType)
std::cout << typeid(t).name();
else
std::cout << t;
}
template <typename First, typename... Rest>
void PrintParams(First first, Rest... rest)
{
if (IsAUserType)
std::cout << typeid(first).name();
else
std::cout << first;
PrintParams(rest...);
}
// If you know what to do with this, then that would also be very helpful...
//Overload << operator for user types
//template <typename T>
//friend std::ostream& operator<< (std::ostream& os, T t)
//{
//
//if (std::is_fundamental<t>::value)
//std::clog << t;
//else
//std::clog << typeid(t).name();
//}
expected result for input like (class test, 3.4, "string") would be
"test3.4string"
You could split your single argument function up in two and use SFINAE to enable the correct one depending on if the argument is a fundamental type or not:
template<typename T, typename std::enable_if<std::is_fundamental<T>::value, int>::type = 0>
void PrintParams(T t) {
std::cout << t;
}
template<typename T, typename std::enable_if<!std::is_fundamental<T>::value, int>::type = 0>
void PrintParams(T t) {
std::cout << typeid(t).name();
}
template<typename First, typename... Rest>
void PrintParams(First first, Rest... rest) {
PrintParams(first); // ... and call the single argument version here
std::cout << ",";
PrintParams(rest...);
}
Another way would be to check if the type supports streaming using operator<< instead of checking that it's a fundamental type. That would make streaming work for classes (like std::string and user defined ones too).
#include <iostream>
#include <type_traits>
#include <typeinfo>
#include <utility>
// SFINAE support
namespace detail {
template<class>
struct sfinae_true : std::true_type {};
template<class S, class T>
static auto test_lshift(int)
-> sfinae_true<decltype(std::declval<S>() << std::declval<T>())>;
template<class S, class T>
static auto test_lshift(long) -> std::false_type;
} // namespace detail
template<class T>
struct has_ostream : decltype(detail::test_lshift<std::ostream, T>(0)) {};
// using the SFINAE support stuff
template<typename T, typename std::enable_if<has_ostream<T>::value, int>::type = 0>
void PrintParams(const T& t) {
std::cout << "Type: " << typeid(t).name() << "\n"
<< " supports operator<< Value = " << t << "\n";
}
template<typename T, typename std::enable_if<!has_ostream<T>::value, int>::type = 0>
void PrintParams(const T& t) {
std::cout << "Type: " << typeid(t).name() << "\n"
<< " does NOT support operator<<\n";
}
template<typename First, typename... Rest>
void PrintParams(First first, Rest... rest) {
PrintParams(first);
PrintParams(rest...);
}
// example classes
class Foo { // will not support streaming
int x = 5;
};
class Bar { // this should support streaming
int x = 10;
friend std::ostream& operator<<(std::ostream&, const Bar&);
};
std::ostream& operator<<(std::ostream& os, const Bar& b) {
return os << b.x;
}
// testing
int main() {
int i = 2;
Foo f;
Bar b;
std::string s = "Hello world";
PrintParams(i, f, b, s);
}
Possible output:
Type: i
supports operator<< Value = 2
Type: 3Foo
does NOT support operator<<
Type: 3Bar
supports operator<< Value = 10
Type: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
supports operator<< Value = Hello world
I think std::is_class can replace your IsAUserType.
https://en.cppreference.com/w/cpp/types/is_class
Primitive data types include integer , character , void , float etc..which are defined already inside the language i.e , user can use these data types without defining them inside the language.
User defined data types are the data types which user have to define while or before using them.
I've seen types that have corresponding to_string() function, but haven't overloaded operator<<(). So, when inserting to stream, one has to << to_string(x) which is verbose. I'm wondering whether it's possible to write a generic function that users operator<<() if supported and falls back to << to_string() if not.
SFINAE is overkill, use ADL.
The trick is to make sure that an operator<< is available, not necessarily the one supplied by the type definition:
namespace helper {
template<typename T> std::ostream& operator<<(std::ostream& os, T const& t)
{
return os << to_string(t);
}
}
using helper::operator<<;
std::cout << myFoo;
This trick is commonly used in generic code which needs to choose between std::swap<T> and a specialized Foo::swap(Foo::Bar&, Foo::Bar&).
Try
template <typename T>
void print_out(T t) {
print_out_impl(std::cout, t, 0);
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t,
typename std::decay<decltype(
std::declval<OS&>() << std::declval<T>()
)>::type*) {
o << t;
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t, ...) {
o << t.to_string();
}
LIVE
Yes, it is possible.
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
struct streamy
{
};
std::ostream&
operator<<(std::ostream& os, const streamy& obj)
{
return os << "streamy [" << static_cast<const void *>(&obj) << "]";
}
struct stringy
{
};
std::string
to_string(const stringy& obj)
{
auto oss = std::ostringstream {};
oss << "stringy [" << static_cast<const void *>(&obj) << "]";
return oss.str();
}
template <typename T>
std::enable_if_t
<
std::is_same
<
std::string,
decltype(to_string(std::declval<const T&>()))
>::value,
std::ostream
>&
operator<<(std::ostream& os, const T& obj)
{
return os << to_string(obj);
}
int
main()
{
std::cout << streamy {} << '\n';
std::cout << stringy {} << '\n';
}
The generic operator<< will only be available if the expression to_string(obj) is well-typed for obj a const T& and has a result of type std::string. As you have already conjectured in your comment, this is indeed SFINAE at work. If the decltype expression is not well-formed, we will get a substitution failure and the overload will disappear.
However, this will likely get you into troubles with ambiguous overloads. At the very least, put your fallback operator<< into its own namespace and only drag it in locally via a using declaration when needed. I think you will be better off writing a named function that does the same thing.
namespace detail
{
enum class out_methods { directly, to_string, member_str, not_at_all };
template <out_methods> struct tag {};
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::directly>)
{
os << arg;
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::to_string>)
{
os << to_string(arg);
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::member_str>)
{
os << arg.str();
}
template <typename T>
void
out(std::ostream&, const T&, const tag<out_methods::not_at_all>)
{
// This function will never be called but we provide it anyway such that
// we get better error messages.
throw std::logic_error {};
}
template <typename T, typename = void>
struct can_directly : std::false_type {};
template <typename T>
struct can_directly
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>()))
> : std::true_type {};
template <typename T, typename = void>
struct can_to_string : std::false_type {};
template <typename T>
struct can_to_string
<
T,
decltype((void) (std::declval<std::ostream&>() << to_string(std::declval<const T&>())))
> : std::true_type {};
template <typename T, typename = void>
struct can_member_str : std::false_type {};
template <typename T>
struct can_member_str
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>().str()))
> : std::true_type {};
template <typename T>
constexpr out_methods
decide_how() noexcept
{
if (can_directly<T>::value)
return out_methods::directly;
else if (can_to_string<T>::value)
return out_methods::to_string;
else if (can_member_str<T>::value)
return out_methods::member_str;
else
return out_methods::not_at_all;
}
template <typename T>
void
out(std::ostream& os, const T& arg)
{
constexpr auto how = decide_how<T>();
static_assert(how != out_methods::not_at_all, "cannot format type");
out(os, arg, tag<how> {});
}
}
template <typename... Ts>
void
out(std::ostream& os, const Ts&... args)
{
const int dummy[] = {0, ((void) detail::out(os, args), 0)...};
(void) dummy;
}
Then use it like so.
int
main()
{
std::ostringstream nl {"\n"}; // has `str` member
out(std::cout, streamy {}, nl, stringy {}, '\n');
}
The function decide_how gives you full flexibility in deciding how to output a given type, even if there are multiple options available. It is also easy to extend. For example, some types have a str member function instead of an ADL find-able to_string free function. (Actually, I already did that.)
The function detail::out uses tag dispatching to select the appropriate output method.
The can_HOW predicates are implemented using the void_t trick which I find very elegant.
The variadic out function uses the “for each argument” trick, which I find even more elegant.
Note that the code is C++14 and will require an up-to-date compiler.
Based on the answer of #MSalters (credits goes to him), this one solves my problem and should make a complete answer.
#include <iostream>
#include <string>
#include <type_traits>
struct foo_t {};
std::string to_string(foo_t) {
return "foo_t";
}
template <class CharT, class Traits, class T>
typename std::enable_if<std::is_same<CharT, char>::value,
std::basic_ostream<CharT, Traits>&>::type
operator<<(std::basic_ostream<CharT, Traits>& os, const T& x) {
return os << to_string(x);
}
int main() {
std::cout << std::string{"123"} << std::endl;
std::cout << foo_t{} << std::endl;
}