Thread: What's the best way to handle many program options?

  1. #1
    Registered User
    Join Date
    Jun 2007
    Posts
    8

    Exclamation What's the best way to handle many program options?

    I'm in my second year at uni, doing a Computer Science degree, and am now starting to write non-trivial programs. I'm learning lots of intermediate level stuff, but one thing I'm stumped on is handling many concurrent user options.

    I'm writing a program at the moment that takes multiple (say 20) user options, and uses them in combination to process the input. I've read the options using getopt_long into structures of related options, and validated them. Now I need to figure out the best way to use the options.

    A massive function full of nested if statements doesn't sound good to me. Nor does having a different function for each combination of options :P Is a combination of both of those approaches sensible? Is there a good way to do this, or will I just cop it because I've got so many program options?

    I want to avoid messy code, but I also need this to be quick to run, as it will be used a lot, so I can't afford to go through 20 if statements on each iteration.

    Thanks for the help Any input at all is appreciated.

  2. #2
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    switch statements ftw!

    In reality, it depends upon what form the data is represented in, and how many of those different conditions can be used together to result in similar output.... if that makes sense.

  3. #3
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    Haha, I forgot to mention several of the options are strings.

    The majority of the options are true/false, and they can all be true. They will be used to operate on the input separately, then in all combinations.

    For example,

    Code:
    typedef struct{
        int add;
        int sub;
        int mul;
        int div;
        int addval;
        int subval;
        int mulval;
        int divval;
    } math_opts;
    
    math_opts m_opts;
    
    m_opts = {0, 0, 0, 0, 10, 15, 3, 7};
    When the user runs the program, if they supply any of the first four options, they will be applied to the input. If they supply arguments to those four options, they will replace the default value.

    ie:
    Code:
    ./program --add 3 --mul
    > 17
    20 /* added 3 */
    51 /* multiplied by 3, the default value */
    60 /* did both */
    >

  4. #4
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Where you can, with integers or chars, I would use switch statements.

    This example of yours doesn't look that difficult to write cleanly. Sounds like you already have a game plan for whatever you're actually writing.

  5. #5
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    Thanks for the encouragement MacGyver

    I'm afraid of something horrible like this:

    Code:
    /* Not actual code! */
    int process_input(options)
    
    switch (add)
    {
      case 1:
      {
        /* do addition */
        switch (sub)
        {
            case 1:
            {
                /* do subtraction */
                /* do subtraction and addition */
                switch (mul)
                {
                     case 1:
                     {
                          /* do multiplication */
                          /* do mul & add */
                          /* do mul & sub */
                          /* do mul & sub & add */
                          switch (div)
                          {
                               case 1:
                               {
                                    /* do div */
                                    /* do div & mul */
                                    /* div & sub /*
                                    /* div & add /*
                                    /* div & mul & sub /*
                                    /* div & mul & add /*
                                    /* div & add & sub /*
                                    /* div & mul & sub & add /*
                                    if (strcmp(somestringoption, "option1") == 1)
                                    {
                                          /* Every combination of the above with "option1" */
                                     }
                                    else if (strcmp(somestringoption, "option2") == 1)
                                    ...
                                    BREAK;
                                }
    ....
    ouch!

  6. #6
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Perhaps I'm totally missing this, but can't you just order the operations in an array or a linked list?

    So for example, let's assume the program is executed like this as you wrote in your previous example:

    Code:
    ./program --add 3 --mul
    Right away you know that you will have to add and multiple. If you create an array or a linked list of operations, you can perform the operations somewhat easily.

    Tracing this for a second, take the next user input, which according to your example, was 17. First operation is to add 3 (supplied value) to 17. Second is to multiply 3 (default value) to 17. After you're finished, apparently you output the result of all operations at once, which just involves exactly what you're doing; going through the entire array or linked list.

    Hopefully this make sense. I'm writing it somewhat quickly, so I hope this is helpful and appropriate for your situation.

  7. #7
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Excuse me, but the way you are organizing your swich code leads me to believe that you expect to make a case for every number there is. Rather than doing that, maybe you could store an operation in a variable, and make cases for each operation you support.

    Or better yet, implement some sort of notation, like Reverse Polish Notation. You could toy with a more complicated expression parser, but I think you'll be set with something simple.

  8. #8
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    Quote Originally Posted by citizen View Post
    Excuse me, but the way you are organizing your swich code leads me to believe that you expect to make a case for every number there is.
    I'm not quite sure what you mean. Most of the variables have two cases, 0 or 1, to indicate FALSE (not given as an option) and TRUE (given as an option) respectively.

    If A is true, and B is true, and C is true, then I want to do the compute the following transformations on the input data, i:
    A(i)
    B(i)
    C(i)
    A(B(i))
    A(C(i))
    B(C(i))
    A(B(C(i))

    The order of transformations does not matter, so A(B(i)) == B(A(i))

    MacGyver - Are you suggesting precomputing a list of possible combinations of operations, then iterating through that list?

    You've made me think of another possibility:

    ie Give each operation a numeric value
    A = 1
    B = 2
    C = 4

    Then sum the values given to create a single unique set of transformations to apply:

    Code:
    switch(combination)
    {
        case 1:
        /* only A was given */
        case 2: 
        /* only B was given */
        case 3:
        /* A and B were given */
        etc...
    }
    What do you think?
    Last edited by stickmangumby; 06-04-2007 at 08:39 PM.

  9. #9
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Your last idea will work, but you have to repeat code.

    A list of operations in your sample program would be simplistic. You submitted add and multiply in the command line arguments. Build it into a list.... pseudo-pseudocode:

    Code:
    list = {add, multiply}
    
    var single = 0
    
    foreach item of list
    	single = do_operation(item);
    	print single
    end foreach
    The do_operation() function could be a function containing one switch statement that does only one operation, based upon the current operation. TBH, you don't even need a real function. You could just stick your switch statement right there, and compute the value of one operation and put that in single.

    The real work would be on building the actual list of operations. Then all you would have to do would be to cycle through them all in a loop. You could create another variable named final that should contain the value of all of the operations combined as you go through this loop. That way you just have to iterate through the loop once and print final when finished.

  10. #10
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    The only problem in your code MacGyver is that the program also needs to handle combinations of inputs.

    Code:
    list = {add, multiply, add & multiply}
    Although, storing the result of 'add' for reuse in 'add & multiply' and simply applying multiply to it sounds like a plan. What do you think?

    Some kind of structure of operations to apply, and previous results, could be great.

    Code:
    typedef struct {
        int op;
        int val;
    } operation;
    
    typedef struct {
        operation *ops;
        int totops;
    } operation_list;
    
    /* Fill operation_list with relevant info */
    
    for (i = 0; i < totops; i++)
    {
        /* Do operation_list->ops[i].op on operation_list->ops[i].val */
        /* Store the result in relevant operation_list->ops.val */
    }
    Does this make sense? I feel like it's a step in the right direction, but I need to think it through more.

  11. #11
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    I think you're confusing this way too much, but at the same time, I don't think I understand what you envision for the final product.

    I'm under the impression that you give the program a list of operations limited to add, multiply, subtract, and divide, and default numbers to use for said operations. The program then cycles through in a loop, asking the user for input. For each number given, the program computes the results of all operations on said number, one operation at a time, using the default numbers provided. After all operations on that number are finished, the program prints the result as if all operations were performed on that number at once, in order.

    If what I stated is correct, then you don't need to find a way to specify that two or more operations must be performed. You simply walk through the list and compute all of the operations together.

  12. #12
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    The operations above were simply for illustrative purpose.

    I think what you've missed is the fact that the program computes the results of all operations, and all combinations of operations, on the input.

    Code:
    ./program --add 3 --mul
    > 17
    20 /* added 3 */
    51 /* multiplied by 3, the default value */
    60 /* did both -> took the input (17), add 3, then multiply by 3 */
    Does that clarify what you're asking?

  13. #13
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    I didn't think I missed it. I see three calculations in your sample:

    Code:
    17 + 3
    17 * 3
    (17 + 3) * 3
    I described what I perceived the last line to be as such:

    Quote Originally Posted by MacGyver View Post
    After all operations on that number are finished, the program prints the result as if all operations were performed on that number at once, in order.
    Regardless, your method will probably work just fine. It would involve more from you, however.

  14. #14
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You still don't need to use gigantic switch statements to handle that sort of thing.

    Think of it this way. You have two operations you can do to the number: add 3 or multiply by 3. You either perform these options or you don't. So it's sort of like using binary numbers, where 0s mean you don't perform the operation and 1s mean you do. In this case, the possible operations are 00 (ignored), 01, 10, and 11. Do you see? With three operations, you'd have 000, 001, 010, 011, 100, 101, 110, and 111.

    I think that this code needs to be implemented in two parts. You have the code which actually adds or multiplies by three -- this could probably be a switch statement -- and you have code which calls this other code for every possible combination.

    Here's what I'm envisioning:
    Code:
    int perform_operation(int x, int y, const char *operation) {
        if(!strcmp(operation, "--add")) return x + y;
        /* ... */
        return x;
    }
    
    for each combination(arguments) {
        perform_operation(number, argument);
    }
    Also, this actually looks like it might work quite well if you implemented it recursively. It might be difficult otherwise.
    Code:
    void do_operation(int x, int value, char *argv[]) {
        if(argv[1]) do_operation(x, value, argv+1);
        x = perform_operation(x, value, *argv);
        print_possible_value(x);
        if(argv[1]) do_operation(x, value, argv+1);
    }
    Do you see what I'm saying? I hope it's legible

    [edit] Also see this Wikipedia entry: http://en.wikipedia.org/wiki/Permutation [/edit]
    Last edited by dwks; 06-05-2007 at 12:16 AM.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  15. #15
    Registered User
    Join Date
    Jun 2007
    Posts
    8
    Haha, we're going in circles

    I'll add a third option to the example to make the combinations explicit.

    Code:
    ./program --add 3 --mul --sub 1
    > 17
    20 /* add */
    51 /* mul */
    18 /* sub */
    60 /* add & mul */
    19 /* add & sub */
    48 /* sub  & mul*/
    57 /* add & sub & mul */
    Every combination of options is executed. In the example I am giving (with the options add, sub, mul, and div) the ordering is important. However, in the actual application the ordering will not be important.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Program optimization
    By a.mlw.walker in forum C Programming
    Replies: 1
    Last Post: 03-27-2009, 06:12 AM
  2. Direct3D problem
    By cboard_member in forum Game Programming
    Replies: 10
    Last Post: 04-09-2006, 03:36 AM
  3. Encryption program
    By zeiffelz in forum C Programming
    Replies: 1
    Last Post: 06-15-2005, 03:39 AM
  4. Date program starts DOS's date
    By jrahhali in forum C++ Programming
    Replies: 1
    Last Post: 11-24-2003, 05:23 PM
  5. fopen();
    By GanglyLamb in forum C Programming
    Replies: 8
    Last Post: 11-03-2002, 12:39 PM