简体   繁体   English

用于C ++ iostream的自定义操纵器

[英]Custom manipulator for C++ iostream

I'd like to implement a custom manipulator for ostream to do some manipulation on the next item being inserted into the stream. 我想为ostream实现一个自定义操纵器,对插入到流中的下一个项目进行一些操作。 For example, let's say I have a custom manipulator quote : 例如,假设我有一个自定义操纵器引用

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

The manipulator quote will quote name to produce: 操纵者引用将引用名称以产生:

SELECT * FROM customers WHERE name = 'Joe'

How do I go about accomplishing that? 我该如何完成呢? Thanks. 谢谢。

It's particularly difficult to add a manipulator to a C++ stream, as one has no control of how the manipulator is used. 将操纵器添加到C ++流特别困难,因为无法控制操纵器的使用方式。 One can imbue a new locale into a stream, which has a facet installed that controls how numbers are printed - but not how strings are output. 可以将新的语言环境灌输到流中,该流已安装了一个控制数字打印方式的方面 - 但不是如何输出字符串。 And then the problem would still be how to store the quoting state safely into the stream. 然后问题仍然是如何将引用状态安全地存储到流中。

Strings are output using an operator defined in the std namespace. 使用std命名空间中定义的运算符输出字符串。 If you want to change the way those are printed, yet keeping the look of manipulators, you can create a proxy class: 如果要更改打印方式,同时保持操纵器的外观,可以创建代理类:

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

Which would be suitable to be used for ostream . 哪个适合用于ostream If you want to generalize, you can make it a template too and also accept basic_stream instead of plain string . 如果要进行概括,可以将其设为模板,也可以接受basic_stream而不是普通string It has different behaviors to standard manipulators in some cases. 在某些情况下,它与标准操纵器有不同的行为。 Because it works by returning the proxy object, it will not work for cases like 因为它通过返回代理对象来工作,所以它不适用于像

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

Try this: 尝试这个:

#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: "True manipulator semantics" (ie a persistent quoting state) could also be achieved by wrapping an std::ostream rather than deriving from it, as noted by Benôit in the comments.] [编辑:“真正的操纵者语义”(即持久引用状态)也可以通过包装 std::ostream而不是从中派生来实现,正如Benôit在评论中所指出的那样。

To the best of my knowledge this cannot be done directly without either deriving a new class from std::ostream or similar, or wrapping such a class in another class that forwards most methods to its contained std::ostream object. 据我所知,这不能直接或者没有获得从一个新的类来完成std::ostream或类似的,或转发大多数方法其包含在另一个类包装这样一类std::ostream对象。 That's because, for the code example you provide to work, you will need to somehow modify the behaviour of std::ostream& operator<<(std::ostream&, std::string const&) , which is defined somewhere in the iostreams hierarchy (or possibly wherever std::string is defined). 这是因为,对于您提供的代码示例,您需要以某种方式修改std::ostream& operator<<(std::ostream&, std::string const&)的行为,该行为在iostreams层次结构中的某处定义(或者可能在定义了std::string地方)。 You will also need to use the (somewhat ugly) facilities in ios_base to record a boolean flag holding the current quoting state. 您还需要使用ios_base的(有点丑陋)工具来记录保持当前引用状态的布尔标志。 Look up ios_base::xalloc() , ios_base::iword() and ios_base::pword() to find out how to do that. 查找ios_base::xalloc()ios_base::iword()ios_base::pword()以了解如何执行此操作。

However, if you are willing to use the following syntax: 但是,如果您愿意使用以下语法:

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

This can be done very simply using a global function (in an appropriate namespace of course). 这可以使用全局函数(当然在适当的命名空间中)非常简单地完成。

This syntax has the advantage that quoting is not persistent, meaning it can't "leak out" when a function sets the quote formatting flag and forgets to set it back to its original value. 这种语法的优点是引用不是持久的,这意味着当函数设置quote格式化标志并忘记将其设置回原始值时,它不会“泄漏”。

或者只使用OTL ,它基本上已经实现了SQL的流接口,与您的示例非常相似。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM