Autor Wątek: C++ manager  (Przeczytany 2625 razy)

Offline Kyroaku

  • Użytkownik

# Marzec 29, 2016, 00:34:57
Witam,
Ogólnie pisze sobie bibliotekę GUI.
Każdy element, każda kontrolka dziedziczy po klasie Layout.
Klasa Manager między innymi przechowuje teksturki.

Teraz przykładowo, aby zrealizować zapis kontrolki do pliku, klasa Layout posiada metodę save().
Tworząc nową kontrolkę, robię sobie tylko overload tej metody i jest git.
Problem pojawia się, kiedy muszę zapisać ścieżkę do tekstury. Manager trzyma tekstury, więc to u niego muszę jej szukać. Niestety, jeśli w pliku z kontrolką (którą dziedziczy po Layout), zaincluduje Managera, to kompilator mi powie, że klasa bazowa jest niezdefiniowana.

Jak Wy byście do tego podeszli ?
Jakieś rady ?

Postanowiłem pisać kod w miarę czytelny, a mój każdy kolejny pomysł, to wiadro błota :P

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Kos

  • Użytkownik
    • kos.gd

# Marzec 29, 2016, 09:56:39
Może wyrzuć całą serializację z kontrolek i napisz ją jako moduł niezależny od całej reszty? Nie będzie tak prościej?

(Ten Twój layout trochę pachnie jak god object, ale wszystkie "obiektowe" GUI które widziałem miały z tym problem >_>)

Offline lethern

  • Użytkownik

  • +1
# Marzec 29, 2016, 18:19:10
Niezdefiniowana - czy przypadkiem nie masz jakiegoś "cyklu" w include, który powoduje, że najpierw includujesz gdzieś jeden plik, który próbuje zaincludować drugi plik, który wymaga coś z tego pierwszego..? Może to nie jest problem tekstur i managerów tylko dołączenia plików

Offline Kyroaku

  • Użytkownik

# Marzec 29, 2016, 20:53:47
Cytuj
Może wyrzuć całą serializację z kontrolek i napisz ją jako moduł niezależny od całej reszty? Nie będzie tak prościej?
Próbowałem tak:
class Loader
{
public:
virtual void load(Layout*) = 0;
virtual void save(Layout*) = 0;
};
Następnie dla każdej kontrolki miałem oddzielny loader, który dziedziczył powyższą klasę i definiował metody.
W Managerze dodałem sobie listę "zarejestrowanych" kontrolek:
unordered_map<int, Loader*>loaders;
Każdemu Layoutowi dodałem indentyfikator.
Przy zapisie Layoutu do pliku, identyfikator zapisywany jest na samym początku, a potem wywoływana metoda loaders[identyfikator]->save(layout).
Przy odczycie, najpierw wczytywany jest identyfikator, a potem wywoływana metoda loaders[identyfikator]->load(layout).
Tak to wymyśliłem, żeby dać możliwość stworzenia własnej kontrolki wraz z opcją zapisu/odczytu razem z "domyślnymi" kontrolkami.
W tym momencie tak, czy siak, przy implementacji takiego loadera muszę mieć dostęp do listy teksturek i teraz nie wiem, czy przekombinowałem, czy może wywalić te teksturki z Managera do jakiegoś singletona może (?)

Co o tym w ogóle myślicie ?

Offline Xender

  • Użytkownik

# Marzec 29, 2016, 22:16:42
@lethern: +1, a kontynuując tę myśl - obowiązkowy link: http://forum.4programmers.net/C_i_C++/162017-SOLVEDc++Klasy_zaprzyjaznianie_i_dostep_do_siebie?p=642607#id642607

W tym momencie tak, czy siak, przy implementacji takiego loadera muszę mieć dostęp do listy teksturek i teraz nie wiem, czy przekombinowałem, czy może wywalić te teksturki z Managera do jakiegoś singletona może (?)

Co o tym w ogóle myślicie ?

No ten Manager od początku brzmiał jakoś globalowato*.
To jak teraz masz, każda kontrolka ma swojego?

Może rzeczywiście zrób z niego globala*/singletona, tylko jak chcesz "ładnego, obiektowego" singletona, to najlepiej odpuść leniwą inicjalizację na rzecz zwykłej funkcji inicjalizującej wołanej raz na początku maina - kolejność inicjalizacji rzeczy w programie lepiej mieć pod kontrolą.

* Jeśli Twój program ma wątki - rozważ, czy "global" powinien być globalny, czy thread-local / czy i kiedy singleton (które jego metody?) ma być bezpieczny dla wątków.
« Ostatnia zmiana: Marzec 30, 2016, 18:58:39 wysłana przez Xion »

Offline lethern

  • Użytkownik

# Marzec 30, 2016, 11:35:50
U mnie serializacja jest dość prymitywna, w każdej "swojej klasie" mam jedną funkcję serializacji, która musi obsługiwać każdą zmienną, ale w dość prosty sposób (jedna linijka), plus (ta klasa) ma stałą garść dodatków (tj. funkcje pomagające, np. gdy serializuje wskaźniki (obiekt-id), co dalej pominę)
(kod jest mocno obcięty, traktować jak pseudokod)
Jedna funkcja serializacji obsługuje zapis ORAZ odczyt:
class IObject
{
public:
virtual void serialization( ISerializedDataBase* d ) M_OVERRIDE;
}
Przykładowa klasa, która tego używa; "IItem" jest analogiczną klasą jak CTextField, dla każdej dziedziczonej jest też być wywołana serializacja, dziedziczy to oczywiście po IObject
class CTextField : IItem
{
virtual void serialization( ISerializedDataBase* d );

// jakieś dane
CGraphicTextArea* textGraphic;
bool resizing;
}
Teraz cpp tej przykładowej klasy
void CTextField::serialization( ISerializedDataBase* d )
{
Item::serialization( d ); // mam to rozwiązane przy pomocy makr, ale do tego się rozwija
M_SERIALIZE_IOBJECT_PTR( textGraphic )
M_SERIALIZE( resizing )
}
Powyższa funkcja wykonuje szereg makr typu M_SERIALIZE, które robią to samo, więc opisze (przybliże) je wspólnie:
#define M_SERIALIZE_D( _x ) d->serializeCommon( _x, #_x );
/**/ // co się rozwija do
/**/ (ISerializedDataBase*)d -> serializeCommon( _x, #_x );
/**/ // czyli
/**/  M_SERIALIZE_IOBJECT_PTR( textGraphic )
/**/ // ==>
/**/  d -> serializeCommon( this->textGraphic , "textGraphic" );
Funkcje serializujące są zależne od typu (osobne dla delegatów, klas, tablic, typów złożonych, typów prostych), przykładowo:
template<typename T>
void ISerializedDataBase::serializeCommon( T& x, const char* s )
{
if( isSerializing() ){
*this << (int)x;
}else{
int i;
*this >> i;
x= (T)i;
}
}
W skrócie: funkcja ta, w zależności od aktualnego stanu (trybu: zapisujemy czy odczytujemy), serializuje lub deserializuje
Gdzie te operatory << i >> to oczywiście coś co np. zapisuje/odczytuje ze strumienia, czy to binarnie czy z XML

Tym mniej prymitywnym mechanizmem jest dla mnie serializacja obiektów (wskaźników, id), co sprowadza się ogólnie do tego, że w trakcie zapisu zapisuje wskaźnik jako jego ID, a przy odczycie są dwa etapy: najpierw wczytuję jakie będą instancje, tworzę je (puste), a później dopiero je ładuję (deserializacja), mogąc też "deserializować wskaźniki", bo je wszystkie już mam utworzone, choć jestem w trakcie ich ładowania. Co też oznacza, że plik serializacji jest u mnie podzielony na dwie części (pierwsza ma tylko id wraz i informacje które pomogą mi stworzyć klasę tego obiektu, w drugiej częśći jest to co zapisała serializacja

template<class Type>
void ISerializedDataBase::serializeIObjectPtr( Type*& obj, const char* s )
{
if( isSerializing() ){
if( obj==NULL || !obj->getSaveable() ){
Uint id= 0;
M_SERIALIZE_D( this, id )
}else{
obj->serialize_id( this );
}
}
else{ // deserialize
obj= (Type*) gItemsManager->getDeserialized( this );
}
}

IObject* CItemsManager::getDeserialized_impl( ISerializedDataBase* d )
{
IdentType id;
M_SERIALIZE( id )  // jestesmy tu zawsze w trybie deserialize
if( id== 0 )
return NULL;
else{
// pobieramy tu wcześniej (w poprzednim, osobnym etapie) utworzony obiekt
IObject* ret= gItemsFactory->getFromID( id );
return ret;
}
}

Kawałek z pliku danych (zmodyfikowany do czegoś czytelnego):
<CLASS name="CObjectsSaveManager">
<GROUP name="dictionary_items">
<size> 45 </size>
<obj>
   <id> 9 </id>
   <ident> CItemComposition </ident>
</obj>
<obj>
   <id> 10 </id>
   <ident> CItemComposition </ident>
</obj>
</GROUP>
<GROUP name="full_objects">
<item>
   <id> 3 </id>
   <composite>
      <id> 8 </id>
   </composite>
   <it_event>
      <ident> 5  </ident>
      <del_obj>
         <id> 11  </id>
      </del_obj>
   </it_event>
   <graphics items=2>
      <item>
         <id> 12  </id>
      </item>
      <item>
         <id> 14  </id>
      </item>
   </graphics>
</item>


PS Myślałem czy da się ominąć cały ten narzut serializacji, tj. funkcję i wszystkie linijki per zmienna (jak wymyślę sobie nową zmienną w klasie, a zapomne ją dodać do serialization(), to przepada), ale nic ładnego nie wymyśliłem (tj. nic co nie wymamgałoby etapu przed kompilacją modyfikacji plików źródłowych), więc zamiast tego dodałem sobie testy - te niestety nic nie rozwiązały, bo ich tym bardziej nie chce mi się odpalać / aktualizować ; )

PPS chciałem sobie przypomnieć i uporządkować co nieco, stąd ten post :D
« Ostatnia zmiana: Marzec 30, 2016, 12:15:35 wysłana przez lethern »

Offline Kyroaku

  • Użytkownik

# Kwiecień 02, 2016, 19:06:47
Cytuj
obowiązkowy link:
Ok, przeniesienie include'a na koniec pliku pomogło.
Nie jest to dla mnie idealne rozwiązanie, ale niech tak na razie zostanie :P
Później będę się bawił w "fachową" serializację.

Dzięki za odpowiedzi :)