以不同格式输出对象数据的流操作
Stream Manipulation for outputting object data in different formats
假设我有一个具有以下数据成员的员工对象:
class Employee {
private:
int _id;
std::string _name;
std::string _address;
std::string _city;
std::string _state;
std::string _country;
std::string _phone;
double _salary;
...
}
我想用两种不同的方式输出:
XML
<Employee>
<id>12345</id>
<name>Jack Dough</name>
<address>24437 Princeton</address>
<city>Dearborn</city>
<state>Michigan</state>
<country>USA</country>
<phone>303-427-0153</phone>
<salary>140000</salary>
</Employee>
和 JSON-like:
id: 12345
name: Jack Dough
address: 24437 Princeton
city: Dearborn
state: Michigan
country: USA
phone: 303-427-0153
salary: 140000
我如何使用流操纵器做到这一点?
例如:
Employee* employee = new Employee(12345, "Jack Dough", "24437 Princeton", "Dearborn", "Michigan", "USA", "303-427-0153", 140000.00);
cout << toXML << employee;
cout << toJSON << employee;
首先,除非你真的需要将其实现为一个单独的操纵器,否则请考虑其他路线。两种明显的可能性是自定义语言环境,或者只是一个执行格式化和 returns 结果作为字符串的函数。前者看起来像:
std::locale myLoc(std::locale(), XML_formatter);
cout.imbue(myLoc);
cout << employee;
这使得格式样式在一般情况下对于流来说是持久的。如果你真的需要在同一个流中混合不同的样式,函数版本就简单很多:
std::string toXML(Employee const &e) {
std::stringstream ret;
ret << "Employee>\n<id>" << id << "</id>"
<< // ...
return ret.str();
}
// ...
cout << toXML(employees[i]);
如果您真的别无选择,只能将其实现为单独的操纵器,则需要存储一个标志以指示流中的当前格式。 Streams 以 xalloc
、iword
和 pword
的形式提供内存管理接口。基本上,xalloc
给你分配一个字。 iword
允许您访问该单词作为对 long 的引用,而 pword
允许您访问它作为对 void 指针的引用。在你的情况下,你显然只需要一个或两个位,所以你可能想使用 iword
并定义几个位来指定类型的格式。您的 toXML
和 toJSON
操纵器将设置适当的位,您的 operator<<
将读取它们以控制其行为。它笨拙且丑陋,但如果你愿意付出一点努力,它确实有用。
How would I be able to do so with stream manipulators?
我会告诉你怎么做,但请记住,这不是去这里的方法。专用(成员)函数或一些花哨的 OOP 模式是更好的方法。
也就是说,您可以将任意数据附加到流对象。为此,您首先需要一个 "id" 用于该数据。您可以使用 std::ios_base::xalloc
:
static int formatId = ios_base::xalloc();
使用返回的数字,您可以通过 std::ios_base::iword
返回的引用获得(写入)对 long
的访问权限。 (还有std::ios_base::pword
得到一个void *
。)
然后,流操纵器就是可以用流(引用)调用的东西,返回另一个流引用:
ostream & toFoo(ostream & stream) {
stream.iword(formatId) = 1;
return stream;
}
ostream & toBar(ostream & stream) {
stream.iword(formatId) = 2;
return stream;
}
(注意:邪恶的魔法数字,换成更好的设计!)
这里我只是设置了一个"flag",这样最后在输出函数(操作符)中,我可以检查最后使用了哪个机械臂(如果有的话):
struct FooBar {};
ostream & operator<<(ostream & stream, FooBar const &) {
switch (stream.iword(formatId)) {
case 1: stream << "foo"; break;
case 2: stream << "bar"; break;
default: stream << "wild foobar";
}
return stream;
}
嗯,就是这样。我测试了:
int main() {
FooBar f;
cout << f << toFoo << " " << f << endl;
cout << f << toBar << " " << f << endl;
cout << f << endl;
return 0;
}
假设我有一个具有以下数据成员的员工对象:
class Employee {
private:
int _id;
std::string _name;
std::string _address;
std::string _city;
std::string _state;
std::string _country;
std::string _phone;
double _salary;
...
}
我想用两种不同的方式输出:
XML
<Employee>
<id>12345</id>
<name>Jack Dough</name>
<address>24437 Princeton</address>
<city>Dearborn</city>
<state>Michigan</state>
<country>USA</country>
<phone>303-427-0153</phone>
<salary>140000</salary>
</Employee>
和 JSON-like:
id: 12345
name: Jack Dough
address: 24437 Princeton
city: Dearborn
state: Michigan
country: USA
phone: 303-427-0153
salary: 140000
我如何使用流操纵器做到这一点? 例如:
Employee* employee = new Employee(12345, "Jack Dough", "24437 Princeton", "Dearborn", "Michigan", "USA", "303-427-0153", 140000.00);
cout << toXML << employee;
cout << toJSON << employee;
首先,除非你真的需要将其实现为一个单独的操纵器,否则请考虑其他路线。两种明显的可能性是自定义语言环境,或者只是一个执行格式化和 returns 结果作为字符串的函数。前者看起来像:
std::locale myLoc(std::locale(), XML_formatter);
cout.imbue(myLoc);
cout << employee;
这使得格式样式在一般情况下对于流来说是持久的。如果你真的需要在同一个流中混合不同的样式,函数版本就简单很多:
std::string toXML(Employee const &e) {
std::stringstream ret;
ret << "Employee>\n<id>" << id << "</id>"
<< // ...
return ret.str();
}
// ...
cout << toXML(employees[i]);
如果您真的别无选择,只能将其实现为单独的操纵器,则需要存储一个标志以指示流中的当前格式。 Streams 以 xalloc
、iword
和 pword
的形式提供内存管理接口。基本上,xalloc
给你分配一个字。 iword
允许您访问该单词作为对 long 的引用,而 pword
允许您访问它作为对 void 指针的引用。在你的情况下,你显然只需要一个或两个位,所以你可能想使用 iword
并定义几个位来指定类型的格式。您的 toXML
和 toJSON
操纵器将设置适当的位,您的 operator<<
将读取它们以控制其行为。它笨拙且丑陋,但如果你愿意付出一点努力,它确实有用。
How would I be able to do so with stream manipulators?
我会告诉你怎么做,但请记住,这不是去这里的方法。专用(成员)函数或一些花哨的 OOP 模式是更好的方法。
也就是说,您可以将任意数据附加到流对象。为此,您首先需要一个 "id" 用于该数据。您可以使用 std::ios_base::xalloc
:
static int formatId = ios_base::xalloc();
使用返回的数字,您可以通过 std::ios_base::iword
返回的引用获得(写入)对 long
的访问权限。 (还有std::ios_base::pword
得到一个void *
。)
然后,流操纵器就是可以用流(引用)调用的东西,返回另一个流引用:
ostream & toFoo(ostream & stream) {
stream.iword(formatId) = 1;
return stream;
}
ostream & toBar(ostream & stream) {
stream.iword(formatId) = 2;
return stream;
}
(注意:邪恶的魔法数字,换成更好的设计!)
这里我只是设置了一个"flag",这样最后在输出函数(操作符)中,我可以检查最后使用了哪个机械臂(如果有的话):
struct FooBar {};
ostream & operator<<(ostream & stream, FooBar const &) {
switch (stream.iword(formatId)) {
case 1: stream << "foo"; break;
case 2: stream << "bar"; break;
default: stream << "wild foobar";
}
return stream;
}
嗯,就是这样。我测试了:
int main() {
FooBar f;
cout << f << toFoo << " " << f << endl;
cout << f << toBar << " " << f << endl;
cout << f << endl;
return 0;
}