dimanche 26 février 2023

Generic command-line argument parser with template, need implementation suggestion

I want to implement a C++ command line generic argument parser (also for fun). Existing popular argument parsers lack some functionalities.

  • gflags: Does not support vector very well.
  • getopt: C based, this is for without type.

I want a generic argument parser that can process arbitrary types, something like this:

class YourArgument {
   vector<int> your_ints;
   string my_str;
   bool help;
};
YourArgument your_arg; 
Argparse(your_arg, argv, argc); // A template

This Argparse can process something like "./run --help --my_str string --your_ints 1 2 3 run.txt".

I find something like C++17 structure binding can enable such implementation. If we are able to add some getters for a class, then we can implement it as follows.

class YourArgument {
   vector<int> your_ints;
   string my_str;
   bool help;
   auto& get(integral_constant<int, 0>) { return your_ints; }
   auto& get(integral_constant<int, 1>) { return my_str; }
   auto& get(integral_constant<int, 2>) { return help; }
   const char* getstr(int i) {
       if (i == 0) return "your_ints";
       if (i == 1) return "my_str";
       if (i == 2) return "help";
   }
};

template <typename T>
void Argparse_impl(T& your_arg, const char **argv, int &argc, int &c) {
    // Common recursive implementation
    // Skipped here
    Argparse_impl<i+1>(your_arg, argv, argc, c);
}

template <typename T>
void Argparse(T& your_arg, const char **argv, int &argc) {
    int c = 0;
    while (c < argc) Argparse_impl<0>(your_arg, argv, argc, c);
}

The parser implementation itself is just a normal recursive tricks, which is not the part that I need suggestion. My question is that is there any way to generate the boilerplate with macro?

I have tried this implementation, the code roughly looks like this (not the exact code I used). However, it drastically lengthen my compile performance. I have lots of such classes to compile, and the structs are very long.

#define MY_MACRO(...)\
    auto& get_tuple() { return forward_as_tuple(__VA_ARGS_); }\
    template<unsigned i> auto& get() { return get<i>(get_tuple()); }\
    const char* getstr() { static string strs = string(##__VA_ARGS_).split(); return strs[i]; }\




Aucun commentaire:

Enregistrer un commentaire