3

I have to pass system configurations details inside a function in c++ by making use of command line parameters (argv, argc). the function looks as follows:

  function(char * array[]){
    windows_details = array[1];
    graphic_card = array[2];
    ram_detail = array[3];
    procesor_detail = array[4];
  }
 int main(int argc, char *argv[]){
   char *array[] = { argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]};
   function(array);
 }

So, when i execute the program exe as follows i get the right output:

 sample.exe windows_32bit nividia 5Gb i7processor

But my concern is every time values has to be in specific order i.e the user have to take care that "windows_details" will be the first comand line parameter then "graphic_card" and like wise the ram_details and processor_details ,so this solution is not robust i.e if the sequence of values are interchanged the result will not be right. I want the solution to be sequence independent,whatever be the sequence the values to be substituted at right place also the comand line arguments to be passed as values to options. e.g as follows:

sample.exe --configuration i7_processor 5GB windows_32bit nividia or
sample.exe --configuration 5GB i7_processor nividia windows_32bit or
sample.exe --configuration nividia windows_32bit 5GB i7_processor
.
.

So, like above there is option as "--configuration" and then the details in any sequence. I tried to add option part as follows but its not working:

int main(int argc, char *argv[]){
   char *array[] = { argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]};
   std::string arg = *(reinterpret_cast<std::string*>(&array));
   if (arg == "--configuration"){
    function(array);
    }
    else {
    std::cout << "Usage " << argv[0] << "\t--config\t\t Specify the configuration of target" << std::endl;
    return 1;
    }
   return 0;       
 }

So, please help me out in solving my problem. What shall i do to make it more robust along with addition of options?

Learner
  • 453
  • 13
  • 29

4 Answers4

5

You should use getopt or Boost.ProgramOptions. *(reinterpret_cast<std::string*>(&array)) is quite silly and doesn't do what you think it does.

There are plenty of tutorials on how to use both. Here's an example from the getopt manual and a link to the Boost.ProgramOptions documentation

A simple BPO example would look like this:

    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("arch", po::value< string >(),
              "Windows version")
        ("card", po::value< string >(),
              "Graphics card")
        ("ram", po::value< string >(),
              "Amount of RAM")
        ("cpu", po::value< string >(),
              "Type of processor")
    ;

    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).
              options(desc).run(), vm);
    po::notify(vm);

    if (vm.count("help")) {
        cout << "Usage: options_description [options]\n";
        cout << desc;
        return 0;
    }

If you want, you can include support for positional options, but I'm not entirely clear on how you intend to distinguish what the various options are without explicit switches.

user6335390
  • 128
  • 3
2

Boost.Program_options should be the best choice to achieve this, however if you don't want boost and okay to change your function call to accept a map you could have a pretty easily maintainable and readable code something like following:


void function( std::unordered_map<std::string, std::string>& arrayMap )
{
    windows_details = arrayMap ["--configuration"];
    graphic_card =    arrayMap ["--graphic_card"];
    ram_detail =      arrayMap ["--ram_detail"];
    procesor_detail = arrayMap ["--procesor_detail"];
}

Function to extract a parameter using std::find

char* extractOption(char** startItr, char** endItr, 
                     const std::string& searchParam)
{
    char ** itr = std::find(startItr, endItr, searchParam);
    if (itr != endItr && ++itr != endItr)
    {
        return *itr;
    }
    return 0;
}

Then you can use the extract function to fill the map something like following, ( pseudo code only ) :

int main(int argc, char * argv[])
{
    std::unordered_map<std::string, std::string> optionMap;

    std::string options[] = { "--configuration", 
                              "--processor_details" , 
                              "--graphic_card" };

    for(const auto& opts: options)
    {
       char * value= extractOption(argv, argv + argc, opts );

       if (value)
       {
           optionMap[opts] = value ;
       }
       else
       {
           std::cout << "Not found : " << opts << '\n';
       }
     }

    function( optionMap ) ;
    return 0;
}

See Here

P0W
  • 46,614
  • 9
  • 72
  • 119
  • i did as you suggested but its giving compile error as 'i' is not initialized(int the main). It seems that 'i' need to be incremented too. so, can you throw some light upon the for loop in the main section. – Learner May 15 '16 at 07:29
  • in main for loop i replaced options[i] with opts and then i did as : sample.exe --configuration windows_32 --processor_details i7 --graphic_card nividia and getting output as : windows_details = 3084545545878, Grphic_card = 89546646..... So, the output seemd its taking the address not the value. I am not getting what to do to have values. – Learner May 15 '16 at 07:59
  • @Learner updated the post, added a online link to demo – P0W May 15 '16 at 20:39
1

Another easy way, with no third party library, would be using specific flags as (-p , -s , -c...) that correspond to the argument description or role.

Then, in your code, you'll have something like the following :

if (i + 1 != argc) // Check that we haven't finished parsing already
    if (argv[i] == "-f") {
        // We know the next argument *should* be the filename:
        myFile = argv[i + 1];
    } else if (argv[i] == "-p") {
        myPath = argv[i + 1];
    } else if (argv[i] == "-o") {
        myOutPath = argv[i + 1];
    } else {
        std::cout << "Not enough or invalid arguments, please try again.\n";
        Sleep(2000);
                 /*
                  *  Sleep for 2 seconds to allow user (if any) to read above statement. 
                  *  The issue with this is that if we're a backend program to a GUI as mentioned above; 
                  *  that program would also sleep for 2 seconds. Most programs don't
                  *  have this - the console will keep the text there until it scrolls off the page or whatever, so you may aswell leave it out.
                  ***/
        exit(0);
    }
Vtik
  • 3,073
  • 2
  • 23
  • 38
0

regex springs to mind if you want a simple way for the program to appear "intelligent".

Here's a simple start which you can flesh out with more options and error checking as you see fit.

#include <iostream>
#include <regex>
#include <string>
#include <vector>
#include <algorithm>
#include <cstring>

/*
 sample.exe --configuration i7_processor 5GB windows_32bit nividia
 sample.exe --configuration 5GB i7_processor nividia windows_32bit
 sample.exe --configuration nividia windows_32bit 5GB i7_processor
 */

template<class Iter>
std::pair<Iter, Iter> find_config(Iter first, Iter last)
{
    auto begin = std::find(first, last, std::string("--configuration"));
    if (begin == last)
    {
        return { last, last };
    }
    begin = std::next(begin);
    auto end = std::find_if(begin, last, [](auto& opt) {
        return opt.substr(0, 2) == "--";
    });
    return { begin, end };
}

struct configuration
{
    std::string processor = "not set";
    unsigned long long memory = 0;
    std::string os = "not set";
    std::string graphics = "not set";
};

std::ostream& operator<<(std::ostream& os, const configuration& conf)
{
    os <<   "processor = " << conf.processor;
    os << "\nmemory    = " << conf.memory;
    os << "\nos        = " << conf.os;
    os << "\ngraphics  = " << conf.graphics;
    return os;
}

const std::regex re_processor("(.*)_processor", std::regex::icase);
const std::regex re_memory("(\\d+)(mb|gb)", std::regex::icase);

template<class Iter>
auto parse_config(Iter first, Iter last) -> configuration
{
    configuration result;

    for ( ; first != last ; ++first)
    {
        auto& option = *first;
        std::smatch match;
        if (std::regex_match(option, match, re_processor))
        {
            result.processor = match[1].str();
            continue;
        }

        if (std::regex_match(option, match, re_memory))
        {
            unsigned long long num = std::stoi(match[1].str());
            auto mult = match[2].str();
            std::transform(mult.begin(), mult.end(), 
                           mult.begin(), 
                           [](auto& c) { return std::toupper(c); });
            if (mult == "GB") {
                num *= 1024 * 1024 * 1024;
            }
            else {
                num *= 1024 * 1024;
            }
            result.memory = num;
            continue;
        }

    }

    return result;
}

int main(int argc, const char* const * argv)
{
    std::vector<std::string> args(argv, argv + argc);
    const auto& progname = &args[0];

    auto config_range = find_config(std::next(std::begin(args)), std::end(args));
    auto configuration = parse_config(config_range.first, config_range.second);

    std::cout << configuration << std::endl;

}

Example run:

$ sample --configuration i7_processor 5GB windows_32bit nividia
processor = i7
memory    = 5368709120
os        = not set
graphics  = not set
$ 
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142