c++ template specialization of template container - c++

what i would do is like ...
template < template <typename ... > typename __Container, typename ... __Args >
ostream& operator<<(ostream& os, __Container<__Args ...> const& rhs){
stringstream ss;
int i = 0;
for (const auto& it : rhs)
ss << "idx[" << i << "] " << it << "\n";
return os << ss.str();
}
///
/// ... and something template specialization code
///
int main (){
std::vector<int> vec_data = { ... };
std::cout << vec_data << std::endl;
std::deque<int> dq_data = { ... };
std::cout << dq_data << std::endl;
std::map<std::string, double> map_data = { {}, {}, {}, ... };
std::cout << map_data << std::endl;
return 0;
}
in this example, deque and vector are not a problem, but when i tried to struggle to specialize std::map i dumped it. is there any possible way to do it?

Function templates can't be partially specialized, but can be overloaded:
template <typename ...P>
std::ostream &operator<<(std::ostream &os, const std::map<P...> &map)
But it's not the best idea, because you'd also have to specialize for std::multimap and std::unordered_[multi]map, and any non-standard containers you encounter in the future.
Instead you could create a function to print the single element, and overload it for std::pair (which is the element type of maps). Then call it from your operator.
There are more problems with the code:
Operators should only be overloaded where ADL can find them. Otherwise you won't be able to call them from some places (from any namespace that defines any operator<<; or from a template function defined above your operator).
Since the only suitable namespace here would be std, and adding declarations there is not allowed, you shouldn't create such operator in the first place. Write a function instead.
Identifiers containing __ or starting with _[A-Z] are reserved, don't use them.
The operator<< should accept any basic_ostream<...> to support wide streams.
stringstream should be ostringstream, or removed altogether (print directly to the ostream).

Related

Can I overload the insertion operator to take a templated-STL container? [duplicate]

This question already has answers here:
C++ print template container error (error: ambiguous overload for 'operator<<') understanding?
(4 answers)
Closed 3 years ago.
I have this example which is about STL containers so i'm reading about them, It gets so tedious for me to repeatedly use a range-for loop to print the content of a container. So I thought about overloading the insertion operator << thus I can write: std::cout << container << std::endl;.
template<class T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& v) {
for (const auto& e : v)
out << e << ", ";
return out;
}
int main() {
std::list<int> vi{ 10, 24, 81, 57, 2019 };
vi.pop_back();
std::cout << vi << std::endl; // 10, 24, 81, 57,
std::deque<std::string> names{ "Hello", "STL Containers" };
std::cout << names << std::endl; // error here. Bacause I've not overloaded << to take a std::deque<T>
}
As you can see above I find it so comfortable to print a list of some type. The problem is I can only print a list but not other sort of container like a vector or deque...
So how can I overload << to take a container of type T<U> or Should I specialize it for all the containers?
In a real example Shouldn't I do that?
You could provide the following overload :
template <class...>
using void_t = void;
template <class... Args>
constexpr bool exists_t_v(){
return std::is_same<void_t<Args...>, void>::value;
}
template <class Container, class = std::enable_if<
exists_t_v<
decltype(std::begin(std::declval<Container>())),
decltype(std::end(std::declval<Container>()))
>()
>>
std::ostream& operator<<(std::ostream& os, Container&& container){
for(const auto& e : std::forward<Container>(container))
os << e << ',';
return os;
}
This will let any instance container of the type Container for which std::begin(container) and std::begin(container) are defined to use the overload.
One of the issues with solutions like these is std::string support as well as any type that already provide its own overload for which std::begin() and std::end() are defined.
I hope that is not possible, I tried to put iterators in the operator<< but even they require to have exact iterator type, this is the closest possible solution using ostream_iterator:
template<class T2, class T>
void showT(ostream&out, T b, T end){
out << '[';
copy(b, end, ostream_iterator<T2>{out, ", "});
out<<"]";
return;
};

Define a template operator<< for iterables

I'm able to define and use:
std::ostream& operator<<(std::ostream& os, std::vector<int> const& container)
{
for (auto const& n : container)
os << n << ", ";
return os;
}
int main()
{
std::vector<int> data{0,1,2};
std::cout << data << '\n';
}
(demo)
But the definition of that operator doesn't depend on what kind of container I use. From there, I'd like to define a templated version:
template<class Iterable>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
for (auto const& n : iterable)
os << n << ", ";
return os;
}
int main()
{
std::vector<int> data{0,1,2};
std::cout << data << '\n';
}
(demo)
This is where my compiler gets angry and verbosily reject it:
error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'char')
... with a lot of possible candidates.
Why is it not legal and how could I define such an operator?
As stated in this other StackOverflow question, How do I fix “ambiguous overload” error when overloading operator<< (templated)?
, when defining operator<<(std::ostream&, T) for all T, you overload it for types where an existing operator<< exists. Hence the ambiguous call:
os << n << ", ";
^-- recursively calls itself? or calls the overload provided by the Standard Library?
The solution is to use SFINAE to ensure you define your overload only for iterable types. Since the definition of the range-based for loop is based on begin and end, we can use it to discriminate what is an Iterable:
template<class Iterable, class = std::void_t<decltype(begin(std::declval<Iterable>()))>>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
for (auto const& n : iterable)
os << n << ", ";
return os;
}
(demo)
Now, std::cout << data calls your version and std::cout << '\n' calls the build-in overload since the substitution fails for Iterable = char: begin(char) is not defined.

How to determine the type of the object an iterator is pointing to in c++ template function?

I have a template function in C++ that serializes an iterable:
template<typename Stream, typename Iter, typename Infix, typename Closure>
inline Stream &stream_iterable(Stream &os, Iter from, Iter to, Infix infix_, Closure open, Closure close) {
if (from == to) return os;
os << open << *from;
for (++from; from != to; ++from) {
os << infix_ << *from;
}
os << close;
return os;
}
For examaple, it basically converts std::vector<int>{1,2} to a string "[1,2]"
I would like to check the type of the object the iterator is pointing to and if it is std::string, I would like to use std::quoted to add quotation around the elements of the vector, Something like this:
template<typename Stream, typename Iter, typename Infix, typename Closure>
inline Stream &steam_iterable_quoted(Stream &os, Iter from, Iter to, Infix infix_, Closure open, Closure close) {
if (from == to) return os;
os << open << std::quoted(*from);
for (++from; from != to; ++from) {
os << infix_ << std::quoted(*from);
}
os << close;
return os;
}
How can I check the type of (*from) and combine these two functions into one?
You don't actually need to know the type in the body of stream_iterable. As the old saying goes, add a level of indirection:
namespace detail {
template<typename T>
constexpr T const& prepare_to_print(T const& t) { return t; }
auto prepare_to_print(std::string const s&) { return std::quoted(s); }
// A non-const overload would be required as well...
// Forwarding can be a drag sometimes
}
Just pass the dereferenced iterator to prepare_to_print. The nice thing about overloads is that you can customize the behavior further by adding more of them later on.
This simplest, working version:
namespace quoting {
template<class T>
T const& auto_quote( T const& t ) { return t; }
template<class C, class T>
auto auto_quote( std::basic_string<C,T> const& s ) {
return std::quoted(s);
}
// add more std types that need quoting here.
}
now do this:
// ...
if (from == to) return os;
using quoting::auto_quote;
os << open << auto_quote(*from);
for (++from; from != to; ++from) {
os << infix_ << auto_quote(*from);
}
os << close;
// ...
in order to make something be quoted differently, override auto_quote in the namespace of the type (if not in std; no injecting names into std allowed) or in namespace quoting. It will be picked up automatically.
So if you have your my_string in namespace my_ns:
namespace my_ns {
auto auto_quote( my_string const& s ) {
// some manual quoting code
}
}
and it will be picked up automatically by the above code.
You can use decltype(*from) to obtain the result type of the dereference operator, but (1) it may need to be further massaged before doing comparisons (it may be a reference, a value, possibly cv-qualified, ...), and (2) it's not really extensible WRT other types.
In general for this kind of stuff you want to delegate to a template function that (in your case) is responsible to actually output the datum on the stream, with a default for the "normal" types, and specialize it for types that require special treatment.
Use this to retrieve type of object referenced by an iterator: std::iterator_triats<SomeIteratorType>::value_type
To combine these two functions you can use constexpr if statement which is a C++17 feature.

Print any c++11 array with a non-member ostream-overloaded function

Is there a way in C++11 to let the compiler determine the templates of a function parameter? So I want to print a C++11-Array with only one stream-operator-function for all types and do not want to define same stream-operator-overloads just for another array type:
ostream& operator<<(ostream& stream, const array<float, 3>& v)
{
stream << v[0] << ", " << v[1] << ", " << v[2];
return stream;
}
I have tried something like this:
ostream& operator<<(ostream& stream, const array<auto, 3>& v)
{
stream << v[0] << ", " << v[1] << ", " << v[2];
return stream;
}
or
ostream& operator<<(ostream& stream, const array<typename...>& v)
{
stream << v[0] << ", " << v[1] << ", " << v[2];
return stream;
}
but the compiler doesn't compile these alternatives. Is there another nice solution or do I really have to define each time an overloaded function for each type?
You'd need a function template parametrised by the array type:
template <typename T>
ostream& operator<<(ostream& stream, const array<T, 3>& v)
The template parameter can be deduced from the function argument, so normal usage of the operator is fine:
cout << array<float, 3>{{1,2,3}} << endl;
The size can also be a parameter, if you want to support arbitrary arrays
template <typename T, size_t N>
ostream& operator<<(ostream& stream, const array<T, N>& v)
Directly overloading operators in std types is generally a bad idea.
A thin wrapper that does the job, however, works well:
// `tuple_printer` takes a type and a size. The size is by default
// deduced from the type.
template<class Tuple, size_t size = std::tuple_size< std::decay_t<Tuple> >{}>
struct tuple_printer {
// not used:
using indexes = std::make_index_sequence<size>;
// all but the last index: (C++14, but easy to write in C++11)
using pre_indexes = std::make_index_sequence<size-1>;
// store a forwarding reference to the tuple-like data:
Tuple&& tuple;
// The operator<<. Forwards to other methods.
friend std::ostream& operator<<(std::ostream& stream, tuple_printer&& self) {
std::move(self).print_to(pre_indexes{}, stream, ',');
std::move(self).print_to(std::index_sequence<size-1>{}, stream);
return stream;
}
// printing with a separator:
template<size_t...Is, class X>
void print_to(std::index_sequence<Is...>,std::ostream& stream, X&& x)&& {
// indexes trick, plus array trick. Looks prettier in C++17:
int _[]={0,((void)(
stream << std::get<Is>(std::forward<Tuple>(tuple)) << x
),0)...};
(void)_;
}
// printing without a separator:
template<size_t...Is>
void print_to(std::index_sequence<Is...>,std::ostream& stream)&& {
int _[]={0,((void)(
stream << std::get<Is>(std::forward<Tuple>(tuple))
),0)...};
(void)_;
}
};
// specialization for empty tuples:
template<class Tuple>
struct tuple_printer<Tuple,0> {
Tuple&& tuple;
friend std::ostream& operator<<(std::ostream& stream, tuple_printer&& self) {
return stream;
}
};
// function that does the type deduction for you:
template<class Tuple>
tuple_printer<Tuple> print_tuple(Tuple&& t){return {std::forward<Tuple>(t)};}
live example
Use looks like:
std::cout << print_tuple(array) << '\n';
where we wrap our array (or other tuple-like) with a function call, which does the pretty-print handling for us when streamed to a stream.
As a bonus, the above works with std::tuple and std::array and std::pair.
Overloading operator<< (or any other function that is meant to be found through ADL) outside of the namespace of its arguments is quite problematic and will end up failing some time. Since you cannot add overloads to the std namespace, and assuming that array means std::array, I would recommend against overloading the operator altogether.
You can print the contents of any container that has iterators with a simple algorithm:
std::copy(c.begin(), c.end(), std::ostream_iterator<T>(std::cout, " "));
Improving the style of the printout (changing the separator and not having it after the last item) takes a bit more time, but you should be able to write a simple named function for that and call it from wherever you are calling operator<< now. Yes, it does have some syntactic overhead, yes you should do it.

Overloading << for type inside another class

I have a typedef inside a class and I would like to overload the operator<< for it to be able to print it in ostream. However, the compiler cannot find the overloaded operator. How can I declare it so that it works?
#include <iostream>
#include <set>
using namespace std;
template <class C>
struct K {
typedef std::set<C> Cset;
Cset s;
// and many more elements here
friend ostream& operator<<(ostream& oo, const Cset& ss){
typename Cset::const_iterator it=ss.begin();
oo << "[";
for(; it!=ss.end(); ++it) oo << (*it) << ",";
oo << "]";
return oo;
}
void DoSomething(){
// do something complicated here
cout << s << endl;
// do something complicated here
}
};
int main(){
K <int> k;
k.s.insert(5);
k.s.insert(3);
k.DoSomething();
}
gcc version 4.4.5 20101112 (Red Hat 4.4.5-2) (GCC)
When a friend function is defined inline and there is no forward declaration outside the class, it is only found by ADL. However, your overload will never be found by ADL as it does not involve K arguments (note that K<int>::CSet is a typedef for std::set<C>).
Just for completeness: final version of the code for operator<<:
template <class T, class U>
std::ostream& operator<<(std::ostream& oo, const std::set <T,U> & ss){
typename std::set <T,U> ::const_iterator it=ss.begin();
oo << "[";
if(it!=ss.end()) oo << (*it++);
while(it!=ss.end()) oo << "," << (*it++);
oo << "]";
return oo;
}