Reputation: 2821
I want to be able to define stream operators (<<
, >>
) for an object, but to have more than one set of them so that I can use a different set depending on the context. For example, if I have an object as follows:
struct Foo {
int a;
};
Then in some places I want to be able to use this
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
out<<foo.a;
}
And in others I'd prefer to use this
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
out<<"'"<<foo.a<<"'";
}
But I can't see how to define both of these at the same time.
I'm submitting my own answer but I'm hoping someone else has had cause in the past to think this through more than I have, and preferably has an organised way of doing it in a large application (Hence the term "Managing" in the question title).
Upvotes: 1
Views: 1695
Reputation: 137810
Create a manipulator: a simple proxy which just gives a name (and an overload slot) to the alternative formatting.
// default format
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
return out<<foo.a;
}
// custom format
// helper class
struct print_quoted_manip {
Foo const &ref;
print_quoted_manip( Foo const &in )
: ref( in ) {}
};
// implementation
std::ostream& operator<<( std::ostream& out, print_quoted_manip o )
{ return out << '\'' << o.ref << '\''; }
// the manipulator
print_quoted_manip print_quoted( Foo const &in )
{ return print_quoted_manip( in ); }
// usage
std::cout << print_quoted( my_foo );
You could easily template this to put quotes around any type of object.
Upvotes: 5
Reputation: 258608
Although your proposed solution would work, I'd strongly recommend you don't do it. Operator overloading is a powerful tool that can easily be misused. If the behavior isn't intuitive and obvious, it often does more harm than good.
Id instead suggest just having the first version
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
out<<foo.a;
}
and simply use
cout << "'" << fooObject << "'";
instead of providing a different overload.
This makes the code clear and intuitive, as well as easier to debug.
I can imagine numerous bugs arising from improper use of using namespace
in the code. There's also the code duplication - what if you want to add another member b
to the object and print that one. Someone else might find one of the overloads, modify it, see that it works on a first look, and forget about the other overload.
Upvotes: 2
Reputation: 2821
You can declare namespaces around each set of operators, appropriately named, and then use that namespace within the function where you need to use those particular stream operators. For example:
#include <iostream>
#include <string>
struct Foo {
int a;
};
namespace raw_output {
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
out<<foo.a;
}
}
namespace quoted_output {
std::ostream& operator<<(std::ostream& out, const Foo& foo) {
out<<"'"<<foo.a<<"'";
}
}
void do_raw() {
using namespace raw_output;
Foo f = { 7 };
std::cout<<f<<std::endl;
}
void do_quoted() {
using namespace quoted_output;
Foo f = { 7 };
std::cout<<f<<std::endl;
}
void do_raw_and_quoted() {
Foo f = { 7 };
{ // Anonymous scoping to use different ops in the same function scope
using namespace raw_output;
std::cout<<f<<std::endl;
}
{
using namespace quoted_output;
std::cout<<f<<std::endl;
}
}
int main() {
do_raw();
do_quoted();
do_raw_and_quoted();
}
With this approach you should avoid using namespace at top level (outside function scope), since that could pollute and make ambiguous usage of the desired operator.
Upvotes: 1