简体   繁体   English

Stream Manipulation用于输出不同格式的对象数据

[英]Stream Manipulation for outputting object data in different formats

Say I have an employee object with the following data members: 假设我有一个包含以下数据成员的员工对象:

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

I would like to output it two different ways: 我想以两种不同的方式输出它:

XML 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>

and JSON-like: 和JSON一样:

id: 12345
name: Jack Dough
address: 24437 Princeton
city: Dearborn
state: Michigan
country: USA
phone: 303-427-0153
salary: 140000

How would I be able to do so with stream manipulators? 我怎么能用流操作器这样做? For Example: 例如:

Employee* employee = new Employee(12345, "Jack Dough", "24437 Princeton", "Dearborn", "Michigan", "USA", "303-427-0153", 140000.00);
cout << toXML << employee;
cout << toJSON << employee;

First of all, unless you really need to implement this as a separate manipulator, consider other routes. 首先,除非您确实需要将其作为单独的操纵器实现,否则请考虑其他路径。 Two obvious possibilities would be a custom locale, or just a function that does the formatting and returns the result as a string. 两个显而易见的可能性是自定义区域设置,或者只是执行格式化并将结果作为字符串返回的函数。 The former would look something like: 前者看起来像:

std::locale myLoc(std::locale(), XML_formatter);
cout.imbue(myLoc);

cout << employee;

This makes the formatting style persistent for the stream in general. 这使得格式化样式通常对于流持久。 If you really need to mix different styles in the same stream, the function version is a lot simpler: 如果你真的需要在同一个流中混合使用不同的样式,那么函数版本要简单得多:

std::string toXML(Employee const &e) { 
    std::stringstream ret;

    ret << "Employee>\n<id>" << id << "</id>"
        << // ...
    return ret.str();
}

// ...
cout << toXML(employees[i]);

If you truly have no choice but to implement this as a separate manipulator, you'll need to store a flag to indicate the current format in the stream. 如果您真的别无选择,只能将其作为单独的操作符实现,则需要存储一个标志以指示流中的当前格式。 Streams provide a memory management interface in the form of xalloc , iword and pword . Streams以xallociwordpword的形式提供内存管理接口。 Basically, xalloc allocates one word for you. 基本上, xalloc为您分配一个单词。 iword gives you access to that word as a reference to a long, and pword gives you access to it as a reference to pointer to void. iword允许您访问该单词作为对long的引用,并且pword允许您访问它作为指向void的引用。 In your case, you apparently only need one or two bits, so you probably want to use iword and define a couple of bits to specify the formatting for the type. 在您的情况下,您显然只需要一个或两个位,因此您可能希望使用iword并定义几个位来指定类型的格式。 Your toXML and toJSON manipulators will set the appropriate bits, and your operator<< will read them to control its behavior. 您的toXMLtoJSON操纵器将设置适当的位, operator<<将读取它们以控制其行为。 It's clumsy and ugly, but it does work if you're willing to put a little effort into it. 这是笨拙和丑陋的,但如果你愿意付出一点努力,它确实有效。

How would I be able to do so with stream manipulators? 我怎么能用流操作器这样做?

I'll show you how, but keep in mind that this is not the way to go here. 我会告诉你怎么做,但请记住,这不是去这里的方法。 Dedicated (member) functions or some fancy OOP pattern is a better approach. 专用(成员)功能或一些花哨的OOP模式是一种更好的方法。

That said you can attach arbitrary data to stream objects. 也就是说,您可以将任意数据附加到流对象。 For that, you first need an "id" for that data. 为此,您首先需要该数据的“id”。 You get that using std::ios_base::xalloc : 你可以使用std::ios_base::xalloc

static int formatId = ios_base::xalloc();

With the number returned you can get (write) access to a long via the reference returned by std::ios_base::iword . 随着返回的数字就可以得到(写)访问long通过返回的参考std::ios_base::iword (There's also std::ios_base::pword to get a void * .) (还有std::ios_base::pword来获取void * 。)

Then, a stream manipulator is just something that's callable with a stream (reference), returning another stream reference: 然后,流操纵器只是可以用流(引用)调用的东西,返回另一个流引用:

ostream & toFoo(ostream & stream) {
  stream.iword(formatId) = 1;
  return stream;
}
ostream & toBar(ostream & stream) {
  stream.iword(formatId) = 2;
  return stream;
}

( Attention: Evil magic numbers, replace with better design! ) 注意:邪恶的魔法数字,用更好的设计取代!

Here I just set up a "flag" so that finally, in the output function (operator), I can check which manipulator (if any) was used last: 这里我只是设置一个“标志”,最后,在输出函数(运算符)中,我可以检查最后使用的操纵器(如果有的话):

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

Well, that's it. 嗯,就是这样。 I tested with: 我测试过:

int main() {
  FooBar f;
  cout << f << toFoo << " " << f << endl;
  cout << f << toBar << " " << f << endl;
  cout << f << endl;
  return 0;
}

(Live here) (住在这里)

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

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