Как вызвать функцию по имени (std::string) в С++? - программирование
Подтвердить что ты не робот

Как вызвать функцию по имени (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);
}
4b9b3361

Ответ 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 в С++-функции!