Overload operator << for std :: tuple - possible simplifications?

I used the answer to the SO question "iteration over a tuple" to write a method for overloading << . This method has been tested and seems to work correctly with g++ 4.7 when compressing Debian.

However, this method is similar to circular motion, as it seems that << cannot be explicitly instantiated (I found a post about this here ). Thus, one of them is forced to define a string method, and then calls this. I have a similar method for a vector that is more direct. Anyone have suggestions on how to eliminate the extra step of creating a string method using the same approach or otherwise? Thanks in advance.

 #include <tuple> #include <iostream> #include <string> #include <sstream> #include <vector> using std::ostream; using std::cout; using std::endl; using std::vector; using std::string; // Print vector<T>. template<typename T> ostream& operator <<(ostream& out, const vector<T> & vec) { unsigned int i; out << "["; for(i=0; i<vec.size(); i++) { out << vec[i]; if(i < vec.size() - 1) out << ", "; } out << "]"; return out; } //////////////////////////////////////////////////////////////// // Print tuple. template<std::size_t I = 0, typename... Tp> inline typename std::enable_if<I == sizeof...(Tp), string>::type stringval(const std::tuple<Tp...> & t) { std::stringstream buffer; buffer << "]"; return buffer.str(); } template<std::size_t I = 0, typename... Tp> inline typename std::enable_if<I < sizeof...(Tp), string>::type stringval(const std::tuple<Tp...> & t) { std::stringstream buffer; size_t len = sizeof...(Tp); if(I==0) buffer << "["; buffer << std::get<I>(t); if(I < len - 1) buffer << ", "; buffer << stringval<I + 1, Tp...>(t); return buffer.str(); } template<typename... Tp> ostream& operator <<(ostream& out, const std::tuple<Tp...> & t) { out << stringval(t); return out; } int main() { typedef std::tuple<int, float, double> T; std::tuple<int, float, double> t = std::make_tuple(2, 3.14159F, 2345.678); cout << t << endl; } 

When compiled, this gives

 [2, 3.14159, 2345.68] 
+6
source share
4 answers

You can simply pass std::ostream& to this stringval function and use out << instead of buffer << .

Demo :

 #include <tuple> #include <iostream> #include <type_traits> template <size_t n, typename... T> typename std::enable_if<(n >= sizeof...(T))>::type print_tuple(std::ostream&, const std::tuple<T...>&) {} template <size_t n, typename... T> typename std::enable_if<(n < sizeof...(T))>::type print_tuple(std::ostream& os, const std::tuple<T...>& tup) { if (n != 0) os << ", "; os << std::get<n>(tup); print_tuple<n+1>(os, tup); } template <typename... T> std::ostream& operator<<(std::ostream& os, const std::tuple<T...>& tup) { os << "["; print_tuple<0>(os, tup); return os << "]"; } 
+4
source

Non-recursive 17-position solution in C ++ based on fold expressions (C ++ 17), index sequences (C ++ 14), lambda functions and template parameter packages (like C ++ 11):

 #include <tuple> #include <iostream> #include <ostream> #include <utility> template< typename F, typename ...types > F for_all(F f, types &&... values) { (f(std::forward< types >(values)), ...); return std::move(f); } template< typename F, typename ...types, std::size_t ...indices > F for_all_indices(F f, std::tuple< types... > const & t, std::index_sequence< indices... >) { return for_all(std::move(f), std::get< indices >(t)...); } template< typename first, typename ...rest > // non-nullary tuples only std::ostream & operator << (std::ostream & out, std::tuple< first, rest... > const & t) { //return ((out << std::get< first >(t)) << ... << std::get< rest >(t)); // simply prints extracted tuple elements w/o delimiters out << '['; for_all_indices([&out] (auto const & value) { out << value << ", "; }, t, std::index_sequence_for< rest... >{}); return out << std::get< sizeof...(rest) >(t) << ']'; } int main() { std::cout << std::make_tuple(1, 2.F, 3.0) << std::endl; return 0; } 

Live demo

+2
source

You probably don't need C ++ 17 (which has not yet been released) to get a non-recursive (actually recursive, but more natural) solution. For example, you do not need smoothed expressions and only indexes (C ++ 14) and template parameter packages (C ++ 11) are needed.

 #include <iostream> #include <sstream> #include <utility> #include <tuple> #include <string> template<class T> std::ostringstream& concat_to_stream(std::ostringstream &oss, T &&arg) { oss << arg; return oss; } template<class First, class ...Rest> std::ostringstream& concat_to_stream(std::ostringstream &oss, First &&firstArg, Rest &&... restArgs) { oss << firstArg << ", "; return concat_to_stream(oss, std::forward<Rest &&>(restArgs)...); } template<class ...Types> std::string concat_to_string(Types &&... args) { std::ostringstream oss; oss << '['; concat_to_stream(oss, std::forward<Types &&>(args)...); oss << ']'; return oss.str(); } template<class Tuple, size_t... Indices> std::string help_concat(const Tuple &tuple, std::index_sequence<Indices...>) { return concat_to_string(std::get<Indices>(tuple)...); }; template<class ...Types> std::string tuple_to_string(const std::tuple<Types...> &tuple) { return help_concat(tuple, std::make_index_sequence<sizeof...(Types)>{}); }; template<class ...Types> std::ostream &operator<<(std::ostream &os, const std::tuple<Types...> &tuple) { return os << tuple_to_string(tuple); } int main() { std::tuple<int, double, std::string> sample_tuple = std::make_tuple(3, 1.723, "Hi!"); std::cout << sample_tuple << '\n'; // [3, 1.723, Hi!] return 0; } 

The recursive part is the concat_to_stream part, which is quite natural and common. The key part of help_concat that I learn from Implementing std :: tuple From The Ground Up: Part 6, tuple_cat Take 1 .

The method consists in using the fictitious std::index_sequence in the parameter list to output size_t... Indices in the template parameter list, which allows us to "tightly" the contents of std::tuple into a list of variable parameters, which can be accepted using the concat_to_string function.

+1
source

Here is a non-recursive version using std :: integer_sequence and some other methods related to it.

 template<class Ch, class Tr, class Tuple, std::size_t... Is> void print_tuple_impl(std::basic_ostream<Ch,Tr>& os, const Tuple& t, std::index_sequence<Is...>) { using swallow = int[]; (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...}; } template<class Ch, class Tr, class... Args> decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os, const std::tuple<Args...>& t) { os << "("; print_tuple_impl(os, t, std::index_sequence_for<Args...>{}); return os << ")"; } 

initially it from here: http://en.cppreference.com/w/cpp/utility/integer_sequence

0
source

Source: https://habr.com/ru/post/908249/


All Articles