How would you go about writing a library that, when included, it overloads the << operator for any* existing STL container?
Except adaptors like stack and queue, since you cannot iterate through them.
The one self-imposed requirement is that it mustn't include any of the containers' header files. That would bloat the final executable unnecessarily. It makes more sense to include this header after the containers' that I would like to work with. This restriction implies the use of templates or macros.
I'm looking for advice and pointers, so, please, don't just post fully working code that can do this! Implementing this myself is part of the learning process. Small snippets of code that demonstrate how certain things work are welcome.
What I did up to this point:
I have overloaded the << operator for every container with a different template signature. The problem that I have run into with this approach is, that there are containers that have the same number of template parameters, but some contain std::pair-s, others single values. More precisely, this is the case of std::map & std::multimap vs std::unordered_set & std::unordered_multiset.
This collision forces me to either implement a non-template version of one of the pairs, or to come up with a way to differentiate between std::pair-s and single values. I don't really know how to go about doing the second one, though. The reason because I'm not "how to do that" directly is that I'm starting to believe that this could be avoided entirely with a better overall design.
Thank you in advance!
What worked for me:
Overload operator<< for std::pair
Overload operator<< for every STL container with different template arguments
Note:
template <typename T1> and template <typename T1, typename T2> are different
template <typename T1, typename T2> and template <typename T1, size_t T2> are
different
template <typename T1, typename T2> and template <typename C1, typename C2>
are NOT different
Put them in a namespace to make sure that your operators won't collide with any other operators that you might need in the future.
Use "templates in templates", so for example a function would look like:
template <typename Type, template <typename TYPE> class TClass>
void func(TClass<Type>& tc) {
if (tc.somethingTrue())
tc.doStuff();
}
You could overload operator<< as a template that takes a template template argument (i.e., any container).
Then you could provide two overloads of a template function (e.g., print) that the one overload would specialize on std::pair.
template<typename T>
std::ostream& print(std::ostream &out, T const &val) {
return (out << val << " ");
}
template<typename T1, typename T2>
std::ostream& print(std::ostream &out, std::pair<T1, T2> const &val) {
return (out << "{" << val.first << " " << val.second << "} ");
}
template<template<typename, typename...> class TT, typename... Args>
std::ostream& operator<<(std::ostream &out, TT<Args...> const &cont) {
for(auto&& elem : cont) print(out, elem);
return out;
}
LIVE DEMO
A very simple attempt at this problem is something like:
template<typename T>
void print_container(std::ostream& os, const T& container, const std::string& delimiter)
{
std::copy(std::begin(container),
std::end(container),
std::ostream_iterator<typename T::value_type>(os, delimiter.c_str()));
}
Related
In order to be able to write stuff like cout << vec for iterable types like vector<int> vec;, I wanted to define operator<<(osteam&, const T&) for iterable T only. My first shot was
template<class T> using extract_iterator_t = typename T::iterator;
template<class T> concept iterable = requires { typename extract_iterator_t<T>; };
template<iterable T>
ostream& operator<<(ostream& os, const T& seq) {
for(const auto& i: seq) os << i << ',';
return os;
}
but that misses iterable things like int*, so I replaced the first line by
template<class T> struct extract_iterator { using type = T::iterator; };
template<class T> struct extract_iterator<T*> { using type = T*; };
template<class T> struct extract_iterator<T[]> { using type = T*; };
template<class T> using extract_iterator_t = typename extract_iterator<T>::type;
However, now g++-11.1 refuses to compile this, saying
error: 'char' is not a class, struct, or union type
because it tries to use my custom operator<< for the <<',' part, rightfully complaining about a substitution failure. However, I always thought Substitution Failure Is Not An Error, so why would g++ insist on using my custom operator<< for <<','?
Here's the godbolt: https://godbolt.org/z/jejexE18h
Edit: everyone pointing out ranges is correct that they are the right tool to use for this, but this example was just to illustrate a problem I have with a construct similar but not equal to ranges - I thought I'd present a minimum breaking example instead of telling a long introductory story which boils down to the same problem.
The issue is that in your primary definition:
template <class T> struct extract_iterator { using type = T::iterator; };
the failure that happens when we try to do T::iterator is outside of the immediate context of the substitution, so it is not SFINAE-friendly.
You could fix this by adding the right constraint:
template<class T> struct extract_iterator;
template<class T> requires requires { typename T::iterator; }
struct extract_iterator<T> { using type = T::iterator; };
But this whole approach is wrong anyway. T* isn't iterable, it's an iterator. And C++20 already comes with the tool you need for this job: Ranges:
template <std::ranges::range R>
std::ostream& operator<<(std::ostream& os, R&& seq);
Note that this is problematic anyway for other reasons, and you should instead just use fmt which comes with direct support for formatting ranges in a proper way.
I'm trying to learn a bit about templates and metafunctions, namely std::enable_if. I'm making a menu system for our school assignments (extracurricular, mind you), and need a way of getting input from the user. I'd like to define a template class for various types of input - something used along the lines of:
std::string userInput = Input<std::string>("What's your name?").Show();
float userHeight = Input<float>("How tall are you?").Show();
I'd like to (and I'm sure there are reasons not to, but nevertheless) do this generalized sort of conversion using a std::stringstream: get input from user, feed into SS, extract into variable of type T.
It's easy enough to see if the conversion failed during runtime, but I'd like to use std::enable_if to prevent people from using my Input<> class for cases where conversion is impossible, say:
std::vector<Boats> = Input<std::vector<>>("Example").Show();
Obviously a std::stringstream cannot convert a string to a vector, so it will always fail.
My question is this:
Can I format an std::enable_if clause to ONLY allow instantiation of my template class for the types listed above? Alternatively, is there a better way to go about it? Have I got things completely the wrong way around?
What I've done so far
I believe I have found a list of allowed types that std::stringstream can "convert" a string into:
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/
I've been using std::enable_if like this up until this point:
template <typename T, typename = typename
std::enable_if<std::is_arithmetic<T>::value, T>::type>
However, now I'd like to extend it to allow not only arithmetic values, but all values supported by the sstream >> operator.
If you prefer to use a SFINAE with a class template parameter, then you want
template <
typename T,
typename = decltype(std::declval<std::istringstream &>() >> std::declval<T &>(), void())
>
class Input /*...*/
I think that you are trying to use std::enable_if for something which doesn't require it. If your template function already relies on operator<< applied on a generic type T, then compilation will fail in any case if the operator is not specialized for that type.
Nothing prevents you from using std::enable_if to solve your specific problem, though that may be not the best way to do it.
If C++20 concepts were already largely adopted I'd say that that would be your way to go.
You could go the way as suggested here on SO and implement a class is_streamable which can check for that like this:
#include <type_traits>
#include <utility>
#include <iostream>
#include <sstream>
template<typename S, typename T>
class is_streamable
{
template<typename SS, typename TT>
static auto test(int)
-> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
template<typename, typename>
static auto test(...)->std::false_type;
public:
static const bool value = decltype(test<S, T>(0))::value;
};
class C
{
public:
friend std::stringstream& operator<<(std::stringstream &out, const C& c);
};
std::stringstream& operator<<(std::stringstream& out, const C& c)
{
return out;
}
int main() {
std::cout << is_streamable<std::stringstream, C>::value << std::endl;
return 0;
}
This would return one if the operator is implemented and zero if not.
With that you can alter your snippet to
template <typename T, typename = typename
std::enable_if<is_streamable<std::stringstream, C>::value, T>::type>
There are several thing you want:
a trait, is_streamable
a way to forbid class instantiation.
For the traits, you might use std::experimental_is_detected or run your own:
template <typename T>
auto is_streamable_impl(int)
-> decltype (T{},
void(), // Handle evil operator ,
std::declval<std::istringstream &>() >> std::declval<T&>(),
void(), // Handle evil operator ,
std::true_type{});
template <typename T>
std::false_type is_streamable_impl(...); // fallback, ... has less priority than int
template <typename T>
using is_streamable = decltype(is_streamable_impl<T>(0));
Then to forbid intantiation, several choices:
static_assert:
template <typename T>
class Input
{
static_assert(is_streamable<T>::value);
// ...
};
or SFINAE friendly class:
template <typename T, typename = std::enable_if_t<is_streamable<T>>>
class Input
{
// ...
};
so you allow to know if Input<T1> is valid.
Notice that without all that stuff, your program won't compile anyway when instantiating the problematic method (hard error, so no SFINAE friendly).
Being SFINAE friendly is not necessary most of the time.
I'm writing a library for which each value type can be converted to string using a to_string() free function.
I'd like to enable std::ostream& operator<<(std::ostream&, _) for all types T for which to_string(T) is valid. Here is my try:
namespace mylibrary {
// All my types are declared in the `mylibrary` namespace so that ADL is happy.
template <typename T>
inline std::string to_string(const T& value) {
// Return a string for value. Doesn't really matter how.
}
template <typename T>
inline std::ostream& operator<<(std::ostream& os, const T& value) {
return os << to_string(value);
}
}
This actually works... but a bit too well: when resolving os << to_string(value) my templated operator<<() is picked up as candidate even for std::string which sadly makes the call ambiguous an ends up in a compiler error.
I tried using std::enable_if<> to conditionnaly disable my operator<< but sadly I couldn't get something that compiles.
How can I restrict my operator<<() for types for which to_string(T) is a valid expression ?
Alternatively, is there way to restrict my operator<<() for types which are defined in my namespace ?
You can use expression-SFINAE to restrict the template overload set. For example like this:
#include <string>
#include <type_traits>
template <typename> using void_t = void;
template <typename T, typename = void_t<decltype(to_string(std::declval<T>()))>>
std::ostream& operator<<(std::ostream& os, const T& value) {
return os << to_string(value);
}
(You should probably wrap this up somehow so as not to have a visible second template parameter in your public template; users will discover and abuse it.)
namespace details{
template<template<class...>class,class,class...>
struct can_apply:std::false_type{};
template<template<class...>class Z,class...Ts>
struct can_apply<Z,std::void_t<Z<Ts...>>,Ts...>:
std::true_type
{};
}
template<template<class...>class Z,class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
is a small metaprogramming library that gives you the can_apply<template, types...> trait. It uses C++1z std::void_t which is easy to write if your compiler lacks it.
template<class X>
using to_string_t=decltype(to_string(std::declval<X>()));
is a SFINAE enabled "type of calling to_string" (either string or error, we hope). We stitch together:
template<class X>
using can_to_string=can_apply<to_string_t,X>;
and we have a trait can_to_string which is true iff you can to_string. Both of the above two using templates shoud be in the same namespace as your to_string for built-in types for adl reasons.
Now we can write our <<:
template <class T,class=std::enable_if_t<can_to_string<const T&>{}>>
inline std::ostream& operator<<(std::ostream& os, const T& value) {
return os << to_string(value);
}
The modest metaprogramming library overhead makes the final code much cleaner than alternatives.
enable_if_t is C++14 but also easy to write in C++11. You may have to replace {} with ::value in C++11 within the enable_if as well.
The result_t using alias is one line, the can_ alias is another, and then we have a friendly can_ alias for almost any bit of C++ code, which allows for clean enable_if SFINAE.
All of this can be done on one line, but I find the resulting << interface to be less than clear in what the requirements are.
Consider these two functions:
template <class Type,
class = typename std::enable_if</*HAS OPERATOR <<*/>::type>
void f(std::ostream& stream, const Type& value);
template <class Type,
class... DummyTypes,
class = typename std::enable_if<sizeof...(DummyTypes) == 0>::type>
void f(std::ostream& stream, const Type& value, DummyTypes...);
As the non-variadic overload has the priority over the variadic overload, I want to check whether the type has the operator<< with an std::ostream using std::enable_if in the first version.
So what should I write instead of /*HAS OPERATOR <<*/ ?
The following should work
template <class Type,
class = decltype(std::declval<std::ostream&>() << std::declval<Type>())>
void f(std::ostream& stream, const Type& value)
{
stream << value;
}
(note you don't need to use std::enable_if in this case)
Using trailing return type (1), you can actually have a foretaste of concepts:
template <typename Type>
auto f(std::ostream& out, Type const& t) -> decltype(out << t, void()) {
// ...
}
Because of SFINAE, this overload can only be selected if the type of out << t can be resolved, and this implies that an overload of << exists that accepts both parameters.
The one pitfall is that this does not work if you need the contrary, that is enabling a function if this overload does not exists. In this case an enable_if strategy (and the symmetric disable_if) is necessary, as far as I know.
(1) thanks to Simple for helping out with the syntax
It is easiest to check when you have the arguments around, i.e., I would rather try to use something like this:
template <typename Type>
auto f(std::ostream& out, Type const& value)
-> typename std::enable_if<sizeof(out << value) != 0>::type {
...
}
A similar effect could be obtained using std::declval() but off-hand I'm not sure about creating references.
So, I'm developing a templated Vector class for an assignment and I came across a few issues that I solved by Googling, but I still want to know why what I was doing was wrong. First issue is that I had:
template <typename T>
class Vector
{
...
template <typename T2>
Vector(const Vector<T2> &other);
}
template <typename T, T2>
Vector<T>::Vector(const Vector<T2> &other)
{
...
}
this was giving me an "unable to match function definition to an existing declaration" in VS11. I fixed it by putting the template definitions on separate lines:
template <typename T>
template <typename T2>
Vector<T>::Vector(const Vector<T2> &other)
{
...
}
but I still don't know why this was needed. I know that the first definition is valid for functions/classes that use multiple templates in them, but why is it the case that the syntax changes when you mix a templated class with a templated member function?
My second question has to do with types inside of template classes. When writing the iterator I had functions such as:
template <typename T>
class Vector
{
...
class iterator
{
...
iterator &operator++(void);
}
}
template <typename T>
Vector<T>::iterator &Vector<T>::iterator::operator++(void)
{
...
}
which gave me "dependent name is not a type" and I later found out that I needed to add "typename" in front:
template <typename T>
typename Vector<T>::iterator &Vector<T>::iterator::operator++(void)
{
...
}
After Googling the warning number (which resulted in an error), I realized why the error exists, but it isn't too obvious why the compiler doesn't know that Vector<T>::iterator is a type. I mean, it has the definition of the class, so...
Anyway, thanks for clarifying these few things for me!
template <typename T>
template <typename T2>
Vector<T>::Vector(const Vector<T2> &other)
{
Is also valid to be written as
template <typename T> template <typename T2>
Vector<T>::Vector(const Vector<T2> &other)
{
You just need (?) to write it out as two sets, since they are after all two sets of parameters - first one is for the class, second one for the function.
typename for dependant types and related rules (this for template base class members and template for template member functions(?) ) have to do with something called "Two Phase Lookup". This is however badly implemented (as in not) on MSVC++, so it may not throw as many errors as a conforming implementation might.
More info - http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html
http://womble.decadent.org.uk/c++/template-faq.html#disambiguation
http://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates/