Manipulador personalizado para C ++ iostream

Eu gostaria de implementar um manipulador personalizado para ostream para fazer alguma manipulação no próximo item a ser inserido no stream. Por exemplo, digamos que eu tenho uma cotação de manipulador personalizado:

std::ostringstream os; std::string name("Joe"); os << "SELECT * FROM customers WHERE name = " << quote << name; 

A citação do manipulador irá citar o nome para produzir:

 SELECT * FROM customers WHERE name = 'Joe' 

Como faço para realizar isso? Obrigado.

É particularmente difícil adicionar um manipulador a um stream de C ++, pois não há controle de como o manipulador é usado. Pode-se imbuir um novo código de idioma em um stream, que possui uma faceta instalada que controla como os números são impressos – mas não como as sequências são produzidas. E então o problema ainda seria como armazenar o estado de citação com segurança no stream.

As strings são produzidas usando um operador definido no namespace std . Se você quiser alterar a maneira como eles são impressos, mantendo a aparência dos manipuladores, você pode criar uma class de proxy:

 namespace quoting { struct quoting_proxy { explicit quoting_proxy(std::ostream & os):os(os){} template friend std::ostream & operator<<(quoting_proxy const& q, Rhs const& rhs) { return q.os << rhs; } friend std::ostream & operator<<(quoting_proxy const& q, std::string const& rhs) { return q.os << "'" << rhs << "'"; } friend std::ostream & operator<<(quoting_proxy const& q, char const* rhs) { return q.os << "'" << rhs << "'"; } private: std::ostream & os; }; struct quoting_creator { } quote; quoting_proxy operator<<(std::ostream & os, quoting_creator) { return quoting_proxy(os); } } int main() { std::cout << quoting::quote << "hello" << std::endl; } 

Qual seria apropriado para ser usado para ostream . Se você quiser generalizar, também pode torná-lo um modelo e também aceitar basic_stream vez de string simples. Tem comportamentos diferentes para manipuladores padrão em alguns casos. Como funciona retornando o object proxy, ele não funcionará para casos como

 std::cout << quoting::quote; std::cout << "hello"; 

Tente isto:

 #include  #include  // The Object that we put on the stream. // Pass in the character we want to 'quote' the next object with. class Quote { public: Quote(char x) :m_q(x) {} private: // Classes that actual does the work. class Quoter { public: Quoter(Quote const& quote,std::ostream& output) :m_q(quote.m_q) ,m_s(output) {} // The << operator for all types. Outputs the next object // to the stored stream then returns the stream. template std::ostream& operator<<(T const& quoted) { return m_s << m_q << quoted << m_q; } private: char m_q; std::ostream& m_s; }; friend Quote::Quoter operator<<(std::ostream& str,Quote const& quote); private: char m_q; }; // When you pass an object of type Quote to an ostream it returns // an object of Quote::Quoter that has overloaded the << operator for // all types. This will quote the next object and the return the stream // to continue processing as normal. Quote::Quoter operator<<(std::ostream& str,Quote const& quote) { return Quote::Quoter(quote,str); } int main() { std::cout << Quote('"') << "plop" << std::endl; } 

[EDIT: “Semântica do manipulador real” (isto é, um estado de citação persistente) também poderia ser alcançado envolvendo um std::ostream vez de derivar dele, como observado por Benôit nos comentários.]

Para o melhor de meu conhecimento, isso não pode ser feito diretamente sem derivar uma nova class de std::ostream ou similar, ou envolvendo essa class em outra class que encaminha a maioria dos methods para seu object contido std::ostream . Isso porque, para o exemplo de código que você fornece para trabalhar, você precisará modificar de alguma forma o comportamento de std::ostream& operator<<(std::ostream&, std::string const&) , que é definido em algum lugar na hierarquia iostreams ( ou possivelmente onde quer que std::string esteja definido). Você também precisará usar as facilidades (um tanto feias) em ios_base para gravar uma bandeira booleana mantendo o estado de cotação atual. Procure por ios_base::xalloc() , ios_base::iword() e ios_base::pword() para descobrir como fazer isso.

No entanto, se você estiver disposto a usar a seguinte syntax:

 os << "SELECT * FROM customers WHERE name = " << quote(name); 

Isso pode ser feito de forma muito simples usando uma function global (em um namespace apropriado, é claro).

Essa syntax tem a vantagem de que a cotação não é persistente, o que significa que não pode "vazar" quando uma function define o sinalizador de formatação de quote e esquece de configurá-lo de volta ao seu valor original.

Ou apenas use o OTL que basicamente já implementa uma interface de stream para SQL de forma muito semelhante ao seu exemplo.