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.
Related
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 want to write a function that takes a generic container with any type and prints it.
Let's leave for a moment that it won't work for some associative containers and focus on the issue:
template<template<typename> typename Cont, typename T>
void print(const Cont<T>& cont)
{
for (const auto it : cont)
{
cout << it << " ";
}
cout << endl;
}
int main()
{
vector<string> v;
print(v);
}
The error states:
error C2672: 'print': no matching overloaded function found
error C3207: 'print': invalid template argument for 'Cont', class template expected
Can anyone please explain why the compiler cannot deduce the types here?
Even when I explicitly state print<vector<string>>(v)?
std::vector has more than one class-template-args.
template<
class T, // -----------> you have mentioned!
class Allocator = std::allocator<T> // -----> this didn't!
> class vector;
But you have provided only one. This is the reason for the no matching overloaded compiler error.
In order to fix the issue, you need to provide the variadic args in the template template arg.
template<template<typename...> typename Cont, typename T>
// ^^^^^^^^^^^^^^^^^^^^
void print(const Cont<T>& cont)
{
for (const auto& it : cont) {
std::cout << it << " ";
}
std::cout << std::endl;
}
(See a demo)
However, you could have done this simply
template <typename Cont>
void print(const Cont& cont)
{
// ...
}
Or like the standard way, passing the begin and end iterator of the container to the function
#include <algorithm> // std::copy
#include <iterator> // std::iterator_traits
template<typename Iterator>
constexpr void print(const Iterator begin, const Iterator end) noexcept
{
using ValueType = typename std::iterator_traits<Iterator>::value_type;
std::copy(begin, end, std::ostream_iterator<ValueType>(std::cout, " "));
std::cout << "\n";
}
and call it like
print(v.begin(), v.end());
std::vector is defined as
template<
class T,
class Allocator = std::allocator<T>
> class vector;
std::vector is a class template with two template parameters. It does not match
the template template parameter of your function.
Change it to
template <typename Cont>
void print(const Cont& cont)
{
for (const auto& elem : cont) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
You can deduce T from the container, if that is necessary.
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
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;
}
I have the following problem:
template<class T>
void set(std::string path, const T data)
{
stringstream ss;
ss << data << std::endl;
write(path, ss.str();
}
template<class T>
void set(std::string path, const T data)
{
std::stringstream ss;
for(typename T::const_iterator it = data.begin(); it < data.end(); ++it)
{
ss << *it;
if(it < data.end() -1 )
ss << ", ";
}
ss << std::endl;
write(path, ss.str());
}
I get the following error:
error: ‘template<class T> void myclass::set(std::string, T)’ cannot be overloaded
error: with ‘template<class T> void myclass::set(std::string, T)’
Is there a way to differentiate between container types and other types in templates?
Use a trait:
#include <type_traits>
template <typename T>
typename std::enable_if<is_container<T>::value>::type
set (std::string const & path, T const & container)
{
// for (auto const & x : container) // ...
}
template <typename T>
typename std::enable_if<!is_container<T>::value>::type
set (std::string const & path, T const & data)
{
std::ostringstream oss;
oss << data;
write(path, oss.str());
}
You can find a suitable trait in the pretty printer code.
In C++03 you can do this with a little bit of SFINAE to selectively enable different versions of the function for different types:
#include <boost/type_traits.hpp>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
template<class T>
void set(typename boost::enable_if<boost::is_pod<T>, std::string>::type path, const T data)
{
std::cout << "POD" << std::endl;
stringstream ss;
ss << data << std::endl;
}
template<class T>
void set(typename boost::disable_if<boost::is_pod<T>, std::string>::type path, const T data)
{
std::cout << "Non-POD" << std::endl;
std::stringstream ss;
for(typename T::const_iterator it = data.begin(); it < data.end(); ++it)
{
ss << *it;
if(it < data.end() -1 )
ss << ", ";
}
ss << std::endl;
}
int main() {
int i;
float f;
std::vector<int> v;
set("", v);
set("", i);
set("", f);
}
I used boost for convenience here, but you could roll your own if boost isn't an option or use C++11 instead.
is_pod isn't quite what you want really, you probably want an is_container trait, but that's not so trivial, you'll need to make a trait of your own and is_pod makes a good approximation for how to use traits to selectively enable functions as simple answer.
You might try Substitution Failure Is Not An Error (SFINAE) techniques here.
Firstly, you need a function to determine whether a type has an iterator member...
template <typename T>
struct Has_Iterator
{
template <typename>
static char test(...);
template <typename U>
static int test(typename U::const_iterator*);
static const bool result = sizeof test<T>(0) != sizeof(char);
};
In the above code, the C++ Standard requires test(typename U::const_iterator*) to be used in preference to the vague "..." parameter match, so long as U is actually a struct/class with a const_iterator member type. Otherwise, you have a "substitution failure" - which is not a fatal error stopping compilation (hence SFINAE), and the attempt to find a matching function is satified by test(...). As the return types of the two functions differ, the sizeof operator can test which one has been matched, setting the result boolean appropriately.
Then you can forward requests to print things to template specialisations that support them...
template <typename T>
void print(const T& data)
{
printer<Has_Iterator<T>::result, T>()(data);
}
// general case handles types having iterators...
template <bool Has_It, typename T>
struct printer
{
void operator()(const T& data)
{
for (typename T::const_iterator i = data.begin(); i != data.end(); ++i)
std::cout << *i << ' ';
std::cout << '\n';
}
};
// specialisation for types lacking iterators...
template <typename T>
struct printer<false, T>
{
void operator()(const T& data)
{
std::cout << data << '\n';
}
};
As my predecessors wrote you have to use some kind of trait. You probably should use Boost for that, but if you don't want to you can just use something like this ( http://ideone.com/7mAiB ):
template <typename T>
struct has_const_iterator {
typedef char yes[1];
typedef char no[2];
template <typename C> static yes& test(typename C::const_iterator*);
template <typename> static no& test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template <bool> class bool2class {};
template <class T>
void set_inner(const std::string &path, T & var, bool2class<false> *) {
// T is probably not STL container
}
template <class T>
void set_inner(const std::string &path, T & var, bool2class<true> *) {
// T is STL container
}
template <class T>
void set(const std::string &path, T &var) {
set_inner(path, var, (bool2class<has_const_iterator<T>::value>*)0);
}
It's not an easy task to distinguish container from simple array, so I used here checking if type has const_iterator. Probably you should also check if it has begin(), end() and other things you would use in future code.