Page 1 of 1
operator overload : how to take care of the "rest" ?
Posted: May 19th, 2019, 5:20 pm
by binbinhfr
Hi,
I needed to overload the operator<< to output tests on the console (to output a string to a wide stream), and I wonder why the following piece of code did not work the way I would like to (well infact I know, my template overloads some already declared types
) ...
Code: Select all
std::wostream& operator<<(std::wostream& ost, const char* s)
{
return( s == nullptr ? ost : ost<< std::wstring(s, s + strlen(s)));
}
std::wostream& operator<<(std::wostream & ost, const std::string & s)
{
return(ost << std::wstring(s.begin(), s.end()));
}
template <typename A>
std::wostream& operator<<(std::wostream& ost, const A& a)
{
ost << "don't know how to output type [" << typeid(a).name() << "]";
return(ost);
}
I mean, the first two functions (when alone) works fine, but I figured out that I could write some kind of an overload operator<< that would take care of all the types that I did not explicitely defined (or that are not defined in std).
Any idea on what I should do to create such a function that takes care of the remaining types ?
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 3:10 am
by albinopapa
Custom types or User Defined Types will need to have overloads for them as well.
What other types are you looking for?
wchar_t* and std::wstring should already be overloaded for std::wcstream.
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 7:21 am
by binbinhfr
Infact, I illustrated on this example, but my question was more general : is it possible to write an overload function or operator that takes care about all the types that have not been specifically overloaded elsewhere in the code ?
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 9:20 am
by albinopapa
Ok, well then no not really.
Say you have this:
Code: Select all
struct Thing1
{
int a, b, c;
std::string name;
};
struct Thing2
{
Vec3f position, velocity;
Rectf bb;
};
How are you going to have one function take care of both of these types?
The best you can do is to define overloads for them.
Code: Select all
struct Thing1
{
int a, b, c;
std::string name;
friend std::wcstream& operator<<( std::wcstream& wcs, Thing1 const& thing )
{
wcs << thing.name << " has these values "<< "a = " << thing.a << "b = " << thing.b << "c = " << thing.c << '\n';
return wcs;
}
};
struct Thing2
{
Vec3f position, velocity;
Rectf bb;
friend std::wcstream& operator<<( std::wcstream& wcs, Thing2 const& thing )
{
wcs << "Position: x = " << thing.position.x
<< ", y = " << thing.position.y
<< ", z = " << thing.position.z
<< "\nVelocity: x = " << thing.velocity.x
<< ", y = " << thing.velocity.y
<< ", z = " << thing.velocity.z
<< "\nBoundingBox: left = " << thing.bb.left
<< " top = " << thing.bb.top
<< " right = " << thing.bb.right
<< " bottom = " << thing.bb.bottom << '\n';
return wcs;
}
};
Well, ok, there might be one way to have a single function.
Make each user-defined type ( structs/classes ) have a function that returns a string or wstring of it's members or whatever you want.
Code: Select all
struct Thing1
{
std::string to_string()const
{
std::stringstream ss;
ss << thing.name << " has these values "<< "a = " << thing.a << "b = " << thing.b << "c = " << thing.c << '\n';
return ss.str();
}
int a, b, c;
std::string name;
};
struct Thing2
{
std::string to_string()const
{
std::stringstream ss;
ss << "Position: x = " << thing.position.x
<< ", y = " << thing.position.y
<< ", z = " << thing.position.z
<< "\nVelocity: x = " << thing.velocity.x
<< ", y = " << thing.velocity.y
<< ", z = " << thing.velocity.z
<< "\nBoundingBox: left = " << thing.bb.left
<< " top = " << thing.bb.top
<< " right = " << thing.bb.right
<< " bottom = " << thing.bb.bottom << '\n';
return ss.str();
}
Vec3f position, velocity;
Rectf bb;
};
template<typename T>
std::wcstream& operator<<( std::wcstream& wcs, T const& value )
{
wcs << value.to_string();
return wcs;
}
This will end up calling your operator<<(std::wcstream&, std::string) overload.
If you don't want to overload for each type, then define a common function for each type that the single template can call and use.
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 10:41 am
by binbinhfr
Thanks for your help. I see that there is no real answer to my question. And maybe as you said, what is the need to call a function on a type that is unknown, because your function won't know anything about this unkown type's internals...
I just wanted to see if it was possible to write some kind of a default overload operator that would answer "I do not know how to handle this TYPE".
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 11:09 am
by cyboryxmen
Code: Select all
template<typename Type>
std::wostream& operator<<(std::wostream& ost, Type&& obj)
{
return ost << std::string(std::forward<Type>(obj));
}
Can't get more general than that.
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 12:15 pm
by binbinhfr
cyboryxmen wrote: ↑May 20th, 2019, 11:09 am
Can't get more general than that.
oh yes, that's almost what I did in my example that you can see in the original thread, but then everytime I try to use a wcout<< the compiler complains that it is ambiguous...
I suppose because for example it finds the original ostream<<int definition and then the template that also matches... so it cannot choose...
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 1:25 pm
by cyboryxmen
Printing out an object by printing out the name of its type is ultimately useless. You're better off throwing a compiler error to signal to your users that they're doing something that doesn't make sense.
Printing out the object as a string is a good generic option to take. In fact, you don't even need the other overloads. Just use the string version.
Code: Select all
std::wostream& operator<<(std::wostream & ost, const std::string& s)
{
return(ost << std::wstring(s.begin(), s.end()));
}
struct X
{
X() = default;
constexpr X(const int a) noexcept : a{a}
{
}
operator std::string() const noexcept
{
return std::to_string(a);
}
int a;
};
int main()
{
auto test = std::string("poopoo\n");
std::wcout << "lol\n";
std::wcout << test;
std::wcout << X{12} << '\n';
return EXIT_SUCCESS;
}
Re: operator overload : how to take care of the "rest" ?
Posted: May 20th, 2019, 3:41 pm
by binbinhfr
cyboryxmen wrote: ↑May 20th, 2019, 1:25 pm
Printing out an object by printing out the name of its type is ultimately useless. You're better off throwing a compiler error to signal to your users that they're doing something that doesn't make sense.
Printing out the object as a string is a good generic option to take. In fact, you don't even need the other overloads. Just use the string version.
Yes, it was only an example.
I just wonder if there was a way to define a template overload function that would be used by default on any type where the function is not already defined...