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.
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