Pergunta

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

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

O manipulador Citação citá nome para produzir:

SELECT * FROM customers WHERE name = 'Joe'

Como faço para ir sobre como realizar isso? Obrigado.

Foi útil?

Solução

É particularmente difícil para adicionar um manipulador para um fluxo C ++, como não se tem controle de como o manipulador é usado. Pode-se imbuir uma nova localidade em um córrego, que tem uma faceta instalado que controla como os números são impressos - mas não como cordas são de saída. E então o problema ainda seria como armazenar o estado citando em segurança para o fluxo.

Cordas são exibidos utilizando um operador definido no namespace std. Se você quiser mudar a forma como os são impressos, mas mantendo a aparência de manipuladores, você pode criar uma classe proxy:

namespace quoting {
struct quoting_proxy {
    explicit quoting_proxy(std::ostream & os):os(os){}

    template<typename Rhs>
    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 adequado para ser usado para ostream. Se você quer fazer generalizações, você pode torná-lo um modelo muito e também aceitar basic_stream vez de string simples. Ele tem diferentes comportamentos para manipuladores padrão em alguns casos. Porque ele funciona, retornando o objeto proxy, não vai funcionar para casos como

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

Outras dicas

Tente isto:

#include <iostream>
#include <iomanip>

// 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<typename T>
                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 manipulador True" (ou seja, um estado citando persistente) também poderia ser alcançado pelo embalagem um std::ostream vez de derivar a partir dele, como observado por Benoit nos comentários. ]

Para o melhor de meu conhecimento isso não pode ser feito diretamente , sem qualquer derivação de uma nova classe de std::ostream ou similar, ou embalar tal classe em outra classe que encaminha a maioria dos métodos para o seu objeto std::ostream contido. Isso porque, para o exemplo de código que você fornecer para o trabalho, você precisará de alguma forma modificar o comportamento do std::ostream& operator<<(std::ostream&, std::string const&), que é definido em algum lugar na hierarquia iostreams (ou possivelmente onde quer std::string é definido). Você também vai precisar de utilizar as instalações (um pouco feio) em ios_base para gravar um boolean flag segurando o estado citando atual. Olhe para cima 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 sintaxe:

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

Isto pode ser feito muito simplesmente usando uma função global (em um espaço de nome apropriado é claro).

Esta sintaxe tem a vantagem de citar não é persistente, o que significa que não pode "vazar" quando uma função define o quote formatação bandeira e esquece-se de configurá-lo de volta ao seu valor original.

Ou apenas usar OTL que basicamente já implementa uma interface de fluxo de SQL de forma muito semelhante ao seu exemplo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top