Как вызвать функцию по имени (std::string) в С++?
Интересно, есть ли простой способ вызвать функцию из строки. Я знаю простой способ, используя "if" и "else".
int function_1(int i, int j) {
return i*j;
}
int function_2(int i, int j) {
return i/j;
}
...
...
...
int function_N(int i, int j) {
return i+j;
}
int main(int argc, char* argv[]) {
int i = 4, j = 2;
string function = "function_2";
cout << callFunction(i, j, function) << endl;
return 0;
}
Это базовый подход
int callFunction(int i, int j, string function) {
if(function == "function_1") {
return function_1(i, j);
} else if(function == "function_2") {
return function_2(i, j);
} else if(...) {
} ...
...
...
...
return function_1(i, j);
}
Есть ли что-то более простое?
/* New Approach */
int callFunction(int i, int j, string function) {
/* I need something simple */
return function(i, j);
}
Ответы
Ответ 1
То, что вы описали, называется отражение, а С++ его не поддерживает. Однако вы можете столкнуться с некоторой практикой, например, в этом конкретном случае вы можете использовать std::map
, который сопоставляет имена функций (std::string
objects) с указателями функций, которые в случае функций с одним и тем же прототипом может быть проще, чем может показаться:
#include <iostream>
#include <map>
int add(int i, int j) { return i+j; }
int sub(int i, int j) { return i-j; }
typedef int (*FnPtr)(int, int);
int main() {
// initialization:
std::map<std::string, FnPtr> myMap;
myMap["add"] = add;
myMap["sub"] = sub;
// usage:
std::string s("add");
int res = myMap[s](2,3);
std::cout << res;
}
Обратите внимание, что myMap[s](2,3)
извлекает указатель функции, отображаемый в строку s
, и вызывает эту функцию, передавая ей 2
и 3
, делая вывод этого примера 5
Ответ 2
Использование карты стандартной строки для стандартных функций.
#include <functional>
#include <map>
#include <string>
#include <iostream>
int add(int x, int y) {return x+y;}
int sub(int x, int y) {return x-y;}
int main()
{
std::map<std::string, std::function<int(int,int)>> funcMap =
{{ "add", add},
{ "sub", sub}
};
std::cout << funcMap["add"](2,3) << "\n";
std::cout << funcMap["sub"](5,2) << "\n";
}
Ответ 3
Есть еще одна возможность, которая еще не упоминалась, которая отражает истина.
Опцией для этого является доступ к функциям, экспортированным из исполняемого файла или совместно используемой библиотеки, с использованием функций операционной системы для разрешения имен на адреса. Это интересное использование, например, загрузка двух "конкурсных" DLL в "судейскую" программу, так что люди могут ее вырезать, если их реальные коды сражаются друг с другом (играя "Реверси" или "Quake" ).
Другой вариант - это доступ к информации отладки, созданной компилятором. В Windows это может быть удивительно легко для компиляторов, которые совместимы, поскольку вся работа может быть отключена для системных DLL или бесплатных DLL, загружаемых из Microsoft. Часть функциональности уже содержится в Windows API.
Однако это больше относится к категории системного программирования - независимо от языка - и, следовательно, оно относится к С++ только в той мере, в какой оно является языком системного программирования par excellence.
Ответ 4
Это возможно на более низком уровне, если вы переплетаете сборку с c, а оттуда используете С++-компилятор с C, а затем используете С++, и это может быть форма отражения.
Принцип работы - через адреса функций. Операционные системы рандомизируют адреса, а затем переменные могут размещаться соответственно, такие как heapAddress + 1
, heapAddress + 2
и т.д. Это также работает и для функций.
DNS - это сервер доменных имен в мире Интернета. Он интерпретирует доменное имя, например http://www.example.com/
, на IP-адрес, скажем, возможно, 10.87.23.9/
(IP-адреса, начинающиеся с 10, обычно являются частными сетями). Вы могли бы использовать идею @LiHO и создать карту, но вместо адресов функций. Опять же, это очень низкий уровень, даже на уровне С++, и довольно востребован. С картой адресов, заданной строкой, вы можете, возможно, декодировать ее по адресу через ссылки на массив long или int.
После декодирования этого адреса вам нужно будет поместить двоичные инструкции в регистры CPU для выполнения. Если вы решите использовать сборку или C для выполнения задания, зависит от того, что вы делаете. Вы можете использовать подход стека операндов и использовать C для выполнения каждой команды с его параметрами, или вы можете использовать сборку для размещения инструкций, данных или обоих в регистры процессора для выполнения.
Вот как я буду его кодировать (если это псевдокод):
заголовочный файл языка C/С++:
//reflection.h
#pragma once
class Reflection {
public:
extern "C" {
static void exec(int& func_addr);
}
}
язык С++:
//mysourcefile.cpp
#pragma once
#include "reflection.h"
#include "map.h"
int main(){
Map map;
Reflection.exec(map.decode("run()"); // executes a function called run() in the current class
wait(10);//seconds
return 0;
}
void run(){
cout<<"This is now running via reflection from an assembly procedure in a C function in a C++ function!";
}
выход:
Теперь это выполняется путем отражения от процедуры сборки в функции C в С++-функции!