2012-03-09 14 views
6

Potrzebuję utworzyć nowe flagi dla formatu pliku wyjściowego. Mam klasyC++ fstream - tworzenie własnych flag formatowania

class foo{ 
    bar* members; 
    ofstream& operator<<(ofstream&); 
    ifstream& operator>>(ifstream&); 
}; 

i chcę go używać jak:

fstream os('filename.xml'); 
foo f; 
os << xml << f; 
os.close(); 

będzie zapisać xml pliku.

fstream os('filename.json'); 
foo f; 
os << json << f; 
os.close(); 

i to za json plików.

Jak mogę to zrobić?

Odpowiedz

6

można łatwo tworzyć yor własne manipulatory, albo porwanie istniejący banderą lub przy użyciu std::ios_base::xalloc do uzyskać nową, specyficzną dla strumienia pamięć , np (W pliku implementacji Foo:

static int const manipFlagId = std::ios_base::xalloc(); 

enum 
{ 
    fmt_xml,  // Becomes the default. 
    fmt_json 
}; 

std::ostream& 
xml(std::ostream& stream) 
{ 
    stream.iword(manipFlagId) = fmt_xml; 
    return stream; 
} 

std::ostream& 
json(std::ostream& stream) 
{ 
    stream.iword(manipFlagId) = fmt_json; 
    return stream; 
} 

std::ostream& 
operator<<(std::ostream& dest, Foo const& obj) 
{ 
    switch (dest.iword(manipFlagId)) { 
    case fmt_xml: 
     // ... 
     break; 
    case fmt_json: 
     // ... 
     break; 
    default: 
     assert(0); // Or log error, or abort, or... 
    } 
    return dest; 
} 

Declare xml i json w nagłówku, a praca jest wykonywana

(Mimo to, ja raczej myślę, że jest to trochę nadużycie. manipulatory. formaty jak XML wykraczają poza zwykłą, lokalnego formatowania i najlepiej obsługiwane przez odrębne klasy, która jest właścicielem ostream i pisze cały strumień, a nie tylko poszczególnych obiektów.)

0

Najprostszy sposób, że nasuwa się rozpocząć, tworząc rodzaj znacznika i podaje się jedną instancję:

struct JsonStreamTag {} json; 

pozwól Taki znacznik skonstruowania obiektu zawijany strumień:

class JsonStream { 
public: 

    // (1) 
    friend JsonStream operator<<(std::ostream& ostream, const JsonStreamTag&) { 
     return JsonStream(ostream); 
    } 

    // (2) 
    template<class T> 
    friend JsonStream& operator<<(JsonStream& json_stream, const T& value) { 
     write_json(json_stream.ostream, value); // (3) 
     return json_stream; 
    } 

protected: 

    JsonStream(std::ostream& ostream) : ostream(ostream) {} 

private: 

    std::ostream& ostream; 

}; 

Konstruktor to protected, aby zapewnić, że można użyć tylko some_ostream << json (1) do skonstruowania JsonStream. Drugi operator wstawiania (2) wykonuje faktyczne formatowanie. Następnie należy zdefiniować przeciążenie write_json() (3) dla każdego odpowiedniego typu:

void write_json(std::ostream& stream, int value) { 
    stream << value; 
} 

void write_json(std::ostream& stream, std::string value) { 
    stream << '"' << escape_json(value) << '"'; 
} 

// Overloads for double, std::vector, std::map, &c. 

Alternatywnie pominąć (2) i dodać przeciążenia dla operator<<(JsonStream&, T) zamiast.

Następnie wystarczy wykonać ten sam proces, aby zapisać odpowiednie XmlStream przy użyciu XmlStreamTag i write_xml(). Zakłada to, że twoje dane wyjściowe mogą być zbudowane całkowicie z określonych wartości, które piszesz; jeśli potrzebujesz nagłówek lub stopkę, która jest taka sama we wszystkich plików będziesz pisać, wystarczy użyć konstruktora i destruktora:

XmlStream(std::ostream& ostream) : ostream(ostream) { 
    ostream << "<?xml version=\"1.0\"?><my_document>" 
} 

~XmlStream() { 
    ostream << "</my_document>"; 
} 
+1

Nie sądzę, że jest to bardzo dobry pomysł. Nie działa dla rzeczy takich jak 'out << json <<" Header: "<< obj;' na przykład. (Oczywiście, dla czegoś takiego jak XML, i tak nie miałoby to sensu, ale to jest argument za niestosowaniem manipulatorów.) –

+0

@JamesKanze: Pytanie nie jest jasno zdefiniowane. Aby dosłownie odpowiedzieć na pytanie, założyłem, że "strumień JSON" traktowałby wszystko jako wartość JSON. Ale myślę, że takie coś jest nie na miejscu. –

+0

XML (i JSON, o ile mi wiadomo) nie są strumieniami; są to bardziej złożone struktury. Niezwiązany typ nie powinien wyprowadzać XML lub JSON do 'ostream'; powinien wstawić go do jakiegoś wystąpienia struktury danych XML lub JSON, która następnie zajmie się strumieniowym wyjściem, na poziomie pliku. –

1

Ten problem jest największą wadą w bibliotece programu iostream.

Rozwiązanie Jamesa Kanze'a jest częściowym rozwiązaniem, które będzie działać we własnych klasach, ale ogólnie obiekty mają określony sposób przesyłania strumieniowego.

Moja zwykła metoda polega na utworzeniu własnej klasy opakowania za pomocą funkcji, którą można przekazać do strumienia, a ponieważ xml będzie zawierał przeciążenia do xml_node() lub xml_attribute(), np.

os << xml_attribute("Id", id); 

ustawi atrybut ID na wszystko, co jest w zmiennej w formacie xml.

Napisałem również zakresy węzłów, aby zapisywały do ​​strumienia tekst otwierający węzeł na budowie i automatycznie zapisywały logikę zamknięcia podczas niszczenia.

Zaletą mojej metody nad rozwiązaniem Jamesa Kanze'a jest to, że można ją rozszerzyć. Myślę, że komentarz Jamesa Kanze sugeruje, że nie popiera swojego rozwiązania i prawdopodobnie użyje czegoś bardziej podobnego do mojego.

W powyższym rozwiązaniu, aby dodać więcej formatów, trzeba edytować funkcje operatora w każdym miejscu, podczas gdy kod formatowania JSON byłby zupełnie innym zestawem funkcji, a jeśli dodalibyśmy inny format, dodaj kod bez konieczności edytowania istniejącego kodu.

Do wprowadzenia, przy okazji, dla XML używałbyś istniejącego parsera DOM lub SAX i nie używałbyś bezpośrednio tego programu.

+0

Nie sądzę, że używanie strumienia bezpośrednio do wyjścia XML jest odpowiednim rozwiązaniem. Strumień jest abstrakcją strumienia, a nie hierarchiczną strukturą danych. Do wyprowadzania XML, użyłbym biblioteki takiej jak Xerces; coś, co pozwala na wstawienie w strukturze hierarchicznej. Reszta: klasy opakowujące są przydatne, jeśli potrzebujesz niestandardowego formatowania dla typu wbudowanego lub dla bardzo skomplikowanych struktur, ale manipulatory działają dobrze w wielu przypadkach. –

+0

Aby być bardziej precyzyjnym (i być może to również masz na myśli): wyprowadzanie do globalnie innego formatu powinno odbywać się z innym mechanizmem niż strumień: niestandardowe manipulatory są dla pojedynczych typów niestandardowych, a nie coś, co wpływa na wynik wszystkich typów . (I nie możesz dodać opcji formatowania do 'int'). –