2012-12-04 12 views
17

Potrzebuję wskazówek lub wskazówek, jak zaimplementować niestandardowy ostream. Moje wymagania to:Niestandardowe ostream

  1. Klasa z operatorem "< <" dla kilku typów danych.
  2. Celem jest wysłanie danych wyjściowych do bazy danych. Każda "linia" powinna przejść do osobnego rekordu.
  3. Każde rekordowe najważniejsze pole to tekst (lub blob), ale niektóre inne pola, takie jak czas itp., Mogą być w większości wydedukowane automatycznie
  4. buforowanie jest ważne, ponieważ nie chcę przechodzić do bazy danych dla każdy rekord.

Po pierwsze, czy warto czerpać z ostream? Co otrzymuję dzięki pochodzeniu od ostream? Co jeśli moja klasa po prostu implementuje kilka metod (w tym niektóre niestandardowe typy danych). Którą funkcję otrzymuję od ostream?

Zakładając, że to, czego chcę, to klasa wywodząca się z ostream, potrzebuję wskazówek wyjaśniających związek między ostream a klasami streambuf. Którego muszę wdrożyć? Patrząc na niektóre próbki, wydaje się, że nie muszę w ogóle wyprowadzać z ostream, i po prostu dać konstruktorowi ostream niestandardowy streambuf. Czy to prawda? czy to podejście kanoniczne?

Jakie funkcje wirtualne w niestandardowym strumieniu strebuf muszę wprowadzić? Widziałem kilka próbek (w tym ta strona: here i here i kilka innych), niektóre zastępują metodę sync, a inne zastępują metodę overflow. Którą należy przesłonić? Ponadto, patrząc na źródła stringbuf i filebuf (Visual Studio lub GCC), obie te klasy buforów implementują wiele metod w strumieniu.

Jeśli wymagana jest niestandardowa klasa pochodna streambuf, czy istnieje jakakolwiek korzyść wynikająca z stringbuf (lub jakiejkolwiek innej klasy) zamiast bezpośrednio ze streambuf?

Jeśli chodzi o "linie". Chciałbym przynajmniej, gdy moi użytkownicy klasy używającej manipulatora "endl" będą nową linią (tj. Rekord w bazie danych). Być może - zależy to od wysiłku - każda "\ n" postać powinna być również uważana za nowy rekord. Komu mój niestandardowy ostream i/lub streambuf otrzymują powiadomienia o każdym?

+2

Powinieneś prawdopodobnie utworzyć własną klasę 'streambuf', która obsługuje całą ciężką pracę, a następnie stwórz bardzo prostą klasę' ostream', która dziedziczy 'std :: basic_ostream' i inicjuje się za pomocą twojego obiektu' streambuf'. –

+4

Powinieneś sprawdzić [Boost.Iostreams] (http://www.boost.org/doc/libs/release/libs/iostreams/doc/index.html) - dzięki temu tworzenie niestandardowych strumieni i buforów jest o wiele prostsze. –

+0

Dziękuję za twoją edycję, @MarkusParker – Uri

Odpowiedz

15

Niestandardowe miejsce docelowe dla ostream oznacza implementację własnego ostreambuf. Jeśli chcesz, aby twój pakiet był rzeczywiście buforowany (tzn. Nie łączy się z bazą danych po każdym znaku), najprościej to zrobić, tworząc klasę dziedziczącą po std::stringbuf. Funkcja, którą musisz przesłonić, to metoda , która jest wywoływana za każdym razem, gdy strumień jest przepłukiwany.

class MyBuf : public std::stringbuf 
{ 
public: 
    virtual int sync() { 
     // add this->str() to database here 
     // (optionally clear buffer afterwards) 
    } 
}; 

Następnie można utworzyć std::ostream użyciem bufor:

MyBuf buff; 
std::ostream stream(&buf) 

Większość ludzi odradzane przekierowanie strumienia do bazy danych, ale zignorował mój opis, że baza danych w zasadzie ma jedno pole blob gdzie cały tekst będzie. W rzadkich przypadkach mogę wysłać dane do innego pola. Można to ułatwić dzięki niestandardowym atrybutom zrozumiałym dla mojego strumienia. Na przykład:

MyStream << "Some text " << process_id(1234) << "more text" << std::flush 

Powyższy kod utworzy rekord w bazie o:

blob: 'Some text more text' 
process_id: 1234 

process_id() to metoda powrocie strukturę ProcessID. Następnie, w implementacji mojego ostream, mam operator<<(ProcessID const& pid), który przechowuje identyfikator procesu, dopóki nie zostanie zapisany. Działa świetnie!

+0

Dziękuję @JDW za edycję (musiałem ręcznie ją powtórzyć). – Uri

+0

Czy twoja klasa 'MyStream' dziedziczy po' std :: ostream'? Czy zastępuje wszelkie metody? Pytam o to, ponieważ dostaję błąd z informacją, że konstruktor jest chroniony –

+0

najlepszym sposobem "wyczyszczenia bufora" jest 'this-> str (" ")' – jtbr

18

Najprostszym sposobem jest dziedziczyć std::streambuf i zastąpić tylko dwie metody:

  • std::streamsize xsputn(const char_type* s, std::streamsize n) - dołączyć daną bufor o wielkości przewidzianej do wewnętrznego bufora, std::string na przykład;
  • int_type overflow(int_type c) - do dołączenia pojedynczego char do wewnętrznego bufora.

Twoja linia może być zbudowana z dowolnego elementu (na przykład połączenie DB). Po dodaniu czegoś do wewnętrznego bufora możesz spróbować podzielić go na linie i wcisnąć coś do DB (lub po prostu buforować instrukcje SQL, aby wykonać je później).

Aby go użyć: wystarczy podłączyć swój streambuf do dowolnego std::ostream przy użyciu konstruktora.

Proste! Zrobiłem coś takiego, aby wysyłać łańcuchy do syslog - wszystko działa dobrze z dowolnym niestandardowym operator<< dla klas zdefiniowanych przez użytkownika.

+2

Czy implementacja xsputn nie zniszczyłaby wszystkich celów streaguf? Zarówno filebuf, jak i stringbuf nie zastępują tej metody, ale tylko przepełnienie (które jest wywoływane przez stringbuf). – Uri

+0

W rzeczywistości tylko metoda przepełnienia ma zostać zastąpiona. Domyślnie sputn wykonuje sputc na każdym znaku. – DawidPi

4

my2c - Myślę, że rozwiązujesz ten problem w niewłaściwy sposób. Strumień może wydawać się fajnym pomysłem, ale będziesz potrzebował sposobu na wskazanie końca wiersza (a co jeśli ktoś zapomni?) Sugerowałbym coś na wzór tego, jak działają Java PreparedStatements i partie, w dostarczeniu zestawu metod, które akceptują typy i indeks kolumny, a następnie metodę "wsadową", która wyraźnie wyjaśnia, że ​​faktycznie wsadowy jest ten wiersz, a następnie wykonanie, aby wsadić wsad.

Dowolny strumień oparty operacja będzie opierać się na typie (typowo), aby wskazać kolumnę do wypełnienia - ale co jeśli masz dwa ints? IMO, jako użytkownik, nie wydaje się być naturalnym sposobem wstawiania rekordów do bazy danych ...

1

Aby dodać nowe źródło lub miejsce docelowe wejścia/wyjścia postaci do mechanizmu iostreams, należy utworzyć nowe streambuf klasa. Zadaniem klas bufora strumieniowego jest komunikowanie się z "urządzeniem zewnętrznym", które będzie przechowywać znaki i zapewniać bufory.

Problem z używaniem iostreams do komunikacji z bazą danych polega na tym, że tabela bazy danych nie pasuje do koncepcji sekwencji znaków. Trochę jak pchnięcie okrągłego kołka w kwadratową dziurę. A streambuf działa tylko na znakach. To jest jedyna rzecz, jaka kiedykolwiek została mu przedstawiona. Oznacza to, że streambuf musi przeanalizować przedstawiony strumień znaków, aby znaleźć separatory pól i rekordów. Jeśli zdecydujesz się na tę trasę, przewiduję, że skończysz pisanie konwertera CSV-SQL w swoim streambuf, tylko po to, aby działało.

Prawdopodobnie będziesz lepszy dzięki dodaniu kilku przeciążeń operator<< do swoich klas. Możesz spojrzeć na ramy Qt na pomysły tutaj. Mają także możliwość używania pozycji operator<< w celu dodawania elementów do kolekcji i innych.

Powiązane problemy