Como formatar meus próprios objetos ao usar fluxos STL?
Pergunta
Eu quero saída de meu próprio objeto para um fluxo STL mas com formatação personalizada. Eu vim com algo assim, mas desde que eu nunca usei local e imbuir antes de eu não tenho idéia se isso faz sentido e como implementar MyFacet e operador <<.
Então, minhas perguntas são: isso faz sentido e como implementar MyFacet e operador <<
?A seguir é um exemplo simplificado que mostra o que eu quero fazer.
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"
Solução
Bem, uma localidade é geralmente usada para permitir a saída diferente / formatação do mesmo objecto com base no local de entrada (o local especificado nas fato) de formatação que está presente. Para um bom artigo sobre este see: http://www.cantrip.org/locale.html. Agora, talvez a sua porque o seu exemplo acima é bastante simplificado, mas para mim parece que você está tentando chegar a uma maneira inteligente para alternar entre a impressão de uma parte de um objeto ou de outra. Se for esse o caso, pode ser mais simples basta sobrecarregar o operador de fluxo para cada tipo e usar o interruptor se externamente.
De qualquer forma, eu não vou fingir que sou um especialista em facetas e locais, mas ter um olhar para esse artigo, o seu muito completa e vai lhe dar uma explicação melhor do que eu vou!
Outras dicas
A implementação de seu próprio operador << para rastreamento é geralmente uma boa idéia. No entanto, eu nunca precisei para locais imbuir. No entanto, eu tentei e funcionou muito bem. Aqui está o que eu fiz:
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;
}
Depois de imbuir um local com a faceta:
std::locale mylocale(locale("US"), new my_facet(my_facet::use_numeric));
std::cout.imbue(mylocale);
No entanto, uma maneira mais elegante seria implementar diferentes facetas de uma mesma família faceta que pode ser substituído na localidade.