Domanda

Ecco probabilmente una domanda molto noobish per voi: come (se possibile) posso restituire un ifstream da una funzione

In sostanza, ho bisogno di ottenere il nome del file di un database da parte dell'utente, e se il database con quel nome non esiste, quindi ho bisogno di creare il file per l'utente. So come fare, ma solo chiedendo all'utente di riavviare il programma dopo la creazione del file. Volevo evitare che disagi per l'utente, se possibile, ma la funzione di seguito non viene compilato in gcc:

ifstream getFile() {
    string fileName;
    cout << "Please enter in the name of the file you'd like to open: ";
    cin >> fileName;
    ifstream first(fileName.c_str());
    if(first.fail()) {
        cout << "File " << fileName << " not found.\n";
        first.close();
        ofstream second(fileName.c_str());
        cout << "File created.\n";
        second.close();
        ifstream third(fileName.c_str());
        return third; //compiler error here
    }
    else
        return first;
}

EDIT: mi dispiace, ha dimenticato di dirvi dove e che cosa l'errore del compilatore è stato:

main.cpp:45: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here 

EDIT: ho cambiato la funzione per restituire un puntatore, invece, come suggerito Remo, e ha cambiato la linea in main () per "banca dati ifstream = * getFile ()"; ora ottengo questo errore di nuovo, ma questa volta in prima linea in main ():

main.cpp:27: note: synthesized method ‘std::basic_ifstream<char, std::char_traits<char> >::basic_ifstream(const std::basic_ifstream<char, std::char_traits<char> >&)’ first required here
È stato utile?

Soluzione

bool checkFileExistence(const string& filename)
{
    ifstream f(filename.c_str());
    return f.is_open();
}

string getFileName()
{
    string filename;
    cout << "Please enter in the name of the file you'd like to open: ";
    cin >> filename;
    return filename;
}

void getFile(string filename, /*out*/ ifstream& file)
{
    const bool file_exists = checkFileExistence(filename);
    if (!file_exists) {
        cout << "File " << filename << " not found." << endl;
        filename = getFileName();  // poor style to reset input parameter though
        ofstream dummy(filename.c_str();
        if (!dummy.is_open()) {
            cerr << "Could not create file." << endl;
            return;
        }
        cout << "File created." << endl;
    }
    file.open(filename.c_str());
}

int main()
{
    // ...
    ifstream file;
    getFile("filename.ext", file);
    if (file.is_open()) {
        // do any stuff with file
    }
    // ...
}

Altri suggerimenti

No, non proprio. ifstream non ha un costruttore di copia, e se si tenta di restituire uno, che significa copiando l'istanza nella funzione out ovunque le esigenze di ritorno per andare.

La soluzione usuale è quello di passare un riferimento a uno, e modificare tale riferimento nella funzione.

Modifica: mentre che permetterà il codice per il lavoro, non risolverà il problema di fondo. In questo momento, si sta mescolando due responsabilità piuttosto diversi in una sola funzione: 1) Ottenere un nome di file, 2) aprire o creare il file. Penso che se si separano quelli, il codice sarà più semplice e renderà molto più facile per eliminare la fonte del problema che stai vedendo.

Modifica 2: Usare un riferimento come questo funziona perfettamente senza un operator=. L'idea generale è qualcosa di simile:

int open_file(char const *name, fstream &stream) { 
    stream.open(name);
}

L'operatore di assegnazione è necessario né utile in questo caso - usiamo semplicemente il fstream esistente tramite il riferimento. Un operator= sarebbe necessario se e solo se abbiamo dovuto passare l'argomento alla ctor. Con un flusso, siamo in grado di costruire un flusso di default che non si connette a un file e quindi utilizzare aperta per la connessione al file dopo il fatto.

ifstream non supporta la semantica di copia costrutto (che ciò che il messaggio di errore in fondo sais), in modo da non può restituire un ifstream. Restituisce un ifstream *, invece, e passare al chiamante la responsabilità di cancellare il puntatore allocare.

Questo commento potrebbe non rispondere alla tua domanda, ho solo voglia di chiedere a Mr. @Corwin circa la sua risposta: Proprio come il suo codice, abbiamo: blocco getFileName per la richiesta il nome del file, penso che dovremmo codice come questo (Questo è il mio parere solo):

void getFile(/*out*/ ifstream& file){
    string filename = getFileName();
    const bool file_exist = checkFileExistence(filename);
    if (!file_exist){
       ....
    }
    ....
}

E in int main(), penso:

int main(){
    ifstream file;
    getFile(file);
    if (file.is_open()){
        //some stuff
    }
}

Con questo, è possibile ottenere filename dall'input dell'utente alla console.

Per le vie, ringraziare il signor @Corwin per il codice la sua mi aiuterà molto.

Come opzione, ifstream può essere estesa e costruttore personalizzato aggiunto alla nuova classe.

ho esteso per creare flusso di risorse di prova, incapsulando ricerca di risorse di test all'interno di esso.

// test_utils.h
class TestResourceStream : public std::ifstream {
    public:
        TestResourceStream(const char* file_path);
};
// test_utils.cpp
namespace fs = std::filesystem;
fs::path test_resource_path(const char* file_path) {
    fs::path path{std::string{"tests/resources/"} + file_path};
    if (!fs::exists(path))
        throw std::runtime_error{std::string{"path "} + 
            fs::absolute(path).c_str() + " does not exist"};
    return path;
}
TestResourceStream::TestResourceStream(const char* file_path)
    :std::ifstream{test_resource_path(file_path).c_str()} {}
// usage in test
TEST_CASE("parse") {
    std::list<GosDump::Expertise> expertises;
    TestResourceStream stream("requests/page_response.json");
    GosDump::Json::parse(expertises, stream);
    REQUIRE(10 == expertises.size());
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top