2

I'm building a set of functions in an included file, and i would like to be able to run my test main() to call the function name passed on the command line. Something like this:

void rotate (int degrees) {
    /*...*/
}

void translate (int pixels) {
    /*...*/
}

int main(int argc, char* argv[] ) {
    const int degs = 100;
    const int pix = 250;

    string func = argv[1];
    //call func(degs) or func(pix) based on the command line argument passed
}

I'd like to be able to run this like % a.out translatefrom the command line. What is the syntax to make this work. Thanks in advance I looked for a while on this one and couldn't find anything.

8
  • Am I permitted to ask why you do not use Boost unit testing framework instead? You are obviously using c++ anyways :) Commented Apr 8, 2014 at 6:48
  • 1
    You'll need to compare the input string with the function name and call if they match. There's no way to call the function via the string directly. Commented Apr 8, 2014 at 6:49
  • This ^. A function menu is your best chance if you want to do this. Just so you know this is called "Reflection" and it's mostly available on scripting languages. Commented Apr 8, 2014 at 6:51
  • @πάντα ῥεῖ I've tried if string(argv[1])=="rotate") { ... I can make this work. I was wondering if there is a more direct way. Commented Apr 8, 2014 at 6:53
  • You could always use a map with function pointers like Mats proposed in his answer. Commented Apr 8, 2014 at 6:54

3 Answers 3

6

It is not possible to do this in C or C++, because the name of the function "disappears" when you compile the code.

The common way to solve this is to use either a std::map<std::string, FunctionPtr> or some other form of table that contains a translation between string and function pointer.

Of course a very crude version is to simply do:

if (argv[1] == std::string("translate"))
{
    int x = atoi(argv[2]);
    translate(x);
}

(You need the cast to std::string or you need to use strcmp, since using == between a char * and a string literal will just compare the address of the string, and argv isn't going to contain addresses of string literals under any reasonable circumstances)

Function pointers in std::map could be something like this

typedef void (*FuncType)(int v);

std::map<std::string, FuncType> m;

m["translate"] = translate;
m["rotate"]    = rotate;

// Convert argv[2] to integer:
int x = argv[2];
// Call function indicated by argv[1]:
m[argv[1]](x);
Sign up to request clarification or add additional context in comments.

3 Comments

Mats, thank you for posting. I've got this sort of if statement working now. Can you give an example of how to use the map to function pointers?
Edited my answer to cover that part.
@MatsPetersson thanks for coming back and adding to this answer. I get it now.
3

As you requested, here is an example on how you could use std::map to achieve what you want. I am using void functions with void parameters for the sake of simplicity. In your case, you will have to use void (*)(int) for the map in order to use your functions since they take ints as parameters. You will also have to find how to pass the ints themselves. You can probably do this by taking the argv arguments by pairs.

#include <iostream>
#include <map>
#include <string>

void func1() {
    std::cout << "func1" << std::endl;
}

void func2() {
    std::cout << "func2" << std::endl;
}

void func3() {
    std::cout << "func3" << std::endl;
}

std::map<std::string,void (*)(void)> tmap = {
    {"func1",&func1},
    {"func2",&func2},
    {"func3",&func3}                        
};

int main(int argc, char* argv[]) {
    if (argc > 1) {
        for (int i =1; i != argc; ++i) {
            if (tmap.find(argv[i]) != tmap.end()) {
                tmap[argv[i]]();
            }
            else {
                std::cout << "There is no " << argv[i] << " function available." << std::endl;
             }
         }
    }
    else {
        std::cout << "No functions were choosen." << std::endl;
    }
    return 0;
}

1 Comment

Well, I'm glad I could help.
2

As requested...

#include <iostream>
#include <string>
#include <map>

typedef void FUNC(int); 

FUNC rotate, translate;

int main(int argc, char *argv[])
{
    std::map<std::string, FUNC *> m;
    m["rotate"] = rotate;
    m["translate"] = translate;   

    if ( argc > 1 )
    {
        FUNC *ptr = m[argv[1]];
        if ( ptr )
            ptr(100);
    }
}

In C++11 you can use brace-enclosed initializers for m.

If the duplication bothers you, you could even go:

#define M_ROW(funcname) m[#funcname] = funcname

3 Comments

Should argv really be an array of char** ?
fixed, thanks . Kinda weird that I didn't get a compilation error for that
@MattMcNabb appreciate the macro hint to reduce the duplication

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.