c++argvdpdk

How to manualy construct argv? (or: converting a vector of strings into a `char**`)


I need to use a function (eal_setup provided by dpdk) that as arguments accepts argc and argv, and is supposed to be called first thing in your main function, taking the argc and argv directly from the call of the application.

The app however needs to be able to accept arguments for other reasons, and some arguments for eal_setup I will never need to change from a default I set them to. This has made a need to manually construct a "fake" argv, by which I mean a char** type variable that contains whatever information I want to pass into eal_setup.

So, what I end up doing is, after processing the real argc and argv with CLI11, I construct an std::vector<std::string>, which I pass through a function supposed to turn the information from a vector of strings into a char**, before using it with eal_setup.

The function I created doesn't work. It compiles, but whenever I try to access an element of the result the same way I would on argv, I get a segmentation error.

char** construct_argv(std::vector<std::string> std_argv){
    std::vector<char*> vec_argv;
    for (std::string str_arg : std_argv){
        vec_argv.insert(vec_argv.begin(), (char*)str_arg.c_str());
    }
    return vec_argv.data();
}

How do I fix this? How can I convert an std::vector<std::string> into a char** correctly, to create a mock argv?


Solution

  • char** construct_argv(std::vector<std::string> std_argv){
    

    std_argv is a parameter to this function. Function parameters are just like local variables. When the function returns all local variables get destroyed. They cease to exist. They become no more. They start pining for the fjords. They become ex-objects.

    Your construct_argv carefully constructs a vector of pointers to each one, and returns it:

    return vec_argv.data();
    

    But, of course, you earned double the headache as your reward. vec_argv is another local object. This returns a pointer to the vector's internal data. And the vector gets destroyed immediately, because this function returns. You're left holding a completely empty bag, full of pointers to destroyed objects, that's itself destroyed.

    This entire function must be re-engineered from scratch. There are many different ways to do this correctly. One way is to pass std_argv by reference, and let the caller be responsible for not letting the original std::vector<std::string> go out of scope and get destroyed. And then return vec_argv itself and make the caller responsible for getting its data().

    (char*)str_arg.c_str()
    

    This cast is fugly. Since C++11 std::string's data() is guaranteed to be null-terminated, so this can be str_arg.data(), too.