How can I alter how the operator<< displays my blah enum? (in some cases I want the full english name, whereas in other cases I want just a single char)
At the moment I'm sort of getting what I want by using:
eg:
1 2
prt(my_blah, true); // print full blah value
prt(my_blah, false); // print short blah value
with my code as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// in header
externconstchar* const prt(const blah in, constbool full = false);
// in definition file
constchar* const prt(const blah in, constbool full)
{
switch (in)
{
case FOO: return full ? "FOO" : "F";
case BAR: return full ? "BAR" : "B";
default: break;
}
return full ? "UNKNOWN" : "?";
}
However, for operator<< I can't change how to print my blah enum value.
eg:
1 2 3 4 5 6 7 8 9 10
// in header
extern std::ostream& operator<<(std::ostream &out, const blah in);
// in definition file
std::ostream& operator<<(std::ostream &out, const blah in)
{
out << prt(in); // using default 'full' value of false
// so short version of blah is always printed
return out;
}
From what I've read online, I use a facet right? Then I have to imbue the facet into std::ostream's locale or something? I have no idea how to do this.
Can someone give me an example of a facet for a user-defined type, and also how you then use it for std::cout or whatever?
#include <iostream>
enum blah
{
FOO,
BAR
};
constchar* const prt(blah in, bool full = true)
{
switch (in)
{
case FOO: return full ? "FOO" : "F";
case BAR: return full ? "BAR" : "B";
default: break;
}
return full ? "UNKNOWN" : "?";
}
std::ostream& operator<<(std::ostream &out, blah in)
{
out << prt(in); // using default 'full' value of false
// so short version of blah is always printed
return out;
}
int main()
{
blah a = FOO;
std::cout << a << std::endl;
return 0;
}
My question is not how do I get the above code to compile?
My question is how do I manipulate std::ostream (or std::cout) such that calling
std::cout << a << std::endl;
yields
FOO
in one case, and calling
std::cout << a << std::endl;
yields
F
in the other case.
I'm thinking it's got to be something like:
1 2
my_blah_facet_class* fmt = new my_blah_facet_class; // facet which manipulates the display of blah enum values
std::cout.imbue(std::locale(std::cout.getloc(), fmt)); // install fmt into std::cout stream
to get "long" printing of blah enum values, I then go:
1 2 3
fmt->set_long_print_format();
blah a = FOO;
std::cout << a << std::endl; // should print "FOO"
and to get "short" printing of blah enum values, I go:
1 2
fmt->set_short_print_format();
std::cout << a << std::endl; // should print "F"
So the real question is - how do I make a facet class that does this?
However, what I'd prefer to do is have a facet class and imbue it into std::cout's locale.
The idea is that I can then set the facet to display full information if I'm in debug mode, and if I'm in no debug mode, but have to print out an error or warning or whatever, then the facet specifies display less information.
So at startup, I go something like this:
1 2 3 4 5 6
my_blah_facet_class* fmt = new my_blah_facet_class; // facet which manipulates the display of blah enum values
std::cout.imbue(std::locale(std::cout.getloc(), fmt)); // install fmt into std::cout stream
if (debug_level == HIGH)
fmt->set_long_print_format();
else
fmt->set_short_print_format();
Now I no longer have to worry how I write my debug statements. In my application's code, I therefore just go:
1 2
blah a = FOO;
std::cout << " a is " << a << std::endl;
In high debug mode this will print
a is FOO
In low debug mode this will print
a is F
ie: no need to worry about how to print the debug statement at the point at which I'm printing it. Those details are encapsulated in the facet class which is imbued into std::cout.
Not only do you need a facet for your custom data type, but you need to use it in your operator<<() Unfortunately, as with many things in life, you don't get that for free! :)
No, seriously, to answer your question Bazzy, it's all part of a larger logging system I'm building.
I'm looking to hide the implementation details inside my logging implementation. All the client should have to say is:
log::cout << "some message here about some " << object << std::endl;
What happens inside log::cout is hidden from the client, and specified at startup.
1. The log could write to a file.
2. The log could display to stdout.
3. It could do both.
4. It could hold a buffer of the n last lines of log output, and if the application exits unexpectedly, dump the buffer contents to a file.
5. Object could be displayed in a verbose manner
6. Object could be displayed with minimal information.
7. Something else I haven't thought of yet
Regardless of what happens, the implementation details are hidden from the user. That's what we aim for in OOD is it not?
And looking at item 7. I can change how the log works internally, and no user code is affected. What if I decided that instead of having 2 ways to display Object (used to be long and short), I now want 3 ways (long, medium and short) - Ok, I admit it's a silly example, but it emphasises the point - be encapsulating the details inside the log object, I can change behaviour without affecting client code.