Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function name as a parameter to main in C

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?

like image 432
sanchop22 Avatar asked Jul 26 '17 12:07

sanchop22


People also ask

Can we name a function with Main?

Note also that while the name main is a convention of sorts, you can name your entry function whatever you want, so long as you tell the linker what the entry point actually is.

Can we pass function as a parameter in C?

We cannot pass the function as an argument to another function. But we can pass the reference of a function as a parameter by using a function pointer.


4 Answers

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...
...
like image 155
Some programmer dude Avatar answered Nov 15 '22 07:11

Some programmer dude


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
like image 34
Cloud Avatar answered Nov 15 '22 09:11

Cloud


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.

like image 34
Persixty Avatar answered Nov 15 '22 07:11

Persixty


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;
}
like image 34
Yet Another John Smith Avatar answered Nov 15 '22 07:11

Yet Another John Smith