Domanda

Mi piacerebbe realizzare un manipolatore personalizzato per ostream fare qualche manipolazione sulla prossima voce di essere inserito nel flusso. Per esempio, diciamo che ho un manipolatore personalizzato citazione :

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

Il manipolatore citazione citeremo nome per la produzione di:

SELECT * FROM customers WHERE name = 'Joe'

Come posso fare per realizzare questo? Grazie.

È stato utile?

Soluzione

E 'particolarmente difficile per aggiungere un manipolatore ad un C ++ flusso, come si ha alcun controllo su come viene utilizzato il manipolatore. Si può infondere un nuovo locale in un flusso, che ha un aspetto installato che controlla come i numeri sono stampati - ma non come stringhe sono uscita. E allora il problema sarebbe ancora come memorizzare lo stato citando in modo sicuro nel flusso.

Le stringhe sono uscita utilizzando un operatore definito nel namespace std. Se si desidera cambiare il modo in cui quelli sono stampati, pur mantenendo l'aspetto di manipolatori, è possibile creare una 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; 
}

Il che sarebbe adatto per essere utilizzato per ostream. Se si vuole generalizzare, si può rendere un modello troppo e anche accettare basic_stream invece di string pianura. Ha diversi comportamenti a manipolatori standard in alcuni casi. Perché funziona restituendo l'oggetto proxy, non funzionerà per casi come

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

Altri suggerimenti

Prova questo:

#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: "semantica manipolatore veri" (vale a dire Stati citando persistente) potrebbe essere ottenuta anche da involucro un std::ostream piuttosto che ne derivano, come notato da Benoit nei commenti. ]

Per quanto a mia conoscenza, questo non può essere fatto direttamente senza né derivare una nuova classe da std::ostream o simili, o confezionare una classe in un'altra classe che inoltra la maggior parte dei metodi per il suo oggetto std::ostream contenute. Questo perché, per l'esempio di codice che fornisci a lavorare, è necessario modificare in qualche modo il comportamento di std::ostream& operator<<(std::ostream&, std::string const&), che è definito da qualche parte nella gerarchia iostreams (o forse ovunque std::string è definito). Sarà inoltre necessario utilizzare le strutture (un po 'brutto) in ios_base per registrare un flag booleano tenendo lo stato citando corrente. Look up ios_base::xalloc(), ios_base::iword() e ios_base::pword() per scoprire come fare.

Tuttavia, se si è disposti a utilizzare la seguente sintassi:

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

Questo può essere fatto in modo molto semplice utilizzando una funzione globale (in un dominio appropriato, naturalmente).

Questa sintassi ha il vantaggio che, citando non è persistente, il che significa che non si può "fuoriuscire" quando una funzione imposta la formattazione quote bandiera e si dimentica di impostare di nuovo al suo valore originale.

o semplicemente usare OTL che sostanzialmente implementa già un'interfaccia flusso per SQL in modo molto simile al tuo esempio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top