2016-02-10 8 views
1

Скажем, у меня есть объект работника со следующими членами данных:поток Манипуляция для вывода данных объекта в различных форматах

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-как:

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; 
+0

Или мой лучший подход будет только иметь функции класса 'ostream & Employee :: ToXml()' 'ostream & Employee :: toJSON()' I как часть манипулятора потока, потому что это выглядит немного лучше для меня, а также, задача кажется крутой. –

ответ

2

Прежде всего, если вы действительно не нужны реализовать это как отдельный манипулятор, рассмотрит другие маршруты. Двумя очевидными возможностями могут быть пользовательские локали или просто функция, которая выполняет форматирование и возвращает результат в виде строки. Первый будет выглядеть примерно так:

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]); 

Если у Вас действительно нет иного выбора, кроме как осуществить это в качестве отдельного манипулятора, вам нужно сохранить чтобы указать текущий формат в потоке. Потоки обеспечивают интерфейс управления памятью в виде xalloc, iword и pword. В принципе, xalloc выделяет вам одно слово. iword дает вам доступ к этому слову в качестве ссылки на long, а pword дает вам доступ к нему в качестве ссылки на указатель на void. В вашем случае вам, по-видимому, нужен только один или два бита, поэтому вы, вероятно, захотите использовать iword и определить пару бит, чтобы указать форматирование для типа. Ваши манипуляторы toXML и toJSON установят соответствующие биты, и ваш operator<< будет читать их, чтобы контролировать его поведение. Это неуклюжий и уродливый, но он работает, если вы готовы приложить к нему немного усилий.

+0

Я думал об этом, и это жизнеспособное решение, чтобы перегрузить оператор << с помощью флага. Он не выводит в тот же поток, а выводит на два разных потока, XML-файл и JSON на консоль, поэтому я думал, что манипуляторы потока будут лучшим решением. Однако, кажется, лучшим решением было бы просто вернуть значение ostream & данных класса в качестве функции-члена. –

+0

Я даже не знал о возможности с местностью, спасибо за то, что указали на это :) –

+0

На данный момент оба являются прекрасными решениями. –

1

Как я могу это сделать с помощью манипуляторов потока?

Я покажу вам, как, но имейте в виду, что это не путь сюда. Выделенные (членные) функции или какой-то причудливый шаблон ООП - лучший подход.

Это означает, что вы можете прикреплять произвольные данные к объектам потока. Для этого вам сначала нужно «id» для этих данных. Вы получаете, что с помощью std::ios_base::xalloc:

static int formatId = ios_base::xalloc(); 

С номером возвращается вы можете получить (запись) доступ к long через ссылку, возвращаемую std::ios_base::iword. (Также есть 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; 
} 

(Внимание: Злые магические числа, заменить лучший дизайн)

Здесь я просто создал «флаг», так что, наконец, в функции вывода (оператор), я могу проверить, какие манипулятором (если таковые имеются) использовали в прошлом:

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

(Live here)

+0

Это прекрасное решение. С этим процессом не были понятны слишком плохие манипуляторы потока. Вы столкнулись с ответом на вопрос, который я тоже не задал, а именно: как связать флаг в цепочке потоков и связать его с перегруженным «оператором <<», поэтому спасибо за это. –