How can I alter how a user-defined type/enum is displayed? (facet/locale?)

Hi folks

For a given user-defined type or enum:

eg:
1
2
3
4
5
enum blah
{
    FOO,
    BAR
};


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
extern const char* const prt(const blah in, const bool full = false);

// in definition file
const char* const prt(const blah in, const bool 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?

TIA
Steve
Last edited on
It compiles fine as is.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

enum blah
{
    FOO,
    BAR
};

const char* 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;
}
Thanks kbw, I know it compiles fine! :)

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?
I sort of cheated. Make the enum a class and store its display state there.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <iomanip>

// template helpers nicked from Microsoft's iomanip
template<class _Arg>
struct _manip
{	// store function pointer and argument value
	_manip(void (__cdecl *_Left)(std::ios_base&, _Arg), _Arg _Val)
		: _Pfun(_Left), _Manarg(_Val)
	{	// construct from function pointer and argument value
	}

	void (__cdecl *_Pfun)(std::ios_base&, _Arg);	// the function pointer
	_Arg _Manarg;	// the argument value
};

template<class _Elem, class _Traits, class _Arg>
std::basic_ostream<_Elem, _Traits>& operator<<(
	std::basic_ostream<_Elem, _Traits>& _Ostr,
	const _manip<_Arg>& _Manip)
{	// insert by calling function with output stream and argument
	(*_Manip._Pfun)(_Ostr, _Manip._Manarg);
	return (_Ostr);
}

class blah
{
public:
	enum value_type { FOO, BAR };
	value_type	value;
	static bool state;

	blah(value_type v) : value(v) {}
	const char* print(bool full = true) const;
};

const char* blah::print(bool full) const
{
	switch (value)
	{
	case FOO: return full ? "FOO" :  "F";
	case BAR: return full ? "BAR" :  "B";
	default:  return full ? "UNKNOWN" : "?";
	}
}

std::ostream& operator<<(std::ostream& os, blah b)
{
	os << b.print(b.state);
	return os;
}

void fb(std::ios_base &, bool state)
{
	blah::state = state;
}

_manip<bool> blahstate(bool state)
{
	return _manip<bool>(&fb, state);
}

bool blah::state;

int main(int argc, char* argv[])
{
	blah a = blah::FOO;

	std::cout << blahstate(true) << a << std::endl;
	std::cout << blahstate(false) << a << std::endl;

	return 0;
}

Last edited on
Thanks man, I can see that will work sort of.

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.
So I've written the following code, and it doesn't work - is there anyone out there that can tell me why?! :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string.h>

enum foo
{
    FOO, BAR
};

const char* const prt(const foo in, const bool full = false)
{
    switch (in)
    {
        case FOO: return full ? "FOO" : "F";
        case BAR: return full ? "BAR" : "B";
        default: break;
    }
    return full ? "UNKNOWN" : "?";
}

class log_facet : public std::locale::facet
{
    typedef std::ostreambuf_iterator<char, std::char_traits<char> > strm_iter;
    bool full;
public:
    log_facet(const bool f = false) : full(f) { }

    void set_short_format() { full = false; }
    void set_long_format()  { full = true;  }

    strm_iter put(strm_iter a_next,
                  std::ios_base& a_ios,
                  char a_fill,
                  const foo i) const
    {
        const char* disp = prt(i, full);
        for (size_t i = 0, end = strlen(disp); i < end; ++i)
            *a_next = disp[i];
        return a_next;
    }
    static std::locale::id id;
};

std::locale::id log_facet::id;

int main()
{
    log_facet* fmt = new log_facet();
    std::cout.imbue(std::locale(std::cout.getloc(), fmt));

    fmt->set_long_format();
    std::cout << FOO << std::endl;

    fmt->set_short_format();
    std::cout << FOO << std::endl;

    return 0;
}


Output is
0
0

whereas I was hoping it would be
FOO
F

TIA
Steve
Ok, I've figured it out.

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! :)

Here's the working example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <string.h>
#include <iterator>

enum foo
{
    FOO, BAR
};

const char* const prt(const foo in, const bool full = false)
{
    switch (in)
    {
        case FOO: return full ? "FOO" : "F";
        case BAR: return full ? "BAR" : "B";
        default: break;
    }
    return full ? "UNKNOWN" : "?";
}

class log_facet : public std::locale::facet
{
    bool full;
public:
    log_facet(const bool f = false) : full(f) { }

    void set_short_format() { full = false; }
    void set_long_format() { full = true; }

    std::ostream_iterator<char> put(std::ostream_iterator<char> a_next, const foo i) const
    {
        const char* disp = prt(i, full);
        for (size_t i = 0, end = strlen(disp); i < end; ++i)
            *a_next = disp[i];
        return a_next;
    }
    static std::locale::id id;
};

std::locale::id log_facet::id;

std::ostream& operator<<(std::ostream& os, const foo val)
{
    std::ostream_iterator<char> it(os);
    if (std::has_facet<log_facet>(os.getloc()))
    {
        std::use_facet<log_facet>(os.getloc()).put(it, val);
    }
    else
    {
        os << prt(val);
    }
    return os;
}

int main()
{
    log_facet* fmt = new log_facet();
    std::cout.imbue(std::locale(std::cout.getloc(), fmt));

    fmt->set_long_format();
    std::cout << FOO << std::endl;

    fmt->set_short_format();
    std::cout << FOO << std::endl;

    return 0;
}
What's wrong with
1
2
bool long = whatever;
std::cout << prt(FOO,whatever);

?
Not enough code :)
Haha, kbw does have a point!

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.
I think you are confusing the interface to a stream with its implementation.

The interface to a stream is transparent.
The internal implementation may vary.

That's why, given these:
1
2
3
string username = "George";
ofstream myfile( "greeting.txt" );
ostringstream mystr;
the following code is identical, even though the target is different in each case:
1
2
3
cout   << "Hello " << username << ".\n";
myfile << "Hello " << username << ".\n";
mystr  << "Hello " << username << ".\n";

The simple example given by kbw (refining your original example) here
http://www.cplusplus.com/forum/general/25606/#msg135989
works likewise -- it does not matter what your target, it works transparently.

Hope this helps.
Topic archived. No new replies allowed.