Moderators Kercyn Δημοσ. 20 Φεβρουαρίου 2016 Moderators Δημοσ. 20 Φεβρουαρίου 2016 Καλησπέρα σας. Προσπαθώ να φτιάξω έναν wrapper για μια C βιβλιοθήκη, η οποία έχει μια συνάρτηση που διαβάζει πράγματα από ένα αρχείο. PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount); Το δικό μου signature είναι αυτό: void read(BPhysFSFilePtr & handle, std::vector<T> & buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) const ...και κάπου μέσα έχω αυτό: LengthRead = PHYSFS_read(handle, static_cast<void *>(&buffer), 1, FileSize); Πέρα από το cast, προσπάθησα να δώσω και buffer.data(). Δε μου διάβαζε τίποτα (ο buffer έμενε με 0 size). Προσπάθησα να κάνω resize ή reserve πριν περάσω το buffer. Όπως και με το cast, μου έλεγε ότι διάβαζε σωστά τα περιεχόμενα του αρχείου (δηλαδή διάβαζε τόσα αντικείμενα όσο και το μέγεθος του αρχείου), αλλά όταν πήγαινα να δω τα περιεχόμενα του buffer μου έλεγε σε όλα τα στοιχεία ότι δεν μπορώ να προσπελάσω τη μνήμη. Άμα το άφηνα να τρέξει χωρίς breakpoint μου crashαρε (δε θυμάμαι ακριβώς το μήνυμα αλλά ήταν memory-related, πήγαινα σε μνήμη που δεν ήταν δική μου). Τελικά κατέληξα σ' αυτό: template <typename T> void read(BPhysFSFilePtr & handle, std::vector<T> & buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) const { PHYSFS_sint64 FileSize = fileLength(handle); PHYSFS_sint64 LengthRead; try { TCHAR *RawBuffer; RawBuffer = new TCHAR[FileSize]; LengthRead = PHYSFS_read(handle, RawBuffer, 1, FileSize); if (LengthRead != FileSize) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, "LengthRead: " + LengthRead + std::string("\nFileSize: ") + Utils::ToString(FileSize) + "\nPHYSFS last error: " + std::string(PHYSFS_getLastError())); } else if (LengthRead == -1) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, "LengthRead == -1\nPHYSFS last error: " + std::string(PHYSFS_getLastError())); } buffer = std::vector<T>(RawBuffer, RawBuffer + LengthRead); } catch (std::exception e) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, e.what()); } } Αυτό δουλεύει, αλλά δεν ξέρω αν είναι exception-safe. Αυτό που θέλω γίνεται με κάποιο τρόπο χωρίς new, μόνο με το vector; Αν όχι, τότε έχετε κάποια πρόταση για το πώς μπορεί να γίνει exception safe, αν δεν είναι ήδη; Ευχαριστώ.
moukoublen Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Παράπλευρη σημείωση: Δεν έχει leak αυτό; Το RawBuffer πότε γίνεται delete; Δεδομένου οτι στον constructor του vector που χρησιμοποιείς γίνεται αντιγραφή και όχι χρήση της ίδιας περιοχής μνήμης νομίζω.
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Γενικά μιλώντας το κάνεις περνώντας .data() ή ισοδύναμα &vector[0]. Δώσε το συγκεκριμένο κώδικα που σου κρασάρει καλύτερα.
Moderators Kercyn Δημοσ. 20 Φεβρουαρίου 2016 Μέλος Moderators Δημοσ. 20 Φεβρουαρίου 2016 Παράπλευρη σημείωση: Δεν έχει leak αυτό; Το RawBuffer πότε γίνεται delete; Δεδομένου οτι στον constructor του vector που χρησιμοποιείς γίνεται αντιγραφή και όχι χρήση της ίδιας περιοχής μνήμης νομίζω. Ναι έχεις δίκιο, το ξέχασα τελείως. Η αλήθεια είναι ότι είχα πιο πολύ το μυαλό μου στο να δω τι γίνεται και πώς θα το κάνω παρά στο να κάνω delete. Γενικά μιλώντας το κάνεις περνώντας .data() ή ισοδύναμα &vector[0]. Δώσε το συγκεκριμένο κώδικα που σου κρασάρει καλύτερα. Με .data() που το δοκίμασα δεν διαβάζει καν. Αυτός είναι ο κώδικας που μου κρασάρει. Αν εννοείς τον κώδικα που καλεί τη συνάρτηση, είναι αυτός: std::vector<TCHAR> GameData; BPhysFSFilePtr GameFile(GAMEDATA_FILE); PHYSFS_sint64 GameFileLength = MPhysFSManager::Instance().fileLength(GameFile); if (GameFileLength == -1) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, "Could not determine image size for " + GAMEDATA_FILE); } MPhysFSManager::Instance().read<TCHAR>(GameFile, GameData, 1, GameFileLength);
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Με .data() που το δοκίμασα δεν διαβάζει καν. Αυτός είναι ο κώδικας που μου κρασάρει. Αν εννοείς τον κώδικα που καλεί τη συνάρτηση, είναι αυτός: Εννοώ το call στη PHYSFS_read, συν το ακριβές static type οποιουδήποτε πράγματος εμφανίζεται μέσα στις παρενθέσεις, συν οτιδήποτε τυχόν έχεις ή δεν έχεις κάνει με το vector από τη δημιουργία του μέχρι το call. Αυτό που περιγράφεις ακούγεται σαν vector με μέγεθος άλλο από αυτό που νομίζεις η/και buffer overrun.
moukoublen Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 template <typename T> void read(BPhysFSFilePtr & handle, std::vector<T> & buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) const { PHYSFS_sint64 FileSize = fileLength(handle); PHYSFS_sint64 LengthRead; try { buffer.resize(LengthRead); LengthRead = PHYSFS_read(handle, buffer.data(), 1, FileSize); if (LengthRead != FileSize) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, "LengthRead: " + LengthRead + std::string("\nFileSize: ") + Utils::ToString(FileSize) + "\nPHYSFS last error: " + std::string(PHYSFS_getLastError())); } else if (LengthRead == -1) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, "LengthRead == -1\nPHYSFS last error: " + std::string(PHYSFS_getLastError())); } } catch (std::exception e) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, e.what()); } } Νομίζω κάτι τέτοιο εννοεί και ο defacer. (Κάπως πρόχειρα εδώ. Μπορεί να έχει ξεφύγει κάτι).
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Α και τώρα που διάβασα λίγο περισσότερο κώδικα... γιατί vector και όχι π.χ. std::string αν μιλάμε για gamedata και ιστορίες; Έχεις πάνω κάτω τις ίδιες ευκολίες συν πολύ εύκολη μετατροπή σε stream αν θελήσεις. Άμα το άφηνα να τρέξει χωρίς breakpoint μου crashαρε Και τώρα που διάβασα και αυτό πιο προσεκτικά... Αν η διαφορά ήταν συγκεκριμένα το breakpoint και όχι το debug vs release build, μήπως έχεις race condition κάπου;
Moderators Kercyn Δημοσ. 20 Φεβρουαρίου 2016 Μέλος Moderators Δημοσ. 20 Φεβρουαρίου 2016 Γιατί η PHYFS_read διαβάζει και άλλα πράγματα (εικόνες, ήχους κλπ). Η PhysicsFS είναι βιβλιοθήκη για να φορτώνεις assets από compressed αρχεία (zip, tar, w/e). Για εικόνες πχ χρησιμοποιώ PHYSFS_sint64 στο read. Οπότε θέλω ένα vector με ό,τι χρειάζεται να διαβάσω εκείνη τη στιγμή, όχι μόνο χαρακτήρες. moukoublen αυτός είναι κώδικας που έχω δοκιμάσει, όπως έγραψα και στο αρχικό post, αλλά δυστυχώς δε με αφήνει να προσπελάσω τη μνήμη που δείχνει ότι διαβάζει. Ο μόνος κώδικας που νομίζω ότι λείπει είναι το BPhysFSFilePtr. Header #pragma once /*! \file */ #include <string> #include <memory> #include <functional> #include "physfs.h" class BPhysFSFilePtr final { public: BPhysFSFilePtr(std::string Path_); ~BPhysFSFilePtr(); operator PHYSFS_File *(); private: std::function<int(PHYSFS_File *)> Deleter; std::unique_ptr<PHYSFS_File, decltype(Deleter)> File; std::string Path; }; CPP #include "BPhysFSFilePtr.hpp" #include "Managers.hpp" #include <memory> #include <functional> #include <string> BPhysFSFilePtr::BPhysFSFilePtr(std::string Path_) { //todo add path to error logs (friend PHYSFS manager or something...) Path = Path_; Deleter = std::bind(&PHYSFS_close, std::placeholders::_1); auto FileRaw = PHYSFS_openRead(Path.c_str()); if (FileRaw == nullptr) { MErrorManager::Instance().ThrowError(__FILE__, __func__, __LINE__, Path + "\n" + PHYSFS_getLastError()); } File = std::unique_ptr<PHYSFS_File, decltype(Deleter)>(FileRaw, Deleter); } BPhysFSFilePtr::~BPhysFSFilePtr() { } BPhysFSFilePtr::operator PHYSFS_File *() { return File.get(); } Το PHYSFS_sint64 είναι typedef signed long long. Αν η διαφορά ήταν συγκεκριμένα το breakpoint και όχι το debug vs release build, μήπως έχεις race condition κάπου; Όχι, δεν κάνει καμιά διαφορά το breakpoint, απλώς το είπα για να επισημάνω ότι το πρόγραμμα δεν τρέχει καν, και ότι μπορώ να δω τι γίνεται μόνο επειδή κάνω step over. PhysFS docs Αυτό που περιγράφεις ακούγεται σαν vector με μέγεθος άλλο από αυτό που νομίζεις η/και buffer overrun. Αυτό προσπαθώ να καταλάβω κι εγώ, πού ακριβώς γίνεται η βλακεία.
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Δε βλέπω γιατί να μη δουλεύει ο κώδικας του moukoublen εκτός από το προφανές λάθος της στιγμής στο resize: buffer.resize(FileSize); LengthRead = PHYSFS_read(handle, buffer.data(), 1, FileSize); Υπόψη ότι αντί για resize πιο γρήγορα reserve και ότι αν θες να διαβάσεις κάτι άλλο εκτός από char το string είναι template specialization του std::basic_string. Και το Deleter γιατί είναι member αφού δε σου χρειάζεται really? Αν είναι για DRY στο signature κάντο typedef.
Moderators Kercyn Δημοσ. 20 Φεβρουαρίου 2016 Μέλος Moderators Δημοσ. 20 Φεβρουαρίου 2016 Δε βλέπω γιατί να μη δουλεύει ο κώδικας του moukoublen εκτός από το προφανές λάθος της στιγμής στο resize: buffer.resize(FileSize); LengthRead = PHYSFS_read(handle, buffer.data(), 1, FileSize); Υπόψη ότι αντί για resize πιο γρήγορα reserve και ότι αν θες να διαβάσεις κάτι άλλο εκτός από char το string είναι template specialization του std::basic_string. Και το Deleter γιατί είναι member αφού δε σου χρειάζεται really? Αν είναι για DRY στο signature κάντο typedef. Ξανατρέχω τον κώδικα του moukoublen, και παρόλο που μου δείχνει ότι το size του buffer είναι 0, για κάποιο λόγο φαίνεται ότι έχει αυτά που θέλω μέσα. Και την πρώτη φορά το ίδιο πρέπει να είχε γίνει, απλώς την πρώτη φορά είδα το size 0 και δεν άνοιξα το RawView να δω αν έχει τίποτα μέσα. Για το std::basic_string το γνωρίζω αλλά δεν μου είχε περάσει απ' το μυαλό να το χρησιμοποιήσω. Λες για τον Deleter του BPhysFSFilePtr; Είναι η συνάρτηση που πρέπει να καλέσω για να κλείσω το αρχείο που άνοιξα, οπότε την καλώ στον destructor του BPhysFSFilePtr (όταν καταστραφεί το File και κληθεί ο custom deleter του). Σας ευχαριστώ και τους δύο, θα δω τι μπορώ να κάνω και αν συνεχίσει να υπάρχει πρόβλημα θα επανέρθω.
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Λες για τον Deleter του BPhysFSFilePtr; Είναι η συνάρτηση που πρέπει να καλέσω για να κλείσω το αρχείο που άνοιξα, οπότε την καλώ στον destructor του BPhysFSFilePtr (όταν καταστραφεί το File και κληθεί ο custom deleter του). Ναι, εννοώ ότι τον deleter δε χρειάζεται να τον έχεις και στην BPhysFSFilePtr. Τον έχει ήδη ο unique_ptr που είναι και αυτός που θα τον χρειαστεί.
moukoublen Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Περίεργο. Νομίζω οτι με το resize γίνεται update και το size και όχι μόνο το capacity.
defacer Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Ισχύει, ούτε γω έχω καταλάβει γιατί του δείχνει size 0.
M2000 Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Το μήκος του Vector βγαίνει με τα περιεχόμενα; Δηλαδή αυτό που έχει σώσει είναι αυτό που παίρνει στο αρχείο μετά; Όταν το πρόγραμμα δεν φαίνεται λάθος και δεν πάει σωστά τότε τα δεδομένα έχουν θέμα. Για να λυθεί το θέμα πρέπει να φτιαχτεί κώδικας για ένα πράγμα στο vector και να μετρηθεί τι πήγε στο αρχείο και τι δίνει μετά στο διάβασμα! (χοντρικά τα έγραψα γιατί δεν έχω εμπειρία από c++ αλλά κάτι ξέρω από αρχεία)
παπι Δημοσ. 20 Φεβρουαρίου 2016 Δημοσ. 20 Φεβρουαρίου 2016 Μπορεις να παιξεις με iterators. πχ #include <iterator> #include <vector> template<class It> void wateva(It& output) { for (int i = 0; i < 10; i++) output = i; } int main(int argc, char **argv) { std::vector<int> buf; wateva(std::back_inserter(buf)); return 0; } 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα