Question

I want to output my own object to a STL stream but with customized formatting. I came up with something like this but since I never used locale and imbue before I have no idea if this makes sense and how to implement MyFacet and operator<<.

So my questions are: does this make sense and how to implement MyFacet and operator<< ?

The following is a simplified example which shows you what I want to do.

struct MyObject
{
  int i;
  std::string s;
};

std::ostream &operator<<(std::ostream &os, const MyObject &obj)
{
    if (????)
    {
        os << obj.i;
    }
    else
    {
        os << obj.s;
    }
}

MyObject o;
o.i = 1;
o.s = "hello";

std::cout.imbue(locale("", new MyFacet(MyFacet::UseInt)));
std::cout << o << std::endl;    // prints "1"

std::cout.imbue(locale("", new MyFacet(MyFacet::UseString)));
std::cout << o << std::endl;    // prints "hello"
Was it helpful?

Solution

Well, a locale is generally used to allow different output/input formatting of the same object based on the local (the specified locale in fact) formatting which is present. For a good article on this see: http://www.cantrip.org/locale.html. Now maybe its because your example above is quite simplified, but to me it looks like you are trying to come up with a clever way to switch between printing one part of an object or another. If that is the case it might be simpler do just overload the stream operator for each type and use the if switch externally.

Anyway, I'm not going to pretend that I'm an expert in facets and locales but have a look at that article, its pretty thorough and will give you a better explanation than I will!

OTHER TIPS

Implementing your own operator << for tracing is generally a good idea. However I've never needed to imbue locales. However I tried it and it worked fine. Here's what I did:

class my_facet : public std::locale::facet
{
public:
    enum option{
        use_string,
        use_numeric
    };
    //Unique id for facet family, no locale can contain two
    //facets with same id.
    static std::locale::id id; 
    my_facet(option o=use_numeric):
    facet(0),
        _option(o)
    {//Initialize reference count to zero so that the memory
     //management will be handled by locale
    };
    option get_option() const {return _option;};
protected:
    option _option;
};
std::locale::id my_facet::id(123456); //Facet family unique id

std::ostream& operator<<(std::ostream& os, const myobj& o)
{
    std::locale const& l = os.getloc();
    if( std::has_facet<my_facet>(l) ){
        my_facet const& f =  std::use_facet<my_facet>(l);
        switch(f.get_option()){
        case my_facet::use_numeric:
            os << "Using numeric! ";
            break;
        case my_facet::use_string:
            os << "Using string! ";
            break;
        default:
            os << "Unhandled case.. ";
            break;
        }
        return os;
    }
    os << "Default case when no facet has been set";
    return os;
}

Then to imbue a locale with the facet:

std::locale mylocale(locale("US"), new my_facet(my_facet::use_numeric));
std::cout.imbue(mylocale);

However a more elegant way would be to implement different facets of the same facet family that can be replaced in the locale.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top