我想为ostream实现一个自定义操纵器,对插入到流中的下一个项目进行一些操作.例如,假设我有一个自定义操纵器引用:
std::ostringstream os; std::string name("Joe"); os << "SELECT * FROM customers WHERE name = " << quote << name;
操纵者引用将引用名称以产生:
SELECT * FROM customers WHERE name = 'Joe'
我该如何完成呢?谢谢.
将操纵器添加到C++流特别困难,因为无法控制操纵器的使用方式.可以将新的语言环境灌输到流中,该流已安装了一个控制数字打印方式的方面 - 但不是如何输出字符串.然后问题仍然是如何将引用状态安全地存储到流中.
使用std
命名空间中定义的运算符输出字符串.如果要更改打印方式,同时保持操纵器的外观,可以创建代理类:
namespace quoting { struct quoting_proxy { explicit quoting_proxy(std::ostream & os):os(os){} templatefriend 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";
试试这个:
#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; }
[编辑:"真操纵语义"(即持久引述状态)也通过以下方式实现缠绕的std::ostream
,而不是从它衍生,如通过贝努瓦在评论中所指出.]
据我所知,这不能做直接不无论是从派生新类std::ostream
或类似的,或转发大多数方法其包含在另一个类包装这样的类std::ostream
的对象.这是因为,对于您提供的代码示例,您需要以某种方式修改std::ostream& operator<<(std::ostream&, std::string const&)
在iostreams层次结构中某处定义的行为(或者可能在任何std::string
位置定义).您还需要使用(有点难看)设施ios_base
来记录保持当前报价状态的布尔标志.查找ios_base::xalloc()
,ios_base::iword()
并ios_base::pword()
找出如何做到这一点.
但是,如果您愿意使用以下语法:
os << "SELECT * FROM customers WHERE name = " << quote(name);
这可以使用全局函数(当然在适当的命名空间中)非常简单地完成.
这种语法的优点是引用不是持久的,这意味着当函数设置quote
格式化标志并忘记将其设置回原始值时,它不会"泄漏" .