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?
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.
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.
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...
...
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.
#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");
}
./ds foo1 foo2 foo3 foo1
dlsym ./ds failed: ./ds: undefined symbol: ./ds
hello world
Mello world
Jello world
hello world
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 main
that 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.
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)
};
// 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With