Moderators Kercyn Δημοσ. 25 Μαΐου 2014 Μέλος Moderators Δημοσ. 25 Μαΐου 2014 Σας ευχαριστώ πολύ όλους σας για τις απαντήσεις (και ειδικά το παπι για την υπομονή του ). Θα τα κοιτάξω όλα αναλυτικά αύριο και θα επανέλθω...
παπι Δημοσ. 25 Μαΐου 2014 Δημοσ. 25 Μαΐου 2014 Δίνω ένα παράδειγμα γιατί νομίζω ότι δεν έχω γίνει κατανοητός: Εσύ είσαι ένα object μιας κλάσης, o migf1 ένα άλλο object της ίδιας κλάσης κι εγώ ο resource manager. Για να λειτουργήσεις σωστά, χρειάζεσαι ένα texture. Απευθύνεσαι, λοιπόν, σε μένα, ρωτώντας με αν έχω το texture που χρειάζεσαι. Μιας και είσαι ο πρώτος που μου ζητάει αυτό το texture, εγώ το φορτώνω και σου λέω "μπορείς να βρεις το texture που θες εκεί". Παράλληλα, σημειώνω ότι 1 object μου έχει ζητήσει το συγκεκριμένο texture. Έρχεται μετά κι ο migf1, ζητώντας μου το ίδιο texture. Εφ' όσον έχω ήδη φορτώσει το texture για σένα, δείχνω στο migf1 πού να το βρει. Υπογραμμίζω ότι και οι 2 χρησιμοποιείτε το ίδιο texture, δε φτιάχνω ένα καινούριο για το migf1. Φεύγει κι ο migf1 κι εγώ σημειώνω ότι 2 objects χρησιμοποιούν αυτό το texture. Έρχεσαι μετά εσύ και μου λες "εγώ τελείωσα, δε θέλω άλλο αυτό το texture". Μειώνω, λοιπόν, εγώ τον counter και τον κάνω 1. Έρχεται μετά κι ο migf1 και μου λέει ότι τελείωσε κι αυτός. Μειώνω κι άλλο τον counter, και τώρα το texture δεν το χρησιμοποιεί κανείς, άρα το σβήνω. Άμα όμως εμένα μου καπνίσει και σηκωθώ και φύγω πριν τελειώσετε και οι 2, τότε το texture που χρησιμοποιείτε όχι μόνο θα έχει χαθεί, αλλά δε θα μπορείτε να με βρείτε για να μου πείτε ότι τελειώσατε. Εκεί στο σημείο που δε μπορείτε να με βρείτε χτυπάει το exception. Πώς γίνεται λοιπόν εγώ (ο resource manager) να αναγκαστώ να φύγω τελευταίος; Ναι βρε παιδακι μου, σε καταλαβα. Αυτο που σου λεω ειναι, οτι το καθε object πρεπει να ειναι αυτοβουλο. Δεν θα το κανει ο manager delete, θα το κανει αυτος που θα καλεσει το τελευταιο release.
Moderators Kercyn Δημοσ. 25 Μαΐου 2014 Μέλος Moderators Δημοσ. 25 Μαΐου 2014 Κακώς χρησιμοποιείς 3 map αντί για 1 map από tuple (ή αν το θέλεις πιο old school από κάποιο εσωτερικό struct). Μια ακόμα καλύτερη κίνηση που θα έλυνε και το πρόβλημά σου "παρεμπιπτόντως" είναι το απλά να μην κάνει απολύτως τίποτα ο resource manager στον destructor του. Εφόσον προφανώς πρόκειται για singleton (αν όχι μηχανικά, τουλάχιστον λογικά) και εφόσον ξέρεις νομοτελειακά πως destruction του resource manager σημαίνει αυτόματα τερματισμός της εφαρμογής, δεν υπάρχει κανένας απολύτως λόγος να κάνεις τίποτα στον destructor. PS: Μου άρεσε το RAII που κάνεις με τους unique_ptr. Θα μπορούσες να γράψεις (μιας και δεν υπάρχει λογικά αυτή τη στιγμή διαθέσιμη) μια function make_unique της οποίας η δουλειά θα είναι αυτή που κάνει και η make_pair: να μη χρειάζεται να γράψεις τα template arguments. Αυτό το tuple δεν το ήξερα και πολύ μ' αρέσει Όπως κι αυτό το "κάνω τίποτα" μου ακούγεται καλό (μαζί με τα ifdefs για το leaking)... Θα το υλοποιήσω το make_unique κι αυτό. Πέρα από τη συζήτηση της αρχιτεκτονικής, μια πρόταση για το πρόβλημά σου. Όταν γράφεις ότι πρώτα καταστρέφεται ο resource manager και μετά ο cursor, αυτό σημαίνει ότι δηλώνεις πρώτα τον cursor και μετά τον resource manager: στη C++ τα αντικείμενα καταστρέφονται σε αντίστροφη σειρά από αυτή που δηλώνονται. Επομένως, είτε αντιστρέφεις τη σειρά που τους δηλώνεις, είτε τους δημιουργείς δυναμικά ως δείκτες, με new, οπότε ελέγχεις πλήρως πότε θα καταστραφεί ο καθένας. ΚΑΤΙ ΜΟΥ ΛΕΕΙ ΟΤΙ ΑΥΤΗ Η ΓΑΜ#$%#$@ Η ΜΑΛΑΚΙΑ ΗΤΑΝ ΠΟΥ ΜΟΥ ΔΗΜΙΟΥΡΓΟΥΣΕ ΤΟ ΠΡΟΒΛΗΜΑ... Όντως στο SharedResources τα δήλωνα με ανάποδη σειρά, αλλά εκεί που γίνονταν όλα initialize τα έκανα με τη "σωστή" σειρά, οπότε νόμιζα ότι θα καταστραφούν με αντίθετη σειρά από αυτή που δημιουργήθηκαν, όχι απ' αυτή που δηλώθηκαν... Αυτό που περιγράφεις μου ακούγεται αρκετά όμοιο με αυτό εδώ: http://stackoverflow.com/questions/15707991/good-design-pattern-for-manager-handler(έχει και πλήρη κώδικα... no thanks, δεν θέλω να τον διαβάσω εγώ ... του έριξα μια ματιά όμως ) Μια άλλη παραπλήσια ιδέα, είναι το referencing counting να το κάνουν τα ίδια τα resource-objects μέσα τους (και όχι ο resource manager) αλλά να έχουν πρόβλεψη και για forced destruction, π.χ. σε περίπτωση που ο manager θελήσει να τα σκοτώσει και να κλείσει το πρόγραμμα, ή για οποιονδήποτε άλλο λόγο, να τους στέλνει ένα message "kill yourself" (δηλαδή να μην βασίζουν το destruction τους αποκλειστικά στο εσωτερικό τους reference counting). Αυτό νομίζω είναι που σου λέει και ο πάπι. Nα έχεις δηλαδή ένα resource-object factory ξεχωριστό από τον resource manager, αλλά προφανώς να μπορούν να επικοινωνούν. Εννοείται πως ο resource manager θα έχει ανά πάσα στιγμή γνώση των resource-objects που είναι ήδη instantiated, ως kinds, αλλά δεν χρειάζεται να κρατάει και reference-counting για το καθένα τους (μπορεί να κρατάει μονάχα pointers προς κάθε instantiated kind of object, ή kind ids, ή οτιδήποτε σχετικό) για να τα περνάει στους consumers, όταν πάρει clearance από το factory. Ναι βρε παιδακι μου, σε καταλαβα. Αυτο που σου λεω ειναι, οτι το καθε object πρεπει να ειναι αυτοβουλο. Δεν θα το κανει ο manager delete, θα το κανει αυτος που θα καλεσει το τελευταιο release. Άρα εγώ δεν είχα καταλάβει τι έλεγες και νόμιζα ότι μου έλεγες άλλα. Αφού διάβασα αυτό που έγραψες τώρα και ξαναδιαβάζοντας αυτά που έγραψε ο migf1, κατάλαβα τι θέλεις να πεις. Σας ευχαριστώ όλους και πάλι, και να είστε έτοιμοι γιατί θα επανέλθω (όχι σήμερα και πιθανότατα όχι για τον ίδιο λόγο)
Moderators Kercyn Δημοσ. 28 Μαΐου 2014 Μέλος Moderators Δημοσ. 28 Μαΐου 2014 Θα μπορούσες να γράψεις (μιας και δεν υπάρχει λογικά αυτή τη στιγμή διαθέσιμη) μια function make_unique της οποίας η δουλειά θα είναι αυτή που κάνει και η make_pair: να μη χρειάζεται να γράψεις τα template arguments. O Sutter εδώ αλλά και παρόμοιες ερωτήσεις στο SO λένε ότι το make_shared/make_unique δεν χρησιμοποιείται όταν χρειάζεσαι custom deleter (όπως εγώ). Όμως, εδώ λέει ότι κλήσεις τύπου // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( std::unique_ptr<T1>{ new T1 }, std::unique_ptr<T2>{ new T2 } ); δεν προσφέρουν κάποιο exception safety, σε αντίθεση με κλήσεις τύπου // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( make_unique<T1>(), make_unique<T2>() ); Με τη διαχείριση των textures είμαι τυχερός γιατί το texture είναι ένα GLuint (= unsigned int). Τώρα, με άλλα assets δεν είμαι τόσο τυχερός. Πχ το Mix_Chunk και το Mix_Music (για ηχητικά εφέ και μουσική αντίστοιχα) χρειάζονται pointers για να παίξουν (οι συναρτήσεις που τα χρησιμοποιούν παίρνουν pointers). Η αντίστοιχη συνάρτηση του requestTexture για τα Mix_Chunks είναι αυτή: std::unique_ptr<Mix_Chunk, void(*)(Mix_Chunk *)> const & cResourceManager::requestMixChunk(MixChunkID id) { std::string path(std::get<1>(mixChunkData.at(id))); if (mixChunkData.find(id) == mixChunkData.end()) { if (PHYSFS_exists(path.c_str())) { auto file = std::unique_ptr<PHYSFS_File, int(*)(PHYSFS_File *)>(PHYSFS_openRead(path.c_str()), PHYSFS_close); if (file == nullptr) errmgr->throwErr(__FILE__, __func__, __LINE__, PHYSFS_getLastError()); PHYSFS_sint64 chunk_size = PHYSFS_fileLength(file.get()); std::unique_ptr<PHYSFS_sint64> chunk_data(new PHYSFS_sint64[chunk_size]); PHYSFS_sint64 length_read = PHYSFS_read(file.get(), chunk_data.get(), 1, chunk_size); if (length_read != chunk_size) errmgr->throwErr(__FILE__, __func__, __LINE__, "length_read != chunk_size"); auto rw = std::unique_ptr<SDL_RWops, void(*)(SDL_RWops *)>(SDL_RWFromMem(chunk_data.get(), chunk_size), SDL_FreeRW);; mixChunkData.emplace(id, std::make_tuple(std::unique_ptr<Mix_Chunk, void(*)(Mix_Chunk *)>(Mix_LoadWAV_RW(rw.get(), 0), Mix_FreeChunk), std::get<1>(mixChunkData.at(id)), std::get<2>(mixChunkData.at(id)))); } else errmgr->throwErr(__FILE__, __func__, __LINE__, "Texture " + utils::toString(int(id)) + " @ " + std::get<1>(mixChunkData.at(id))); } std::get<2>(mixChunkData.at(id))++; return std::get<0>(mixChunkData.at(id)); } Όπως βλέπετε, όταν πάω να βάλω ένα καινούριο ήχο στο map με τα chunks, χρησιμοποιώ τον constructor του unique_ptr με custom deleter. Αυτό δεν είναι σαν το πρώτο (λάθος) παράδειγμα που έδωσα πιο πάνω; Ή το όποιο leak θα το πιάσει το make_tuple; Και στην τελική, εφ' όσον έχω μόνο 1 pointer, κινδυνεύω από leaks; Διαβάζω ότι προτείνεται το std::allocate_shared ως εναλλακτική του make_shared όταν χρειάζεσαι custom allocator, αλλά, όπως και στη make_unique, δεν υπάρχει allocate_unique. Αλλά και να υπήρχε, αυτό που θέλω εγώ είναι ένα make_unique με custom deleter, όχι custom allocator. Μπορείτε να ξεκαθαρίσετε λίγο το τοπίο γιατί έχω μπερδευτεί;
Timonkaipumpa Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 Kercyn Δες και μηχανισμούς που υπάρχουν εδώ. Εντελώς πληροφοριακά. http://en.wikipedia.org/wiki/Automatic_Reference_Counting
παπι Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 Δεν μπορει να μπει deleter σε make_unique, διοτι πολυ απλα η συναρτηση χανει το νοημα της. Εαν προσεξεις καλα, η make_unique δεν παιρνει pointer, αλλα τα args των constructors του object που κραταει το unique_ptr. Βεβαια δεν ξερω απο variable template, για να σου πω, φτιαξε μια δικη σου make_unique... Κατα τα αλλα επιμενω. Reference counter εκ των εσω. Απλο και κατανοητο.
migf1 Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 [Το βάζω σε spoiler, ως off-topic, γιατί δεν απαντάει άμεσα στην ερώτησή σου] Θα συμφωνήσω κι εγώ με τον παπι. Αν δεν έχεις προχωρήσει πάρα πολύ στο προτζεκτ, ίσως είναι καλή ιδέα να το κάνεις re-design με εσωτερικό reference counting στα objects. Η C++ είναι ήδη πάρα πολύ περίπλοκη γλώσσα και νομίζω θα διευκολύνεις πολύ τη ζωή σου αν απλοποιήσεις τουλάχιστον τη λογική του προτζεκτ σου.
defacer Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 O Sutter εδώ αλλά και παρόμοιες ερωτήσεις στο SO λένε ότι το make_shared/make_unique δεν χρησιμοποιείται όταν χρειάζεσαι custom deleter (όπως εγώ). Η std::make_unique όντως δεν χρησιμοποιείται όταν θέλεις custom deleter γιατί σ' αυτή την περίπτωση θα έπρεπε για να έχει νόημα αυτό που κάνουμε να της δώσεις και custom allocator. Διαφορετικά το νέο object δημιουργείται με new οπότε ο custom deleter δεν έχει νόημα γιατί ο default είναι αυτός που κάνει τη σωστή δουλειά. Πρόσεξε ότι τα semantics της make_unique είναι ότι δημιουργεί το αντικείμενο που μπαίνει στο unique_ptr. Αυτό όμως δε σημαίνει πως εσύ δε μπορείς να γράψεις μια άλλη function η οποία έχει άλλα semantics και παίρνει έτοιμο έναν bare pointer του οποίου μετά το ownership δίνεται στο unique_ptr που επιστρέφει. Και αυτή η άλλη function θα μπορεί κανονικά όπως οποιοδήποτε άλλο function template να επωφεληθεί από auto deduction των template arguments, που είναι και ο λόγος για τον οποίο την πρότεινα. (παρένθεση: αν το κάνεις αυτό, έχεις το επιπλέον πλεονέκτημα ότι ο compiler θα βγάλει μόνος του το φίδι από την τρύπα σχετικά με το ποιός είναι ο τύπος του deleter. Όπως το έχεις τώρα δε μπορείς να περάσεις οτιδήποτε άλλο από function pointer -- για παράδειγμα ένα std::function με το ίδιο signature -- εκτός κι αν αλλάξεις και το type του deleter. Αν αντίθετα το type του deleter ήταν deduced από τον compiler τότε δε θα υπήρχε ανάγκη να κάνεις αυτή τη δεύτερη αλλαγή). Ήταν λάθος μου που έμπλεξα τη std::make_unique στη συζήτηση χωρίς να ξεκαθαρίσω ότι η πρώτη και βασική δουλειά της είναι να δημιουργήσει το pointee αντικείμενο ενώ η δική μου πρόταση μιλάει για μια function που δεν θα το δημιουργήσει (τη θέλουμε μόνο για ευκολότερη σύνταξη), επομένως σίγουρα δε μιλάμε για drop-in replacement και η ίδια η std::make_unique δεν κάνει αυτό που θέλουμε εδώ. Όμως, εδώ λέει ότι κλήσεις τύπου // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( std::unique_ptr<T1>{ new T1 }, std::unique_ptr<T2>{ new T2 } ); δεν προσφέρουν κάποιο exception safety, σε αντίθεση με κλήσεις τύπου // In some header file: void f( std::unique_ptr<T1>, std::unique_ptr<T2> ); // At some call site: f( make_unique<T1>(), make_unique<T2>() ); Ναι. Δε νομίζω ότι χρειάζεται εξήγηση γι' αυτό, ο Sutter ήδη το εξηγεί πολύ καλά. Με τη διαχείριση των textures είμαι τυχερός γιατί το texture είναι ένα GLuint (= unsigned int). Τώρα, με άλλα assets δεν είμαι τόσο τυχερός. Πχ το Mix_Chunk και το Mix_Music (για ηχητικά εφέ και μουσική αντίστοιχα) χρειάζονται pointers για να παίξουν (οι συναρτήσεις που τα χρησιμοποιούν παίρνουν pointers). Η αντίστοιχη συνάρτηση του requestTexture για τα Mix_Chunks είναι αυτή: std::unique_ptr<Mix_Chunk, void(*)(Mix_Chunk *)> const & cResourceManager::requestMixChunk(MixChunkID id) { std::string path(std::get<1>(mixChunkData.at(id))); if (mixChunkData.find(id) == mixChunkData.end()) { if (PHYSFS_exists(path.c_str())) { auto file = std::unique_ptr<PHYSFS_File, int(*)(PHYSFS_File *)>(PHYSFS_openRead(path.c_str()), PHYSFS_close); if (file == nullptr) errmgr->throwErr(__FILE__, __func__, __LINE__, PHYSFS_getLastError()); PHYSFS_sint64 chunk_size = PHYSFS_fileLength(file.get()); std::unique_ptr<PHYSFS_sint64> chunk_data(new PHYSFS_sint64[chunk_size]); PHYSFS_sint64 length_read = PHYSFS_read(file.get(), chunk_data.get(), 1, chunk_size); if (length_read != chunk_size) errmgr->throwErr(__FILE__, __func__, __LINE__, "length_read != chunk_size"); auto rw = std::unique_ptr<SDL_RWops, void(*)(SDL_RWops *)>(SDL_RWFromMem(chunk_data.get(), chunk_size), SDL_FreeRW);; mixChunkData.emplace(id, std::make_tuple(std::unique_ptr<Mix_Chunk, void(*)(Mix_Chunk *)>(Mix_LoadWAV_RW(rw.get(), 0), Mix_FreeChunk), std::get<1>(mixChunkData.at(id)), std::get<2>(mixChunkData.at(id)))); } else errmgr->throwErr(__FILE__, __func__, __LINE__, "Texture " + utils::toString(int(id)) + " @ " + std::get<1>(mixChunkData.at(id))); } std::get<2>(mixChunkData.at(id))++; return std::get<0>(mixChunkData.at(id)); } Όπως βλέπετε, όταν πάω να βάλω ένα καινούριο ήχο στο map με τα chunks, χρησιμοποιώ τον constructor του unique_ptr με custom deleter. Αυτό δεν είναι σαν το πρώτο (λάθος) παράδειγμα που έδωσα πιο πάνω; Ή το όποιο leak θα το πιάσει το make_tuple; Και στην τελική, εφ' όσον έχω μόνο 1 pointer, κινδυνεύω από leaks; Μιλώντας για αυτό: mixChunkData.emplace(id, std::make_tuple(std::unique_ptr<Mix_Chunk, void(*)(Mix_Chunk *)>(Mix_LoadWAV_RW(rw.get(), 0), Mix_FreeChunk), std::get<1>(mixChunkData.at(id)), std::get<2>(mixChunkData.at(id)))); Θεωρητικά είναι λάθος. Aν πρώτα γίνει evaluate η Mix_LoadWAV_RW(...) και μετά, κάποια στιγμή πριν κληθεί ο constructor του unique_ptr (διάστημα στο οποίο είναι legal να ασχολείται το πρόγραμμα με τα "άσχετα" .at(), std::get κλπ), πάρεις exception τότε το Mix_Chunk που έχεις στα χέρια σου θα κάνει leak. Επίσης το ίδιο θα συνέβαινε αν μπορούσε να κάνει throw ο unique_ptr(). Πρακτικά δεν υπάρχει πρόβλημα γιατί τόσο ο unique_ptr() όσο και η std::get είναι noexcept οπότε εκεί δε μπορεί να γίνει στραβή, και η map::at δεν πρόκειται να κάνει throw γιατί αν ήταν έτσι θα είχε ήδη κάνει throw στην πρώτη γραμμή (εκεί που φτιάχνεις το path). Αλλά καταλαβαίνεις ότι αυτό είναι μπακάλικο επιχείρημα, παρόλο που ισχύει. Τώρα, κάνοντας ένα βήμα πίσω: ο κώδικας όπως τον βλέπω δε μπορεί να είναι σωστός -- έχεις if(x.find() == x.end()) μετά από το .at() (χωρίς κανένα έλεγχο) της δημιουργίας του path -- είναι δυνατόν να έχει νόημα αυτό το if? Θα είχε κάνει ήδη throw to at(). Επίσης, μπαίνοντας θεωρητικά στο if πάλι πας και κάνεις .at() χωρίς όμως να έχεις βάλει τίποτα στη συγκεκριμένη θέση => guaranteed exception. Τι φάση? Φαντάζομαι buggy απευθείας μετατροπή του προηγούμενου implementation. Διαβάζω ότι προτείνεται το std::allocate_shared ως εναλλακτική του make_shared όταν χρειάζεσαι custom allocator, αλλά, όπως και στη make_unique, δεν υπάρχει allocate_unique. Αλλά και να υπήρχε, αυτό που θέλω εγώ είναι ένα make_unique με custom deleter, όχι custom allocator. Μπορείτε να ξεκαθαρίσετε λίγο το τοπίο γιατί έχω μπερδευτεί; Warning: μπαίνεις σε βαθιά νερά. Ο allocator της std::allocate_shared δε χρησιμοποιείται για να κάνει allocate και το ίδιο το pointee αντικείμενο αλλά και για να κάνει allocate τα εσωτερικά structures που χρειάζεται ο shared_ptr για να δουλέψει (contrast με το να καλέσεις shared_ptr() με custom allocator, στην οποία περίπτωση αυτός χρησιμοποιείται για τα εσωτερικά structures αλλά όχι για το pointee το οποίο το περνάς ξεχωριστά). Επομένως ένας τέτοιος allocator πρέπει να είναι έτοιμος να κάνει γενικότερη δουλειά από το απλά να φτιάξει ένα pointee και τίποτα άλλο. Μπορείς να γράψεις μόνος σου μια allocate_unique αλλά είναι δύσκολο. Εγώ δεν ξέρω αν θα μπορούσα να το κάνω ολόσωστα και σίγουρα θα έπρεπε να παιδευτώ πολύ στην πορεία. Related. Για να ξεκαθαρίσω λοιπόν μια σύνοψη: Λάθος μου που έβαλα τη make_unique στην κουβέντα, πιο πολύ κακό έκανε παρά καλό. Έπρεπε αντί γι' αυτήν να μιλούσαμε για allocate_unique. make_unique με custom deleter και χωρίς custom allocator δεν έχει νόημα, και αν όντως έχεις custom allocator τότε φτάσαμε στην allocate_unique. Θα φτύσεις αίμα για να γράψεις σωστή allocate_unique και έναν κατάλληλο allocator γύρω από τις συναρτήσεις της SDL. Αντί να κάνεις τέτοια πράγματα, το σωστότερο όλων είναι να χρησιμοποιήσεις C++ bindings. Δηλαδή να υπάρχει μια class που εσωτερικά θα κάνει τα πάντα σχετικά με ένα PHYSFS_File, μιά άλλη για SDL_RWOps, για Mix_Chunk, κλπ. Φτιάξε τον κώδικα γιατί αυτή η νεα version με τα tuples απλά δεν δουλεύει. Μετά μπορούμε να συζητήσουμε για καλύτερα implementations. Κακώς μίλησα για tuple, κάνε μια struct με ονόματα για να διαβάζουμε κώδικα σαν άνθρωποι. (σχετικό με 4 και 6) Κάνοντας το #4, ποτέ ΠΟΤΕ ΠΟΤΕ μη βάλεις reference counters που λέει το παπί μέσα σε κείνες τις ίδιες classes επειδή έτσι κατουράς πάνω στο πτώμα της SRP. Παπί sorry, είναι κακό design. Epic fail το γεγονός ότι είπα για RAII κλπ χωρίς να το σκεφτώ λίγο περισσότερο πρώτα και να πω κατευθείαν το #4. 1
παπι Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 Να σου πω την αληθεια, δεν σε καταλαβα. Που ειναι το κακο σε αυτο που λεω στο #8 ; Και γιατι δεν ειναι SRP;
defacer Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 Γιατί θα έχεις μία (πολλές βασικά αλλά δεν έχει σημασία) class που θα κάνει δύο άσχετα μεταξύ τους πράγματα: Model/encapsulate ένα κάτι (stream, chunk, texture, οτι ναναι δεν κολλάμε εκεί) Συντονισμό αυτού του ενός κάτι με όλα τα υπόλοιπα όμοιά του Μόνο προβλήματα θα δημιουργήσει αυτό.
Moderators Kercyn Δημοσ. 28 Μαΐου 2014 Μέλος Moderators Δημοσ. 28 Μαΐου 2014 Μάλιστα. Σ' ευχαριστώ για την ανάλυση που έκανες (ναι, το releaseMixChunk είναι όντως "buggy απευθείας μετατροπή"). Έριξα μια ματιά στο link του open-std, διάβασα ό,τι μπορούσα και δε μπορώ να πω ότι μου πολυάρεσαν αυτά που διάβασα Προφανώς δεν έχω σκοπό να φτιάξω τέτοια πράγματα ούτε να κάνω φύλλο και φτερό όλη την SDL (και τη PHYSFS, και την OpenGL και όλες τις άλλες βιβλιοθήκες που χρησιμοποιώ) για να φτιάξω τα κατάλληλα C++ bindings. Για την SDL βρήκα το SDLpp, αλλά με ξένισε είναι ότι επιστρέφει raw pointers σε πολλά σημεία. Είναι πια τόσο κακό αυτό ή είμαι υπερβολικός (η περιγραφή του πάντως λέει ότι ακολουθεί C++ και τα τελευταία πρότυπα και unique_ptr και όλα αυτά...); Κάποια από τα πράγματα που κάνει τα έχω κάνει ήδη, και πρέπει έτσι κι αλλιώς να την πειράξω για να τη φέρω στα μέτρα μου. παπι και migf1, ο λόγος που είμαι διστακτικός να χρησιμοποιήσω το ref counting είναι γιατί δε θέλω να υπάρχουν πολλά πράγματα που μπορούν να πειράξουν ένα asset. Δεν ήξερα ότι αυτό το SRP είχε όνομα, αλλά με πρόλαβε ο defacer: δε θέλω αυτό το κάτι που έχω να κάνει κάτι άλλο από το να κρατάει τις πληροφορίες ενός asset. Βρίσκω τη χρήση ενός κεντρικού manager πιο "σωστή" και λιγότερο επιρρεπή σε gotchas. Θα ξαναδιαβάσω αυτά που έχετε γράψει, θα προσπαθήσω να φτιάξω την requestMixChunk σωστά αυτή τη φορά και θα επανέρθω
παπι Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 Κατσε βρε παιδι μου. Εαν δεν κανει το texture μια οντοτητα, τοτε για τι oo project μιλαμε; Ετσι οπως ειναι τωρα (αυτα που ειδα δηλαδη) ειναι ενα "να παω ή παω για oo". Προσωπικα πιστευω οτι εαν θελει να κρατησει το χαρακτηρα του api που χρησιμοποιει, και παραλληλα να εκμεταλλευτει την stl. Τοτε, τοτε ειναι καλυτερα να μην βαλει καν κλασεις, αντι αυτου να βαλει TU.
migf1 Δημοσ. 28 Μαΐου 2014 Δημοσ. 28 Μαΐου 2014 ... παπι και migf1, ο λόγος που είμαι διστακτικός να χρησιμοποιήσω το ref counting είναι γιατί δε θέλω να υπάρχουν πολλά πράγματα που μπορούν να πειράξουν ένα asset. Δεν ήξερα ότι αυτό το SRP είχε όνομα, αλλά με πρόλαβε ο defacer: δε θέλω αυτό το κάτι που έχω να κάνει κάτι άλλο από το να κρατάει τις πληροφορίες ενός asset. Βρίσκω τη χρήση ενός κεντρικού manager πιο "σωστή" και λιγότερο επιρρεπή σε gotchas. Θα ξαναδιαβάσω αυτά που έχετε γράψει, θα προσπαθήσω να φτιάξω την requestMixChunk σωστά αυτή τη φορά και θα επανέρθω Εγώ πάλι δεν βλέπω να παραβιάζεται κανένα SRP διατηρώντας εσωτερικά στο factory το reference counting των objects. Από πότε το construction και το destruction ενός object θεωρείται εξωγενής παράγοντας στο ίδιο το object; Εν πάσει περιπτώσει, το τι λογίζεται ως οντότητα και τι responsibilities θα έχει και τι όχι στα δικά σου πρότζεκτ το καθορίζεις εσύ, σύμφωνα με τις ανάγκες του πρότζεκτ σου. Τα design patterns, τα principles, τα coding-styles, κλπ, κλπ, δεν είναι ούτε ευαγγέλια, ούτε πανάκειες, ούτε... μπαλαντέρηδες. Αν εσένα σου απλοποιεί την λογική, τον κώδικα, το maintability, ή οτιδήποτε άλλο θεωρείς σημαντικό, χέστηκες αν παραβιάζεις το τάδε pattern ή το δείνα principle (που είναι και πολύ πιθανό να μη ταιριάζει στις ανάγκες του δικού σου πρότζεκτ... αν ταίριαζε κι εξυπηρετούσε, υποθέτω θα σου απλοποιούσε τα πράγματα αντί να στα περιπλέκει η μη παραβίασή του, οπότε δεν θα το παραβίαζες).
AllCowsEatGrass Δημοσ. 29 Μαΐου 2014 Δημοσ. 29 Μαΐου 2014 Τα design patterns, τα principles, τα coding-styles, κλπ, κλπ, δεν είναι ούτε ευαγγέλια, ούτε πανάκειες, ούτε... μπαλαντέρηδες. Αν εσένα σου απλοποιεί την λογική, τον κώδικα, το maintability, ή οτιδήποτε άλλο θεωρείς σημαντικό, χέστηκες αν παραβιάζεις το τάδε pattern ή το δείνα principle (που είναι και πολύ πιθανό να μη ταιριάζει στις ανάγκες του δικού σου πρότζεκτ... αν ταίριαζε κι εξυπηρετούσε, υποθέτω θα σου απλοποιούσε τα πράγματα αντί να στα περιπλέκει η μη παραβίασή του, οπότε δεν θα το παραβίαζες). Θα συμφωνήσω εν μέρη, αλλά νομίζω πως είναι the other way around και εξηγώ. Πιστεύω η καλύτερη αναλογία για το software development που έχω διαβάσει κατά καιρούς είναι η αρχιτεκτονική σπιτιών. Το πρόβλημα του να μεταβείς απ το ένα δωμάτιο στο άλλο είναι απλό και λυνετε βάζοντας μία πόρτα. Υπάρχει στάνταρ τρόπος για το πως φτιάχνεται μία πόρτα και αυτη έχει στάνταρ διαστάσεις...Τώρα κάποιος μπορεί να γκρεμίσει το τοίχο, γιατί τον βολεύει. Κάπως έτσι είναι και ένα design pattern, δεν είναι ευαγγέλιο ούτε πανάκεια αλλα αν το ακολουθήσεις σου υπόσχεται κάποια αποτελέσματα (θετικα/αρνητικα). Μπορείς να το εξελίξεις και να το τροποιήσεις για τις ανάγκες σου άλλα μπορεί, άθελά σου, να φτάσεις σε σημείο να γκρεμίζεις τοίχους, ενώ μία πόρτα θα ήταν αρκετή.
defacer Δημοσ. 29 Μαΐου 2014 Δημοσ. 29 Μαΐου 2014 Προφανώς δεν έχω σκοπό να φτιάξω τέτοια πράγματα ούτε να κάνω φύλλο και φτερό όλη την SDL (και τη PHYSFS, και την OpenGL και όλες τις άλλες βιβλιοθήκες που χρησιμοποιώ) για να φτιάξω τα κατάλληλα C++ bindings. Αυτό είναι κλασικός προβληματισμός και πράγματι έρχεται η ώρα που αναρωτιέσαι αν αξίζει τον κόπο, και πολλές φορές μένει μια αίσθηση ότι είναι "πολλή δουλειά για το τίποτα". Σου προτείνω λοιπόν να κάνεις το εξής: Γράψε σωστά τη requestMixChunk όπως είναι τώρα Ξαναγράψτη χρησιμοποιώντας υποθετικές classes που αυτή τη στιγμή δεν υπάρχουν Σύγκρινε το αποτέλεσμα και πολλαπλασίασε τη διαφορά επί το πόσες αντίστοιχες υπάρχουν στο πρόγραμμά σου Αυτό θα σου δώσει να καταλάβεις πόσο λιγότερη δουλειά θα είχες χρειαστεί αν πρώτα είχε γίνει η "πολλή δουλειά για το τίποτα". Και βέβαια έτσι μετράς μόνο τα κιλά του κώδικα, ενώ αφήνεις απέξω πράγματα όπως το αν έχεις γράψει bugs. Για παράδειγμα εδώ υπήρχε εκείνο το "θεωρητικό" bug, το οποίο (κι εδώ είναι το άσχημο) χωρίς πολλή προσπάθεια εκ μέρους σου στο μέλλον θα μπορούσε να γίνει και πρακτικό χωρίς να το καταλάβεις με τίποτα εκείνη τη στιγμή. Επιπλέον, it's a learning experience. Φαντάζομαι ότι στη φάση που είσαι learning experience == good. Για την SDL βρήκα το SDLpp, αλλά με ξένισε είναι ότι επιστρέφει raw pointers σε πολλά σημεία. Είναι πια τόσο κακό αυτό ή είμαι υπερβολικός (η περιγραφή του πάντως λέει ότι ακολουθεί C++ και τα τελευταία πρότυπα και unique_ptr και όλα αυτά...); Κάποια από τα πράγματα που κάνει τα έχω κάνει ήδη, και πρέπει έτσι κι αλλιώς να την πειράξω για να τη φέρω στα μέτρα μου. Για να είμαι ειλικρινής μου φαίνεται σαν κάτι που ασχολήθηκε κάποιος λίγο ένα σαββατοκύριακο και μετά το παράτησε. Δεν εμπνέει εμπιστοσύνη, και δεν είναι και πολύς κώδικας για να πεις ότι είναι γολγοθάς να το γράψεις μόνος σου. Για τα design patterns κλπ το μόνο που έχω να πω είναι πως όλοι οι masters ξέρουν πότε αξίζει κάποιος κανόνας να παραβιαστεί. Το πρόβλημα είναι πως παραβιάζοντας τον κανόνα απλώς δε βγαίνει κάποιο συμπέρασμα, γιατί εκτός από τους masters το κάνουν και οι άσχετοι. 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα