How to call a function with a variable in C ++, here is an example

char* funcName = "somefunction"; char* params[] = {"param1", "param2", "param3"}; callFunction(funcName, params); 

So, how can you implement a call function by calling it so that it causes other call functions, how ridiculous it would be to say.

I would be grateful for any help!

  • Well, so you called it :) The question is somehow clearer? - Harry
  • I gave an example, the "callFunction" function, should somehow call that function, which is in the "funcName" variable. I meant it, but I don’t know how to do it - HackMemory
  • 6
    In funcName , you have a string, not a function. There is no reflection in C ++, directly - by no means, indirectly - create, for example, an array of structures with a name and address, look for an address by name and call ... - Harry
  • 2
    if you make it so that functions are functions exported (with an additional dll, although it seems you can export from the binary itself), then everything becomes much simpler :) - KoVadim
  • one
    If from a DLL, then this can be done through GetProcAddress , but this is an application of the API, and not the language itself :) The question should have been asked more precisely ... There are other ways in Linux, but this is not for me, I am not aware of it. And once again, so that you are aware - this is not a matter of language , but of the API of the corresponding OS. - Harry

3 answers 3

It turned out pretty clumsy, but it works.

In the proposed method, the following conditions must be met:

  • Functions must "lie" in dynamic libraries;
  • Functions must be of type void * (void *).

An example source file with functions:

 #include <tuple> #include <iostream> extern "C" void* func0(void *args) { std::tuple<int, int> *t = (std::tuple<int, int> *)args; int result = std::get<0>(*t) + std::get<1>(*t); return std::move((void *)&(result)); } extern "C" void* func1(void *args) { std::tuple<std::string, std::string> *t = (std::tuple<std::string, std::string> *)args; std::cout << std::get<0>(*t) << ' ' << std::get<1>(*t) << std::endl; return nullptr; } 

Compile:

 g++ -fPIC -shared functions.cpp -o libfunctions.so 

To get a list of function names (characters) and save their pointer, we use the capabilities of the libdlfcn and libbfd libraries.

Store the matches "Function Name -> Pointer" will be in the following std :: map :

 std::map<std::string, std::function<void*(void*)> > functions; 

The function call performs the following function. Input parameters: The name of the function, a set of parameters separated by commas. The return value is a void pointer to what the function should return.

 template<typename ... Args> void* callFunction(const std::string &funcName, Args... args) { std::tuple<Args...> t = {args...}; return functions[funcName](&t); } 

To be able to call functions from an external library, you must hold the "handler" to this library.

 void * handle; 

Fill mapping function. Input parameters: the path to the library. The return value is EXIT_FAILURE or EXIT_SUCCESS.

 int start(const std::string& libName) { bfd * abfd = bfd_openr(libName.c_str(), 0); if (abfd == NULL) { std::cout << "Error at openning: " << libName << std::endl; return EXIT_FAILURE; } (void) bfd_check_format(abfd, bfd_object); handle = dlopen(libName.c_str(), RTLD_LAZY); if (!handle) { std::cout << "Error at dlopen: " << libName << std::endl; bfd_close(abfd); return EXIT_FAILURE; } long storage_needed = bfd_get_symtab_upper_bound(abfd); if (storage_needed <= 0) { std::cout << "Error at get storage: " << libName << std::endl; bfd_close(abfd); dlclose(handle); return EXIT_FAILURE; } asymbol **symbol_table = (asymbol**) calloc (storage_needed, sizeof(char)); if (symbol_table == NULL) { std::cout << "Error at calloc: " << libName << std::endl; bfd_close(abfd); dlclose(handle); return EXIT_FAILURE; } long number_of_symbols = bfd_canonicalize_symtab(abfd, symbol_table); if (number_of_symbols <= 0) { std::cout << "Error at get number: " << libName << std::endl; free(symbol_table); bfd_close(abfd); dlclose(handle); return EXIT_FAILURE; } for (int i = 0; i < number_of_symbols; ++i) { const char* function_name = symbol_table[i]->name; void*(*method)(void*) = (void*(*)(void*))dlsym(handle, function_name); if (dlerror() == NULL) functions[function_name] = method; } free(symbol_table); bfd_close(abfd); return EXIT_SUCCESS; } 

In addition, you need to ensure the closure of the library "handler":

 void stop() { dlclose(handle); } 

The main function might look like this:

 int main() { if (start("./libfunctions.so") != EXIT_SUCCESS) return EXIT_FAILURE; void * ret = callFunction("func0", 1, 2); std::cout << *(int*)ret << std::endl; callFunction("func1", std::string("Hello"), std::string("World")); stop(); return EXIT_SUCCESS; } 

Do not forget to add the necessary headers:

 #include <tuple> #include <iostream> #include <map> #include <string> #include <functional> #include <dlfcn.h> #include <bfd.h> 

Compile with links to previously specified libraries:

 g++ main.cpp -lbfd -ldl 

Immediately there are problems with the return value.

First, the function must convert it to void * type before returning, and secondly, after receiving this value, it must be converted back to the desired type.

In addition, when calling each function, you must specify the parameters of the type it needs. That is why in the function call func1 I specified a conversion to std :: string.

But the problems do not end there. After processing the library, the map will be filled with some set of unnecessary characters. You can of course add some more condition (for example, functions should not begin with an underscore). Perhaps the libbfd library contains something to solve this problem, but I didn’t go deep into it.

This solution obviously does not have universality. However, it may be a reference point. And I would be glad to hear additions and improvements from more experienced programmers.

  • Why libbfd? Why can't we just save the handle returned by the dlopen and then call it with the name of the dlsym function each time? Those. Why drag the entire table of function names in a map? Or is it all because of the cross additions to the names and bfd_canonicalize_symtab() removes them? - avp
  • @avp is not not, I just pursued the goal of automatically determining the set of functions so that the user does not have to manually register all the functions that are in his library. extern "C" just removes these cross additions, if I'm not mistaken. It is supposed that this specifier prohibits the use of many convenient things, in particular the type of auto for the return value. Here it would be in place. - AccumPlus
  • Yeah, I just did not pay attention to extern "C" ... Rarely when there is a goal to cause all that is in the lib. Usually, only a few are needed for the task. And extern "C" does not seem to affect auto . The compiler is supposed to deduce the type from the prototype in the source (in .h) - avp
  • By the way, about the transfer of parameters in x86, I somehow answered . I think a primitive result (integer / double / address) can be obtained in a similar way. - avp
  • Ohh, how all a chore. I did not think that everything is so difficult. But thanks for the work. It just gets f-ii shipped gtk. And this is all to attach somehow, I think it will be difficult. The whole point is in one thing, that all of this should be directly somehow determined in runtime, the number of arguments and all that, that’s the problem. - HackMemory

if you need to call a function by the name specified as a string, then the first thing that comes to mind is to declare a two-dimensional array with the names and associated addresses of functions. For clarity, you can define the structure describing the element of the array:

 typedef struct { char* name; void* func; } funcnames_t; 

further declare an array:

 funcnames_t arr[3] = { { "func1", (void*) func1 }, { "func2", (void*) func2 }, { "func3", (void*) func3 } }; 

The next step is to iterate over the array to find the desired item and call the function.

to transfer a variable number of parameters of the specified function, it is probably worth using the array again. those. the type of functions to be called should be approximately as follows:

 void func(int argc, void** argv); 

I think the meaning is clear.

if we accept that the function looks exactly as I indicated, then the first structure can be reduced to the form:

 typedef void (*func_t)(int, void**); typedef struct { char* name; func_t func; } funcnames_t; 

and your code will look something like this:

 for (i = 0; i < sizeof(arr) / sizeof(funcnames_t); i++) { if (!strcmp(arr[i].name, funcname)) { arr[i].func(num_params, params); } } 

PS: the code does not claim to work and is intended only to give an idea of ​​the logic.

  • To give an idea of ​​the logic, you'd better take std::map (or even unordered_map ). Immediately C ++, not C in the tags :) - D-side

The solution is close to what you need. It is possible that it will even suit you. Found here . I duplicate in this answer:

 #include <type_traits> #include <utility> #include <string> #include <iostream> template<typename Fn, Fn fn, typename... Args> typename std::result_of<Fn(Args...)>::type wrapper(Args&&... args) { return fn(std::forward<Args>(args)...); } #define WRAPPER(FUNC, ...) wrapper<decltype(&FUNC), &FUNC>(__VA_ARGS__) int myFunc0(int arg1, int arg2) { return arg1 + arg2; } std::string myFunc1() { return "Some string"; } int main() { auto var1 = WRAPPER(myFunc0, 1, 2); auto var2 = WRAPPER(myFunc1); std::cout << var1 << std::endl << var2 << std::endl; return 0; } 

The compiler must support modern C ++ standards.

  • How does this relate to the desire to call a function by the name specified by the string? - αλεχολυτ
  • @alexolut Here it is not. But let's wait for the answer of the author. Perhaps there is no need for him to get the names of functions in runtime. - AccumPlus
  • So, just it is necessary to receive name f- - HackMemory
  • @HackMemory Well ... Another attempt) - AccumPlus