3

I have declared the following enum :

  enum periods {one, five, ten, fifteen, thirty};

and now I want to pass it as a commandline argument in my main function.

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

  periods mp;
  if (argc == 2) {
      std::string min_prd(argv[2]);
      mp=atoi(min_prd.c_str());
 }

and the compiler complains:

error: invalid conversion from ‘int’ to ‘periods’

I did atoi() because I figured enum is an int - how do I fix my code?

Vajk Hermecz
  • 5,413
  • 2
  • 34
  • 25
user1155299
  • 877
  • 5
  • 20
  • 29
  • 3
    Note that this looks like confusion waiting to happen. `one`==0, `five`==1, `ten`==2, etc. So `static_cast(atoi("1"))`==`five`, and `static_cast(atoi("15"))` is likely nasal demons. – Managu May 15 '12 at 03:55
  • Also, if `argc`==2, then `argv[2]`==`NULL`. `argc`==2 means there are two elements in the `argv` array, namely `argv[0]` and `argv[1]`. Standard behavior is that `argv[2]` be set to `NULL`, as redundant evidence that the end of the argument vector has been reached. – Managu May 15 '12 at 04:19

4 Answers4

7

You have to explicitly cast it:

mp=static_cast<periods>(atoi(min_prd.c_str()));
Pubby
  • 51,882
  • 13
  • 139
  • 180
3

I have declared the following enum :

enum periods {one, five, ten, fifteen, thirty};

and now I want to pass it as a commandline argument in my main function.

periods mp = atoi(argv[2]);   // simplified for answer...

So, there are several issues:

  • you need to cast the int returned by atoi to the enum type... static_cast<periods>(...)
  • you should realise that an argv[2] of "0" will be mapped to the enumeration identifier "one", "1" will map to "five" etc...
    • if you actually want "1" to map to "one", "5" to "five" etc., the easiest way is to change your enum: enum periods { one = 1, five = 5, ten = 10, fifteen = 15, thirty = 30 };, but your example's obviously a little contrived so it's impossible to guess what will work best for your real needs
  • there's no validation

You're better off creating a function:

periods to_periods(const std::string& s)
{
    if (s == "one") return one;
    if (s == "five") return five;
    if (s == "ten") return ten;
    if (s == "fifteen") return fifteen;
    if (s == "thirty") return thirty;
    throw std::runtime_error("invalid conversion from text to periods");
}

(When there are more cases, it's not uncommon to use a std::map or sorted std::vector to track these associations, and it allows reusable algorithms and the same data to support conversions from enum numeric value to textual identifier.)

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
3

The straightforward solution is to use static_cast (as the other answer has already posted it):

periods mp;
if (argc == 2)
{
   std::string min_prd(argv[1]); //the index should be 1
   mp = static_cast<periods>(atoi(min_prd.c_str()));
}

But then atoi should not be used to convert the c-string into int, because atoi doesn't check for error in the input string, hence it is unsafe.

C++11 provides safer conversion functions, so you could use std::stoi as:

try
{
   periods mp;
   if (argc == 2)
   {
      //std::stoi could throw exception on error in input
      mp = static_cast<periods>(std::stoi(argv[1]));
   }
   //use mp here
}
catch(std::exception const & e)
{
   std::cout << "exception caught with message : " << e.what() << std::endl;
}

Now that is a better solution.


However, there is an alternative solution which you could use as:

period mp;
if (argc == 2)
{
   mp = to_period(argv[1]); //how should we implement it?
   if (mp == period_end)
   {
       std::cout << "command line input error" << std::endl;
       return 0;
   }
}

Now the question is, how should we implement to_period function?

Note that this solution assumes that the command line argument for enum value one would be its string representation, i.e it would be "one" instead of 1 which is integral representation.

I would implement this solution as:

  • First create a header file called period_items.h as:

    //period_items.h
    E(one)
    E(five)
    E(ten)
    E(fifteen)
    E(thirty)
    
  • then create another header file called period.h as:

    //period.h
    #include <string>
    
    enum period 
    {
       #define E(item)  item,
         #include "period_items.h"
       #undef E
       period_end
    };
    
    period to_period(std::string const & name)
    {
       #define E(item)  if(name == #item) return item;
         #include "period_items.h"
       #undef E
       return period_end;
    }
    

Now you can simply include period.h and use to_period function. :-)

Note that in the alternative solution, I've used singular form rather than plural form, means I've used period rather than periods. I feel period is appropriate.

You could also add this function to period.h as:

std::string to_string(period value)
{
    #define E(item)  if(value == item) return #item;
        #include "period_items.h"
    #undef E
    return "<error>";
}

Now, you could write this:

#include "period.h"

period v = to_period(argv[1)); //string to period
std::string s = to_string(v);  //period to string

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
2

At a random guess, what you really want is something more like:

periods mp;
if (argc < 2) {
    mp=one; // default value
} else if (strcmp(argv[1], "one")==0) {
    mp=one;
} else if (strcmp(argv[1], "five")==0) {
    mp=five;
} else if (strcmp(argv[1], "ten")==0) {
    mp=ten;
} else if (strcmp(argv[1], "fifteen")==0) {
    mp=fifteen;
} else if (strcmp(argv[1], "thirty")==0) {
    mp=thirty;
} else {
    fprintf(stderr, "I can't possibly comprehend periods = '%s'\n", argv[1]);
    return -1;
}
Managu
  • 8,849
  • 2
  • 30
  • 36