我想为ostream实现一个自定义操纵器,以便对插入到流中的下一个项目进行一些操作。例如,假设我有一个自定义操纵器 quote :
std::ostringstream os;
std::string name("Joe");
os << "SELECT * FROM customers WHERE name = " << quote << name;
操纵者 quote 将引用 name 来生成:
SELECT * FROM customers WHERE name = 'Joe'
我该如何完成? 感谢。
答案 0 :(得分: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";
答案 1 :(得分: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;
}
答案 2 :(得分:6)
[编辑:“真正的操纵者语义”(即持久引用状态)也可以通过包裹 std::ostream
来实现,而不是从它衍生出来,如Benôit所述在评论中。]
据我所知,如果没有从std::ostream
或类似地派生新类,或者将这样的类包装在另一个将大多数方法转发给它的类中,则无法直接直接 包含std::ostream
个对象。这是因为,对于您提供的代码示例,您需要以某种方式修改std::ostream& operator<<(std::ostream&, std::string const&)
的行为,std::string
在iostreams层次结构中的某个位置定义(或者可能定义ios_base
的任何位置)。您还需要使用ios_base::xalloc()
中的(有点丑陋)工具来记录保持当前引用状态的布尔标志。查看ios_base::iword()
,ios_base::pword()
和os << "SELECT * FROM customers WHERE name = " << quote(name);
,了解如何操作。
但是,如果您愿意使用以下语法:
quote
这可以使用全局函数非常简单地完成(当然在适当的命名空间中)。
这种语法的优点是引用不是持久的,这意味着当函数设置{{1}}格式化标志并忘记将其设置回原始值时,它不会“泄漏”。
答案 3 :(得分:1)
或者只使用OTL基本上已经实现了SQL的流接口,与您的示例非常相似。