Search code examples
c++parametersprogram-entry-pointoptional-parameters

Handling optional parameters in main


Suppose I have a main function which basically just calls one other function as entry point to the program. The function (and thus the full program) has a number of mandatory and a number of optional parameters:

#include <iostream>
#include <sstream>

void function_to_call(std::string arg1,
                  std::string arg2,
                  std::string arg3,
                  std::string arg4,
                  std::string arg5 = "foo",
                  std::string arg6 = "bar",
                  int num1 = 1,
                  int num2 = 2
                  )

{
  // do fancy stuff here                                                                                                                                                                                                                                                                                                
}


int main(int argc, char** argv)
{

  int num1, num2;

  std::stringstream stream;

  if( argc < 5 ) {
    std::cerr << "Usage: \n\t" << argv[0]
              << "\n\t\t1st argument"
              << "\n\t\t2nd argument"
              << "\n\t\t3rd argument"
              << "\n\t\t4th argument"
              << "\n\t\t5th argument (optional)"
              << "\n\t\t6th argument (optional)"
              << "\n\t\t7th argument (optional)"
              << "\n\t\t8th argument (optional)"
              << "\n\t\t9th argument (optional)" << std::endl;
  }
  if( argc == 5 ) {
    function_to_call( argv[1], argv[2], argv[3], argv[4] );
  }
  if( argc == 6 ) {
    function_to_call( argv[1], argv[2], argv[3], argv[4], argv[5] );
  }
  if( argc == 7 ) {
    function_to_call( argv[1], argv[2], argv[3], argv[4], argv[5], argv[6] );
  }
  if( argc == 8 ) {
    stream << argv[7];
    stream >> num1;
    function_to_call( argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], num1 );
  }
  if( argc == 9 ) {
    stream << argv[7] << ' ' << argv[8];
    stream >> num1 >> num2;
    function_to_call( argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], num1, num2 );
  }

  return 0;

}

The if chain could maybe be replaced with a switch, the command line might be tidied up a bit by using the getopt library or boost program_options, but that doesn't really change things conceptually.

Is there an obvious way I am missing to handle different numbers of parameters?


Solution

  • I realize one way of doing this might be to use vector of a (trivial) base class and then have small specializations for the different types e.g. like this (not necessarily optimal):

    class Base
    {
    public:
      virtual void set(const char *) = 0;
    };
    
    class Int : public Base {
    public:
      Int(int value) : value_(value) {}
      Int(const char* value) : value_(std::stoi(value)) {}
      virtual void set(const char* value) { value_ = std::stoi(value); }
      int get() { return value_; }
    private:
      int value_;
    };
    
    class Str : public Base {
    public:
      Str(const char* value): value_(value) {}
      virtual void set(const char* value) { value_ = value; }
      std::string get() { return value_; }
    private:
      std::string value_;
    };
    

    Then the option parsing could be done like this, i.e. have the compiler figure out which type we are dealing with

    int main(int argc, char** argv)
    {
    
      std::vector<Base*> theopts = { new Str(""),new Str(""),new Str(""),new Str(""),new Str("foo"),new Str("bar"),new Int(1),new Int(2) };
    
      if( argc < 5 ) {
         // put meaningful handling here
      }
    
      for( int i = 0; i < argc-1; ++i ) {
        theopts[i]->set(argv[i+1]);
      }
    
      function_to_call( static_cast<Str*>(theopts[0])->get(),
                        static_cast<Str*>(theopts[1])->get(),
                        static_cast<Str*>(theopts[2])->get(),
                        static_cast<Str*>(theopts[3])->get(),
                        static_cast<Str*>(theopts[4])->get(),
                        static_cast<Str*>(theopts[5])->get(),
                        static_cast<Int*>(theopts[6])->get(),
                        static_cast<Int*>(theopts[7])->get()
                        );
    }
    

    The function call then obviously is a bit ugly due to the explicit casting, but the number of explicit ifs is very small in this implementation.