I want to provide ostream<< and wostream<< operators for a class, which are identical other than one being widestream and the other not.
Is there some trickery to do this which is ugly than just copy-pasting and making the necessary tweaks?
For reference, this is necessary because we use wostream as standard, but Google-test's EXPECT_PRED3 fails compilation when no ostream<< is provided, even though other macros happily work with ostream or wostream.
My actual code looks like this:
class MyClass
{
...
public:
friend std::wostream& operator<<(std::wostream& s, const MyClass& o)
{
...
}
};
std::ostream and std::wostream are just specializations of a template class std::basic_ostream. Writing a templated operator << shall solve your problem. Here's an example:
struct X { int i; };
template <typename Char, typename Traits>
std::basic_ostream<Char, Traits> & operator << (std::basic_ostream<Char, Traits> & out, X const & x)
{
return out << "This is X: " << x.i << std::endl;
}
As pointed out in comments, you can go even further and parametrize your operator << by any class that exposes some stream-like interface:
template <typename OStream>
OStream & operator << (OStream & out, X const & x)
{
return out << "This is X: " << x.i << std::endl;
}
Related
I have this code
template <typename T>
class KeyValueProperty {
protected:
T value = T();
std::string key = "";
public:
KeyValueProperty(const std::string & key) : key(key) { }
T & operator = (const T &i) { return value = i; };
operator const T & (){ return value; };
};
struct T2 {
KeyValueProperty<std::string> x {"x"};
KeyValueProperty<double> y {"y"};
};
and in main
T2 tx;
tx.x = "hellow";
tx.y = 10;
std::cout << static_cast<std::string>(tx.x) << ::std::endl;
std::cout << tx.y << ::std::endl;
This is working correctly. However, doing only this
std::cout << tx.x << ::std::endl;
will end up in
error C2679: binary '<<': no operator found which takes a right-hand operand of type 'Test::KeyValueProperty' (or
there is no acceptable conversion)
Is it possible to have auto-conversion, or I must manually call casting?
The reason t.y works even without a custom operator<< is because there already exists an operator<<(std::ostream&, double), and the compiler can also see that it can make a double out of your class. It does so and we're happy.
However, there is no operator<<(std::ostream&, std::string). If there was, the same logic would apply and we'd still be happy. Instead, there is:
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);
That is, a generic insertion operator for any kind of basic_string.
Although there exist some template arguments that would make this as if it were operator<<(std::ostream&, std::string), the compiler isn't going to try and guess which possible template arguments would allow it to subsequently match a conversion of your class to the result. There are too many combinations, so this isn't permitted.
This is why you had to explicitly turn your object into a std::string (aka std::basic_string<char>) - this removes one layer from the problem and it can do regular old type deduction to make this work.
The right solution is to give your wrapper class an insertion operator, to sidestep this issue.
You must provide an appropriate overload of operator<<, for example:
template<class T>
std::ostream& operator<<(std::ostream& os, KeyValueProperty<T> const& kvp)
{
return os << T(kvp);
}
I have some Logging::Logger class with the following functions:
template<typename T>
const Logger& Logger::operator<<(const T& in) const {
// ...
return *this;
}
const Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&)) {
// ...
return *this;
}
And the following code:
loggerInstance << "ID: " << 5 << endl;
And I'm getting the following error though all operators seems to be implemented:
error C2678: binary '<<': no operator found which takes a left-hand
operand of type 'const Logging::Logger' (or there is no acceptable
conversion)
Of course, without endl everything is working.
I've looked at the following answer:
std::endl is of unknown type when overloading operator<<
What am I missing?
Because your overloaded operators return a const Logger &, it follows that they must be const class methods, in order for you to be able to chain them together:
const Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&)) const
However, it's better if they were not const class members, and returned a Logger &, instead:
template<typename T> Logger& Logger::operator<<(const T& in)
Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&))
This would be because, presumably, operator<< would be modifying the Logger instance, in some way. If not, you can use const objects and methods, here.
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.
Im trying to build a vector of type template but keep getting an error
template<class T>
struct s{
T val;
public:
s(T a)
{
val = a;
};
friend ostream& operator<<(ostream& os, const T& a);
};
template <typename T>
ostream& operator<< (ostream& os, const T& a){
return os << a.val;
}
int main()
{
s <double> names(4) ;
s <int> figure(7);
s <string> word();
s <vector<int>*> arrays();
cout << names << endl;
cout << figure << endl;
cout << arrays << endl;
return 0;
}
I keep receiving the same error -
request for member 'val' in 'a', which is of non-class type 's<std::vector<int>*>()'|
Any advice will be greatly appreciated
s <vector<int>*> arrays();
Declares a function named arrays taking no arguments and returning a s<vector<int>*>. Remove the redundant parentheses or replace them with {} for C++11. Of course, your s doesn't support default construction, so you need to give it a default constructor - which can be as simple as giving the constructor a default argument:
s(T a = T())
{
val = a;
}
and it's better to use the member initialization list instead of assignment and take a const ref instead of by value, since T can be large:
s(const T& a = T()) : val(a) { }
In C++11, you should take by value and move a into val instead:
s(T a = T()) : val(std::move(a)) { }
Also,
template <typename T>
ostream& operator<< (ostream& os, const T& a){
return os << a.val;
}
would match everything under the sun. Better to match s only:
template <typename T>
ostream& operator<< (ostream& os, const s<T>& a){
return os << a.val;
}
Finally,
friend ostream& operator<<(ostream& os, const T& a);
befriends (and declares) a simple function rather than a function template. To befriend the template operator << as modified above:
template <typename U>
friend ostream& operator<<(ostream& os, const s<U>& a);
Or even better, remove the template operator and define the friend function inline (inside the class definition) instead:
friend ostream& operator<<(ostream& os, const s<T>& a){
return os << a.val;
}
This is more compact, and also limits friendship to the matching version of operator <<, rather than all instantiations of the template.
Demo.
s <vector<int>*> arrays();
This does not instantiate an object, it declares a function that returns s<vector<int>*>, and as a consequence:
cout << arrays << endl;
Tries to print out a function pointer by instantiating your templated operator<<:
template <typename T>
ostream& operator<< (ostream& os, const T& a){
return os << a.val;
}
with:
T = s<vector<int>*>(*)();
and so, a pointer to function a does not have the .val field which triggers the error.
The whole problem is that this
ClassName InstanceName();
is not creating an instance of ClassName using default constructor, but declares a function. In order to do what you wanted (and I presume that is to create instances with default constructors) use syntax
ClassName InstanceName;
So, in order to fix your errors, change
s <string> word();
s <vector<int>*> arrays();
to
s <string> word;
s <vector<int>*> arrays;
and add a default constructor to your class s.
I have a class:
class foo {
private:
std::string data;
public:
foo &append(const char* str, size_t n) { data.append(str,n); }
// for debug output
template <typename T>
friend T& operator<< (T &out, foo const &f);
// some other stuff
};
template <typename T>
T& operator<< (T &out, foo const &f) {
return out << f.data;
}
I want this to work with any class that provides the << operator.
This works fine with std::cout as in:
std::cout << fooObject;
But the following fails:
BOOST_AUTO_TEST_CASE( foo_append_and_output_operator )
{
// fooObject is accessable here
const char* str = "hello";
fooObject.append(str, strlen(str));
output_test_stream output;
output << fooObject;
BOOST_CHECK( output.is_equal(str) );
}
g++ tells me that:
In function ‘T& operator<<(T&, const foo&)
[with T = boost::test_tools::output_test_stream]’:
error: invalid initialization of reference of type
‘boost::test_tools::output_test_stream&’ from expression of type
‘std::basic_ostream<char, std::char_traits<char> >’
What's going on?
I'm using Boost 1.34.1 on Ubuntu 8.04.
So I think I have an explanation, but no solution yet. output_test_stream implements its stream functionality by subclassing wrap_stringstream. The insertion-operator for this is a free function-template that looks like this:
template <typename CharT, typename T>
inline basic_wrap_stringstream<CharT>&
operator<<( basic_wrap_stringstream<CharT>& targ, T const& t )
{
targ.stream() << t;
return targ;
}
// ... further down in the same header
typedef basic_wrap_stringstream<char> wrap_stringstream;
Your operator is called with output_test_stream as the stream-type, and that makes this it's return-type. Your operator then calls the above operator, and just propagates the return value. The return value of the above operator however is a superclass of the returntype of your operator. When compiler tries to create the reference you want to return, it chokes, because it cannot initialize a reference to a subclass from a reference to a superclass, even if both refer to the same object. That make any sense?
You may know that already, but using output_test_stream as an std::ostream works:
class foo {
// [...]
friend
std::ostream& operator<< ( std::ostream &os, const foo &f );
};
std::ostream& operator<< ( std::ostream &os, const foo &f ) {
return os << f.data;
}
Is it a typo? You wrote
foo.append(str, strlen(str));
but foo is the name of the class and not an object.