6

I have a main function as follows:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i, sum = 0;
    char *func_user = argv[1];

    // execute func_user function

    return 0;
}

void foo1(void)
{
    printf("I'm foo1.");
}

void foo2(void)
{
    printf("I'm foo2.");
}

void foo3(void)
{
    printf("I'm foo3.");
}

I want user to give his function name as an argument to main and I want my program to execute this given function. Is there any way to do this (something like using reflection) without using switch/case like methods?

sanchop22
  • 2,509
  • 10
  • 40
  • 59
  • 10
    Short answer: no. Long answer, noooooo!!! – paxdiablo Jul 26 '17 at 12:38
  • 1
    What are you going to do if user provides `bar` ? It seems lika a [XY Problem](https://meta.stackexchange.com/a/66378/184024). Is `map` also considered as switch/case method (because in fact it is, but...)? – Betlista Jul 26 '17 at 12:39
  • 1
    Sure there is. read the string argument representing `foo1`, or `foo2`. Then with a series of `if else` determine which string was passed, and in that branch, call the function `foo1()`, or `f002()`, or... – ryyker Jul 26 '17 at 12:41
  • It will be necessary to map a string to a function pointer somehow. There is no way in C to achieve that without writing code to do the mapping. And it will be necessary to cope with supplied strings that don't correspond to any function - since users can't be prevented from supplying arbitrary strings. – Peter Jul 26 '17 at 12:42
  • 2
    @ryyker but this is switch/case method in broader meaning, isn't it? – Betlista Jul 26 '17 at 12:42
  • @Betlister - depends what you mean by "broader meaning". It is possible to do anything with a set of `if/else` constructs that can be done with a `switch/case`. The reverse is not true (not all `if/else` constructs can be readily mapped to a `switch/case`). – Peter Jul 26 '17 at 12:44
  • @Betlista - Yes, I noticed the restrictions in the very last statement after posting the comment. But string compares in a branch of some kind is the only way to do this in C that I am aware. – ryyker Jul 26 '17 at 12:45
  • 2
    @paxdiablo Short answer: **yes**. Long answer: Whyyyyyyyyy? – Cloud Jul 26 '17 at 12:55
  • 1
    well, compute a hash key of your function name and store pointers to your functions in an array indexed by this key, then call `funcarray[hash(argv[1])]();` and declare any invalid user input **undefined behavior** -- problem solved ;) –  Jul 26 '17 at 13:08
  • @FelixPalmen or fill other entries with NULL and check before calling :) – Ajay Brahmakshatriya Jul 26 '17 at 14:54
  • never access beyond `argv[0]` with out first checking `argc` to assure the command line parameter(s) actually exist. – user3629249 Jul 26 '17 at 23:34
  • Similar question: [SO: How to compare my string, which is stored in an array, to function names from a complete library in c](https://stackoverflow.com/a/43006107/7478597) – Scheff's Cat Jul 27 '17 at 09:39

4 Answers4

14

You can't do it directly, because C neither have introspection nor reflection. You have to map a name (a string) to a (pointer to a) function yourself.

One not to uncommon way to create such a mapping is to have a structure with that information, and then use an array of those structure for all functions. Then you iterate over the array to find the name and its function pointer.

Perhaps something like

struct name_function_map_struct
{
    char *name;             // Name of the function
    void (*function)(void); // Pointer to the function
};

// Declare function prototypes
void foo1(void);
void foo2(void);
void foo3(void);

// Array mapping names to functions
const struct name_function_map_struct name_function_map[] = {
    { "foo1", &foo1 },
    { "foo2", &foo2 },
    { "foo3", &foo3 }
};

int main(int argc, char *argv[])
{
    // Some error checking
    if (argc < 2)
    {
        // Missing argument
        return 1;
    }

    // Calculate the number of elements in the array
    const size_t number_functions = sizeof name_function_map / sizeof name_function_map[0]

    // Find the function pointer
    for (size_t i = 0; i < number_functions; ++i)
    {
        if (strcmp(argv[1], name_function_map[i].name) == 0)
        {
            // Found the function, call it
            name_function_map[i].function();

            // No need to search any more, unless there are duplicates?
            break;
        }
    }
}

// The definitions (implementations) of the functions, as before...
...
Some programmer dude
  • 363,249
  • 31
  • 351
  • 550
5

Yes, but you don't likely want to do this for the reason you're aiming for (i.e. avoiding a switch statement). From another SO answer (compile via gcc -std=c99 -Wall -rdynamic ds.c -o ds -ldl, assuming the filename is ds.c): (Keep in mind that you'll need to know the function signature, ie: return type plus arguments; in advance).

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void hello ()
{
    printf ("hello world\n");
}

int main (int argc, char **argv)
{
    char *buf = "hello";
    void *hndl = dlopen (NULL, RTLD_LAZY);
    if (!hndl) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
        exit (EXIT_FAILURE); };
    void (*fptr) (void) = dlsym (hndl, buf);
    if (fptr != NULL)
        fptr ();
    else
        fprintf(stderr, "dlsym %s failed: %s\n", buf, dlerror());
    dlclose (hndl);
}    

You're best off just creating a function dedicated to providing the function/string mapping. The example I've provided is just for proof that something like what you've asked for can be done. Just because you can, doesn't mean you should.

Modified Example

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void foo1(void);
void foo2(void);
void foo3(void);

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

    /* dlopen() self. */
    void *hndl = dlopen (NULL, RTLD_LAZY);
    if (!hndl) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
        exit (EXIT_FAILURE); };

    /* Attempt to find symbols and call them. */
    for (int i = 0; i < argc; i++) {
        void (*fptr) (void) = dlsym(hndl, argv[i]);
        if (fptr != NULL)
            fptr();
        else
            fprintf(stderr, "dlsym %s failed: %s\n", argv[i], dlerror());
    }

    /* Cleanup. */
    dlclose (hndl);

    return 0;
}

void foo1()
{
    printf("hello world\n");
}

void foo2()
{
    printf("Mello world\n");
}

void foo3()
{
    printf("Jello world\n");
}

Sample Run

./ds foo1 foo2 foo3 foo1
dlsym ./ds failed: ./ds: undefined symbol: ./ds
hello world
Mello world
Jello world
hello world
Cloud
  • 17,212
  • 12
  • 64
  • 137
2

C doesn't work like that. It (normally) compiles to machine code and unless it incorporates debug information the executable doesn't retain the identifiers (function names, variable names and type names) present in the source file.

Even if that debug information is there, there is no standard way of accessing that information during execution.

Your only option is something like:

if(strcmp(func_user,"foo1)==0){
    foo1();
}else if (strcmp(func_user,"foo2)==0){
    foo2();
}/* and so on... */

[or of course arrays and/or data structures that amounts to this...]

Assuming a traditional compile and link process, where you call foo1() what will go into the object code is instructions that effectively say "call foo1". So far so good. But when the linker comes along it will turn "call foo1" into a jump (typically assembler jmp) to an address that the linker has determined is the entry point of foo1() and (unless you include debug symbols as mentioned) all reference to the identifier foo1 will be removed. Execution doesn't need to know what you called the function it only needs to know the address of its entry point and that is all that (normally) appears in the executable.

Footnote: Before anyone mentions it C99 does define a symbol __func__ that is the same as the function being compiled. In main() it will be mainthat can be used to emit meaningful error messages but can't be used in any way to call that function or somehow register it to some look-up table.

Persixty
  • 6,693
  • 2
  • 10
  • 31
1

In addition to @Some programmer dude answer:

Instead of writing function's name twice you can use preprocessor (stringify):

#define FUNC_MAP_ENTRY(name) { #name, &name}

const struct name_function_map_struct name_function_map[] = {
    FUNC_MAP_ENTRY(foo1),
    FUNC_MAP_ENTRY(foo2),
    FUNC_MAP_ENTRY(foo3)
};


By the way, if you use C++ or you have some dynamic array in pure C (it's not hard to implement) you could make dynamic table this is working example how to implement dynamic function table:
// MIT license
// Copyright 2017 "Yet Another John Smith"
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
// THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// ================================
//   Dynamic function names table
// ================================

struct FuncNamesTableEntry
{
    const char* name;
    // You could make it more flexibile (could store any function type)
    // say use (void*), but user have to cast it by hand then
    void(*func_ptr)(void);
};

struct FuncNamesTable
{
    struct FuncNamesTableEntry* entries;
    size_t size;
    size_t capacity;
};


void func_table_init(struct FuncNamesTable * tbl)
{
    tbl->entries = NULL;
    tbl->size = 0;
    tbl->capacity = 0;
}


#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)


void func_table_add_entry(
    struct FuncNamesTable * tbl,
    struct FuncNamesTableEntry entry)
{
    if (tbl->size >= tbl->capacity) {
        struct FuncNamesTableEntry* new_mem;
        size_t new_capacity = tbl->capacity * 1.5 + 1;

        new_mem = realloc(tbl->entries, new_capacity * sizeof(struct FuncNamesTableEntry));
        if (new_mem == NULL) {
            printf("%s\n", "Not enough memory");
            exit(1);
        }

        tbl->entries = new_mem;
        tbl->capacity = new_capacity;
    }

    tbl->entries[tbl->size] = entry;
    tbl->size += 1;
}


struct FuncNamesTable _the_func_table;
// I need this hack to make ADD macro
struct FuncNamesTableEntry _insterting_entry;

#define FUNC_TABLE_INIT() func_table_init(&_the_func_table);
//#define FUNC_TABLE_ENTRY(f_name) struct FuncNamesTableEntry{#f_name, &f_name}
#define FUNC_TABLE_ADD_ENTRY(f_name) do{                        \
    _insterting_entry.name = #f_name;                           \
    _insterting_entry.func_ptr = &f_name;                       \
    func_table_add_entry(&_the_func_table, _insterting_entry);  \
}while(0);

#define FUNC_TABLE_ITERATE(_i)                                     \
    for (struct FuncNamesTableEntry* _i = _the_func_table.entries; \
         _i - _the_func_table.entries < _the_func_table.size;      \
         ++_i)


void foo1(void){}
void foo2(void){}
void foo3(void){}
void foo4(void){}
void foo5(void){}
void foo6(void){}

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

    FUNC_TABLE_ADD_ENTRY(foo1);
    FUNC_TABLE_ADD_ENTRY(foo2);
    FUNC_TABLE_ADD_ENTRY(foo3);
    FUNC_TABLE_ADD_ENTRY(foo4);

    if (1) {
        FUNC_TABLE_ADD_ENTRY(foo5);
    }
    else {
        FUNC_TABLE_ADD_ENTRY(foo6);
    }

    FUNC_TABLE_ITERATE(i)
    {
        printf("Name: '%s'; pointer: %p\n", i->name, i->func_ptr);
    }

    return 0;
}