Pregunta

Esta es probablemente una pregunta muy noobish para usted: ¿Cómo (si es posible) ¿Puedo devolver un ifstream desde una función

Básicamente, necesito para obtener el nombre de archivo de una base de datos por parte del usuario, y si la base de datos con ese nombre de archivo no existe, entonces necesito para crear ese archivo para el usuario. Yo sé cómo hacer eso, pero sólo pidiendo al usuario que reinicie el programa después de crear el archivo. Quería evitar ese inconveniente para el usuario, si es posible, pero la función de abajo no se compila en 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: Lo siento, se olvidó de decir dónde y cuál fue el error del compilador:

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: He cambiado la función para devolver un puntero en cambio, como sugirió Remus, y cambió la línea principal () a "base de datos ifstream = * getFile ()"; Ahora me sale este error de nuevo, pero esta vez en la línea en 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
¿Fue útil?

Solución

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
    }
    // ...
}

Otros consejos

No, no realmente. ifstream no tiene un constructor de copia, y si intenta devolver uno, que significa copiar la instancia en su función a las necesidades siempre que sea de retorno para ir.

La solución habitual es pasar de una referencia a uno, y modificar dicha referencia en su función.

Editar: mientras que le permitirá a su código para el trabajo, no va a solucionar el problema de fondo. En este momento, va a mezclar dos responsabilidades muy diferentes en una única función: 1) obtener un nombre de archivo, 2) abrir o crear ese archivo. Creo que si se separa esos, el código será más simple, y lo hacen mucho más fácil para eliminar la fuente del problema que se está viendo.

Edición 2: Usando una referencia así funciona perfectamente bien sin un operator=. La idea general es algo como:

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

no es ni necesaria ni útil El operador de asignación en este caso - simplemente usamos el fstream existente a través de la referencia. Un operator= sería necesario si y sólo si que tuvimos que pasar el argumento de la ctor. Con una corriente, podemos construir una corriente predeterminada que no se conecta a un archivo, y luego usar abierta para conectar con el archivo después del hecho.

ifstream no es compatible con la semántica copia del constructo (que lo que el mensaje de error, básicamente, EFS), por lo que no puede devolver un ifstream. Devolver un ifstream * en su lugar, y pasar a la persona que llama la responsabilidad de eliminar el puntero asignar.

Este comentario puede no responder a su pregunta, sólo quiero preguntar al Sr. @Corwin sobre su respuesta: Al igual que su código, tenemos: Bloque getFileName para solicitar el nombre de archivo, creo que deberíamos código como este (Esta es mi opinión solamente):

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

Y en int main(), pienso:

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

Con esto, usted puede conseguir filename de entrada del usuario en la consola.

Por las formas, gracias al Sr. @Corwin para el código de su ayuda mucho conmigo.

Como opción, se puede extender ifstream y constructor de encargo añade a la nueva clase.

He extendió a crear el flujo de recursos de prueba, que encapsula dentro de las operaciones de búsqueda de recursos prueba de ello.

// 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());
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top