11

ストリームに挿入される次のアイテムを操作するために、ostream 用のカスタム マニピュレータを実装したいと思います。たとえば、カスタム マニピュレータquoteがあるとします。

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

マニピュレータの引用は、生成する名前を引用します:

SELECT * FROM customers WHERE name = 'Joe'

それを達成するにはどうすればよいですか?ありがとう。

4

4 に答える 4

18

マニピュレータの使用方法を制御できないため、C++ストリームにマニピュレータを追加することは特に困難です。ストリームに新しいロケールを組み込むことができます。ストリームには、数値の印刷方法を制御するファセットがインストールされていますが、文字列の出力方法は制御されていません。そして、問題は、引用状態をストリームに安全に保存する方法です。

std文字列は、名前空間で定義された演算子を使用して出力されます。マニピュレータの外観を維持しながら、それらの印刷方法を変更したい場合は、プロキシクラスを作成できます。

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; 
}

に使用するのに適していますostream。一般化したい場合は、それをテンプレートにしてbasic_stream、プレーンの代わりに受け入れることもできstringます。場合によっては、標準のマニピュレータとは動作が異なります。プロキシオブジェクトを返すことで機能するため、次のような場合には機能しません

std::cout << quoting::quote; 
std::cout << "hello";
于 2009-02-11T06:29:25.667 に答える
7

これを試して:

#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;
}
于 2009-02-11T06:24:51.420 に答える
6

[編集: 「真のマニピュレータ セマンティクス」(つまり、永続的な引用状態) は、コメントで Benôit が指摘したように、派生するのではなく、ラップすることによっても実現できます。std::ostream

私の知る限り、これは、新しいクラスを派生させるか、または類似のクラスを直接作成するか、またはほとんどのメソッドを含まれているオブジェクトに転送する別のクラスにそのようなクラスをラップすることなく、直接行うことはできません。これは、提供するコード例が機能するためには、iostreams 階層のどこかで (またはおそらく定義されている場所で) 定義されている の動作を何らかの方法で変更する必要があるためです。また、現在の引用状態を保持するブール値フラグを記録するために、(やや醜い) 機能を使用する必要があります。を調べて、その方法を見つけてください。std::ostreamstd::ostreamstd::ostream& operator<<(std::ostream&, std::string const&)std::stringios_baseios_base::xalloc()ios_base::iword()ios_base::pword()

ただし、次の構文を使用する場合:

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

これは、グローバル関数を使用して非常に簡単に行うことができます (もちろん、適切な名前空間で)。

この構文には、引用が永続的ではないという利点があります。つまり、関数がquote書式設定フラグを設定し、それを元の値に戻すのを忘れたときに「漏れる」ことはありません。

于 2009-02-11T05:03:52.597 に答える
1

または、例と非常によく似たSQLのストリームインターフェイスを基本的にすでに実装しているOTLを使用します。

于 2009-02-11T07:28:47.477 に答える