I'm trying to put together a very simple logging class which treats certain types, and particularly vectors, specially. I want to have a default behaviour when using the << operator, but modify it in certain cases. The set-up is:
class LoggerStream
{
template <typename ArgType>
LoggerStream & operator<< (const ArgType &arg)
{
// Standard logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
return *this;
}
template <typename DataType>
LoggerStream & operator<< (const arma::Col<DataType> &arg)
{
// Specialised logging logic
return *this;
}
LoggerStream & operator<< (const double &arg)
{
// Specialised logging logic
return *this;
}
// Other stuff...
};
The double case works fine. The problem is that for subclasses of the vector types, the general-purpose template seems to take priority, and the more specific template gets ignored.
Since all three templated cases have just one generic template parameter I guess the the vector cases aren't considered the most specialised, but if it were considered ambiguous I would expect a compiler error. (It compiles just fine.) So how can I indicate a specialisation but still generalise over the type of the vector elements? Thanks in advance.
I guess this is to do with some detail of how the Col class is implemented. I'm using (and aliasing) arma::Col<T>::fixed<N> as well, but writing a specific overload for that doesn't seem to help. Any ideas welcome.
I cannot reproduce it. This code works as it is supposed to:
#include <iostream>
#include <vector>
using namespace std;
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
// Standard logging logic
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
// Specialised logging logic
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
vector<int> v;
foo << v; // calling the vector specialization
}
I'm answering my own question not because I have a resolution (yet), but to clarify the problem after further investigation. In fact this doesn't seem to be specific to arma::Col; rather, the issue seems to be in the precedence given to the more specific overload when the argument is of a subclass type. A simple example that shows the problem without using Armadillo is
#include <iostream>
#include <vector>
using namespace std;
template <typename DataType>
class MyVector : public std::vector<DataType> {};
class LoggerStream
{
public:
template <typename ArgType>
LoggerStream &operator<< (const ArgType &arg)
{
cout << "In general" << endl;
return *this;
}
template <typename DataType>
LoggerStream &operator<< (const std::vector<DataType> &arg)
{
cout << "In vector" << endl;
return *this;
}
};
int main()
{
LoggerStream foo;
std::vector<float> v;
foo << v; // Prints "In vector", as expected
MyVector<float> w;
foo << w; // Prints "In general" (but only if the general case exists)
}
So, using the subclass MyVector produces a call to the general function, not the specialised one. But, if the first version is commented out then both calls produce "In vector". So for some reason both are deemed suitable, but the general function is preferred.
What I still don't know is how to stop this from happening without explicitly writing out a load of specific overloads for individual subclass cases...
Related
I'm using Catch2 with TEST_CASE blocks from within I sometimes declare local temporary struct for convenience. These struct sometimes needs to be displayed, and to do so Catch2 suggests to implement the << operator with std::ostream. Unfortunately, this becomes quite complicated to implement with local-only struct because such operator can't be defined inline nor in the TEST_CASE block.
I thought of a possible solution which would be to define a template for << which would call toString() instead if that method exists:
#include <iostream>
#include <string>
template <typename T>
auto operator<<(std::ostream& out, const T& obj) -> decltype(obj.toString(), void(), out)
{
out << obj.toString();
return out;
}
struct A {
std::string toString() const {
return "A";
}
};
int main() {
std::cout << A() << std::endl;
return 0;
}
I have a few questions:
Is the decltype trick modern C++ or can we achieve the same using <type_traits> instead?
Is there a way to require for the toString() returned value to be a std::string and thus disable the template substitution otherwise?
Is it guaranteed that a class with a concrete implementation of operator<< will be prioritized over the template if it exists?
Also, I find this solution to be quite fragile (I get errors when compiling my overall project although this simple snippet works), and I think it can lead to errors because of its implicit nature. Unrelated classes may define toString() method without expecting it to be used in << template substitution.
I thought it might be cleaner to do this explicitly using a base class and then SFINAE:
#include <iostream>
#include <string>
#include <type_traits>
struct WithToString {};
template <typename T, typename = std::enable_if_t<std::is_base_of_v<WithToString, T>>>
std::ostream& operator<<(std::ostream& out, const T& obj)
{
out << obj.toString();
return out;
}
struct A : public WithToString {
std::string toString() const {
return "A";
}
};
int main() {
std::cout << A() << std::endl;
return 0;
}
The downside of this solution is that I can't define toString() as a virtual method in the base class otherwise it prevents aggregate initialization (which is super-useful for my test cases). Consequently, WithToString is just an empty struct which serves as a "marker" for std::enable_if. It does not bring any useful information by itself, and it requires documentation to be properly understood and used.
What are your thoughts on this second solution? Can this be improved somehow?
I'm targeting C++17 so I can't use <concepts> yet unfortunately. Also I would like to avoid using the <experimental> header (although I know it contains useful stuff for C++17).
You can think of both methods as "operator<< on all types with some property".
The first property is "has a toString()" method (and will work in C++11 even. This is still SFINAE, in this case the substitutions are in the return type). You can make it check that toString() returns a std::string with a different style of SFINAE:
template <typename T, std::enable_if_t<
std::is_same_v<std::decay_t<decltype(std::declval<const T&>().toString())>, std::string>,
int> = 0>
std::ostream& operator<<(std::ostream& out, const T& obj)
{
out << obj.toString();
return out;
}
And a non-template operator<< will always be chosen before this template. A more "specialized" template will also be chosen before this one. The rules for overload resolution are a bit complex, but they can be found here: https://en.cppreference.com/w/cpp/language/overload_resolution#Best_viable_function
The second property is "derives from WithToString". As you guessed, this one is more "explicit", and it is harder to accidentally/unexpectedly use the operator<<.
You can actually define the operator inline, with a friend function:
struct A {
std::string toString() const {
return "A";
}
friend std::ostream& operator<<(std::ostream& os, const A& a) {
return os << a.toString();
}
};
And you could also have this friend declaration in WithToString, making it a self-documenting mixin
template<typename T> // (crtp class)
struct OutputFromToStringMixin {
friend std::ostream& operator<<(std::ostream& os, const T& obj) {
return os << obj.toString();
}
};
struct A : OutputFromToStringMixin<A> {
std::string toString() const {
return "A";
}
};
Consider the following example code for overloading the operator<< for a class A:
#include <iostream>
class A {
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
friend A &operator<<(A &a, const std::string &t)
{
return operator<<<std::string>(a, t + "x");
}
};
My intention is that the second operator explicitly calls the first one.
However, in g++ 7.4 this fails with
In function 'A& operator<<(A&, const string&)':
error: 'operator<<' not defined
return operator<<<std::string>(a, t + "x");
^~
error: expected primary-expression before '>' token
return operator<<<std::string>(a, t + "x");
^
I however do not see why this should not compile.
Here is the code in godbolt.
In-class friend functions are not exposed to any scope. Friend injection used to be a thing (before ADL was invented), but now there is no way to call them except with ADL unless you declare them beforehand. In this case, a workaround is to declare the template function outside the class beforehand.
class A;
template <typename T>
A &operator<<(A &a, const T &t);
Call a function instead of calling an operator.
#include <iostream>
class A {
template <typename T>
static A &print(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
return print(a, t);
}
friend A &operator<<(A &a, const std::string &t)
{
return print(a, t + "x");
}
};
You seem to wish to specialize your template function, but you're not doing it quite right. It should look more like this:
template <> friend A& operator<< <std::string>(A &a, const std::string &t)
{
// Print in here some how. It's not exactly clear to me how you intend to
// do this, as doing something like a << t will create infinite recursion
// finally, return a
return a;
}
Your other option is to switch the order of the functions, creating the template function after you create your first function:
friend A &operator<<(A &a, const std::string &t)
{
// Again, still not sure what you want to do here
// I just want to stress again though, don't do something
// like a << t, or operator<<(a,t)
// That will crash hard and fast, as there is no way to resolve
// it. It will create infinite recursion
return a;
}
template <typename T>
friend A &operator<<(A &a, const T &t)
{
std::cout << t << std::endl;
return a;
}
My intention is that the second operator explicitly calls the first one.
So, first, you will need to actually need the first option in that case.
Second, to do this, you will need to choose a type for t. You would do it like this:
operator<< <SomeType>(a,t);
Keep in mind, t will need to be implicitly converted to SomeType. Else, SomeType needs to be created by calling its constructor:
operator<< <SomeType>(a,SomeType(/* parameters to construct a SomeType ... */));
Note: Doing something like operator<< <SomeType>(a,t + "x") will always become infinitely recursive, and ultimately crash. This is because t + "x" is always an std::string. That means the compiler will always call this overload of the function infinitely, until it finally crashes from a stack overflow. So don't do that.
I'm trying to get up to speed on "modern" C++ particularly using templates. I've got a class Point that overrides the << operator. Works fine. So then I decided to add nice print out for containers in general.
My question - is there a way write a template for multiple container types similar to the code below?
template <typename T>
std::ostream& operator <<(ostream& os, const vector<T>& v)
{
os << "\n";
for( auto x : v ) { os << "\n\t" << x; }
os << "\n";
return os;
}
The code above correctly outputs vectors in a multi-line format as long as T has an << operator. I'd like to make this work for other containers like lists etc.
I also realize that it's probably a bad idea (or at least rude) to override output for containers in a general way for all types. So ultimately the template code above would have the typename hard coded/restricted to 'Point' and a template-ed container.
OK, implimenting AndyG's suggestion I have the following full code:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
struct Point {
double x, y, z;
Point() : x(0), y(0), z(0) {};
Point(double a, double b, double c) : x(a), y(b), z(c) {}
Point(double a[]) : x(a[0]), y(a[0]), z(a[0]) {}
friend std::ostream& operator <<(ostream& os, const Point&p)
{
os << p.x << ", " << p.y << ", "<< p.z;
return os;
}
};
template <template<class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{
os << "\n";
for( auto x : v ) { os << "\n\t" << x; }
os << "\n";
return os;
}
vector<Point> vp = { { 1, 2, 3 },
{ 5, 7, 4 },
{ 8, 2, 5 }
};
int main(int argc, char* argv[])
{
cout << vp[0]; // works
cout << vp; // dosen't work,
}
But still no luck. The compiler can't match operator<< with 'std::vector'
I had tried many variations of the template <template> prior to my first post with about the same results. I can get a template that compiles but the compiler fails to match the operator function and use the template.
I wasn't aware of the variadic template pack, obviously a good idea but doesn't solve the problem.
OK, AndyG set me on the right track and I think I understand what is going on.
The following code doesn't work because the std::container templates also have an allocator parameter which has a default and you rarely need to use it. Hence we think of most container templates of taking just the type/class that they will contain.
This
template < template <class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
{ ... }
doesn't work because the template we are going to call C in our operator<< function takes two parameters not one. Therefore the compiler won't find a match.
This will work:
template < template <class, class> class C, class... T>
std::ostream& operator <<(ostream& os, const C<T...>& v)
because the compiler can match vector<Point, Alloc> to template<class, class> which we choose to call C. It can then use our function template to generate an overload for operator <<.
Note that there are a number of things wrong with this approach in general. The template <class, class> isn't going to match a std::map which takes 4 parameters, 2 of which are defaulted. Even worse it will match any two argument template that may or may not be a container.
We can fix this by using a varg parameters: template <class, class...> however we are still screwed because now the << function template is dealing with std::pairs from the iterator which the compiler doesn't know how to cout <<.
So while this was a useful exercise for me to understand templates better, don't do it.
This link has a neat container pretty print lib: Pretty-print C++ STL containers
This question already has answers here:
std::endl is of unknown type when overloading operator<<
(6 answers)
Closed 10 years ago.
I'm currently writing a wrapper for an std::stringstream and i want to forward all the operator<< calls through my class to the std::stringstream. This works just fine now
(thanks to this question: wrapper class for STL stream: forward operator<< calls), but there is still one issue with it.
Let's say I have the following code:
class StreamWrapper {
private:
std::stringstream buffer;
public:
template<typename T>
void write(T &t);
template<typename T>
friend StreamWrapper& operator<<(StreamWrapper& o, T const& t);
// other stuff ...
};
template<typename T>
StreamWrapper& operator<<(StreamWrapper& o, T const& t) {
o.write(t);
return o;
}
template<typename T>
void StreamWrapper::write(T& t) {
// other stuff ...
buffer << t;
// other stuff ...
}
If I now do this:
StreamWrapper wrapper;
wrapper << "text" << 15 << "stuff";
This works just fine. But if I want to use the stream modifiers like std::endl, which is a function according to http://www.cplusplus.com/reference/ios/endl, I simply doesn't compile.
StreamWrapper wrapper;
wrapper << "text" << 15 << "stuff" << std::endl;
Why? How can I forward the stream modifiers too?
See this answer.
You'll want
typedef std::ostream& (*STRFUNC)(std::ostream&);
StreamWrapper& operator<<(STRFUNC func) // as a member, othewise you need the additional StreamWrappe& argument first
{
this->write(func);
return *this;
}
You seem to be doing a bit of extra work. I normally use:
class StreamWrapper
{
// ...
public:
template <typename T>
StreamWrapper& operator<<( T const& obj )
{
// ...
}
};
With a modern compiler, this should work for all concrete
types. The problem is that manipulators are template functions,
so the compiler is unable to do template argument type
deduction. The solution is to provide non-template overloads
for the types of the manipulators:
StreamWrapper& operator<<( std::ostream& (*pf)( std::ostream& ) )
{
// For manipulators...
}
StreamWrapper& operator<<( std::basic_ios<char>& (*pf)( std::basic_ios<char>& )
{
// For manipulators...
}
Type deduction will fail for the manipulators, but the compiler
will pick up these for function overload resolution.
(Note that you might need even more, for things like std::setw(int).)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
std::endl is of unknown type when overloading operator<<
Operator overloading
I'm currently programming a logger class, but the operator<< method causes a compiler error. Here's a minimized version of the class, in file "logger.h":
#include <iostream>
class Logger {
public:
Logger() : m_file(std::cout) {}
template <typename T>
Logger &operator<<(const T &a) {
m_file<<a;
return *this;
}
protected:
std::ostream& m_file;
};
It is included in my main.cpp and works perfecly when I output a string literal:
log << "hi";
However, the following won't compile.
#include "logger.h"
int main() {
Logger log;
log << std::endl;
}
The g++ compiler reports:
src/main.cpp:5: error: no match for 'operator<<' in 'log << std::endl'
Your problem is not about the chain of << , a single log << endl would also cause the problem. It is because std::endl is a template function:
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
One of the overload of operator<< in basic_ostream is:
template <class charT, class traits = char_traits<charT> >
class basic_ostream : virtual public basic_ios<charT,traits> {
public:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
//...
};
So the template parameters can be deduced when std::cout<<std::endl is used. However, when the left side is the class Logger, the compile cannot deduce the template parameters of endl. Explicitly give the template parameters can let program compile and work:
#include <iostream>
class Logger
{
public:
std::ostream &m_file;
Logger(std::ostream &o = std::cout):m_file(o){};
template <typename T>
Logger &operator<<(const T &a) {
m_file<<a;
return *this;
}
};
int main()
{
Logger log;
log<<std::endl<char, std::char_traits<char> >;
log<<"hi"<<" stackoverflow"<<std::endl<char, std::char_traits<char> >;
return 0;
}
Or you can add a new overload of operator<< in class Logger to let compiler deduce the template parameters of std::endl:
#include <iostream>
class Logger
{
public:
std::ostream &m_file;
Logger(std::ostream &o = std::cout):m_file(o){};
template <typename T>
Logger &operator<<(const T &a) {
m_file<<a;
return *this;
}
Logger &operator<<(std::ostream& (*pf) (std::ostream&)){
m_file<<pf;
return *this;
}
};
int main()
{
Logger log;
log<<std::endl;
log<<"hi"<<" stackoverflow"<<std::endl;
return 0;
}
Also, if you don't need the output to be flushed immediately, you can use '\n' instead of endl.
The error is caused by std::endl which is a function. Refer to:
std::endl is of unknown type when overloading operator<<