-3

I can't seem to find an answer to my question. Everything I've read on the matter seems to not quite connect and I'm starting to think what I want is impossible.

I'm working on a very very very light database management system, it's a college project and in my group I'm tasked with the main functions. Here's my problem:

We will run the project as project.exe commands1.txt commands2.txt commands3.txt for example. Now, I have to create a std::vector containing objects of the "File" class so they can be used later by my colleagues doing the parsing. This is the class code (not finished, still working on)

class File {
    protected:
        std::string name;
        std::fstream file;

    public: 
        File() {}

        File( // TO ADD REGEX
            std::string name
        ) {
            if (name != "")
                this->name = name;
            if (name != "" && !this->file) 
                this->file.open(consts::path + name);

        }       

        ~File(
        ) {
            this->name = "";
            if (this->file)
                this->file.close();
        }

        std::string getName(
        ) {
            return this->name;
        }

        void setName(
            std::string name
        ) {
            if (name != "") // TO ADD REGEX
                this->name = name;
        }

        std::fstream* getFile(
        ) {
            return &(this->file);
        }

        bool getStatus(
        ) {
            if (this->file)
                return true;
            else
                return false;
        }

    };

Also my main:

int main(
    int argc,
    char* argv[]
) {
    
    std::string current_exec_name = argv[0];
    std::vector<std::string> all_args;
    all_args.assign(argv, argv + argc);
    
    std::vector<Files::File> commands = new // ???
}

How do I create a vector with n objects of the class File, so that each one is ran with the constructor File( std::string name ), with name being the equivalent argument in argv[ ]?

Read everywhere that you can initialize them like (C++ FAQ)

class Fred {
public:
  Fred(int i, int j);      ← assume there is no default constructor
  ...
};

int main()
{
  Fred a[10] = {
    Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7),  // The 10 Fred objects are
    Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7), Fred(5,7)   // initialized using Fred(5,7)
  };
  ...
}

but I can't use this style since I don't know how many commands (.txts) will be sent.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • Sounds like a good use case for a `for` loop. Do you know how to use loops? – Silvio Mayolo Nov 09 '22 at 01:19
  • I believe having a `std::fstream` member in your class will make it uncopyable which will cause issues putting it into a `vector` without some additional work. [Non-copyable elements in vector](https://stackoverflow.com/questions/26906014/non-copyable-elements-in-vector) Usually you wouldn't check `!this->file` before calling `open`. You're in the constructor, the stream has just been initialized. Likewise you wouldn't call `close` manually either, the stream destructor will handle that. I would expect a function like `std::fstream* getFile` to return a reference and not a pointer. – Retired Ninja Nov 09 '22 at 02:09

2 Answers2

2

Simply use a loop, calling the vector's push_back() or emplace_back() method on each iteration, eg:

int main(int argc, char* argv[]) {
    
    std::string current_exec_name = argv[0];
    std::vector<std::string> all_args;
    all_args.assign(argv, argv + argc);
    
    std::vector<Files::File> commands;
    commands.reserve(all_args.size());

    for (auto &arg : all_args) {
        commands.push_back(Files::File(arg));
        // or:
        commands.emplace_back(arg);
    }
} 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you and this will be helpful too, but my problem was regarding this part in particular: commands.push_back(Files::File(arg)); . It won't run because i did not overload the = operator for my class. Someone pointed out to me a possible solution in another question (255612), and it worked. – Bl4ck Spyrit Nov 09 '22 at 21:39
1

I suppose the simplest approach is to just tell the compiler to construct your vector from a range. You could even use (almost) the same range you used to assign values to all_args. For example:

#include <iostream>
#include <string>
#include <vector>

class File {
  public:
    File(std::string name) {
        std::cout << "File: " << name << "\n"; // To confirm each construction
    }
};

int main(int argc, char* argv[])
{
    std::vector<File> commands(argv + (argc > 0 ? 1 : 0), argv + argc);
}

This says that commands is to be constructed from a range. Each element (each char*) from argv+1 to argv+argc is used to construct an element of commands. (The weird expression (argc > 0 ? 1 : 0) is to handle the case where no arguments were provided.) Since char* implicitly converts to std::string, the constructor taking a std::string is used.

I did tweak the range to exclude the first argument. I suspect you do not want to construct a File based on the name of your program (what you stored in current_exec_name), but you could always drop the "+ weird expression" if my suspicion is wrong.


Since you did some digging around, here's a bonus reference for you: the vector constructors; I used #5.

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • This worked like a charm. Thank you. In the meantime, I also found the question 255612 which solved this issue by overloading the = operator – Bl4ck Spyrit Nov 09 '22 at 21:46