Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

  • Moderators
Δημοσ.

Καλησπέρα σας.

 

Προσπαθώ να φτιάξω έναν 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, αν δεν είναι ήδη;

 

Ευχαριστώ.

Δημοσ.

Παράπλευρη σημείωση:

 

Δεν έχει leak αυτό;

 

Το RawBuffer πότε γίνεται delete; Δεδομένου οτι στον constructor του vector που χρησιμοποιείς γίνεται αντιγραφή και όχι χρήση της ίδιας περιοχής μνήμης νομίζω. 

Δημοσ.

Γενικά μιλώντας το κάνεις περνώντας .data() ή ισοδύναμα &vector[0].

 

Δώσε το συγκεκριμένο κώδικα που σου κρασάρει καλύτερα.

  • Moderators
Δημοσ.

Παράπλευρη σημείωση:

 

Δεν έχει 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);
Δημοσ.

Με .data() που το δοκίμασα δεν διαβάζει καν. Αυτός είναι ο κώδικας που μου κρασάρει. Αν εννοείς τον κώδικα που καλεί τη συνάρτηση, είναι αυτός:

 

Εννοώ το call στη PHYSFS_read, συν το ακριβές static type οποιουδήποτε πράγματος εμφανίζεται μέσα στις παρενθέσεις, συν οτιδήποτε τυχόν έχεις ή δεν έχεις κάνει με το vector από τη δημιουργία του μέχρι το call.

 

Αυτό που περιγράφεις ακούγεται σαν vector με μέγεθος άλλο από αυτό που νομίζεις η/και buffer overrun.

Δημοσ.
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. (Κάπως πρόχειρα εδώ. Μπορεί να έχει ξεφύγει κάτι).

Δημοσ.

Α και τώρα που διάβασα λίγο περισσότερο κώδικα... γιατί vector και όχι π.χ. std::string αν μιλάμε για gamedata και ιστορίες; Έχεις πάνω κάτω τις ίδιες ευκολίες συν πολύ εύκολη μετατροπή σε stream αν θελήσεις.


Άμα το άφηνα να τρέξει χωρίς breakpoint μου crashαρε

 

Και τώρα που διάβασα και αυτό πιο προσεκτικά...

 

Αν η διαφορά ήταν συγκεκριμένα το breakpoint και όχι το debug vs release build, μήπως έχεις race condition κάπου;

  • Moderators
Δημοσ.

Γιατί η 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.

Αυτό προσπαθώ να καταλάβω κι εγώ, πού ακριβώς γίνεται η βλακεία.

Δημοσ.

Δε βλέπω γιατί να μη δουλεύει ο κώδικας του 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
Δημοσ.

Δε βλέπω γιατί να μη δουλεύει ο κώδικας του 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 του).

 

Σας ευχαριστώ και τους δύο, θα δω τι μπορώ να κάνω και αν συνεχίσει να υπάρχει πρόβλημα θα επανέρθω.

Δημοσ.

Λες για τον Deleter του BPhysFSFilePtr; Είναι η συνάρτηση που πρέπει να καλέσω για να κλείσω το αρχείο που άνοιξα, οπότε την καλώ στον destructor του BPhysFSFilePtr (όταν καταστραφεί το File και κληθεί ο custom deleter του).

 

Ναι, εννοώ ότι τον deleter δε χρειάζεται να τον έχεις και στην BPhysFSFilePtr. Τον έχει ήδη ο unique_ptr που είναι και αυτός που θα τον χρειαστεί.

Δημοσ.

Το μήκος του Vector βγαίνει με τα περιεχόμενα; Δηλαδή αυτό που έχει σώσει είναι αυτό που παίρνει στο αρχείο μετά; Όταν το πρόγραμμα δεν φαίνεται λάθος και δεν πάει σωστά τότε τα δεδομένα έχουν θέμα. Για να λυθεί το θέμα πρέπει να φτιαχτεί κώδικας για ένα πράγμα στο vector και να μετρηθεί τι πήγε στο αρχείο και τι δίνει μετά στο διάβασμα!

(χοντρικά τα έγραψα γιατί δεν έχω εμπειρία από c++ αλλά κάτι ξέρω από αρχεία)

Δημοσ.

Μπορεις να παιξεις με 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;
}



  • Like 1

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...