2013-10-20 129 views
14

我不知道是否有一種簡單的方法來從字符串調用函數。我知道一個簡單的方法,用'if'和'else'。如何在C++中通過名稱(std :: string)調用函數?

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); 
} 

回答

34

你所描述什麼叫反射和C++不支持它。但是,您可能會遇到一些變通方法,例如在這個非常具體的情況下,您可能會使用將函數名稱(std::string對象)映射到函數指針的函數指針,這些函數指針在具有相同原型的函數中可能比它看起來:

#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函數指針並調用此函數,傳遞23它,使這個例子的輸出是5

+0

您可以使用std :: function和初始化程序列表語法稍微改進。 –

+0

@LokiAstari:'std :: function'是C++ 11我猜。 – LihO

+0

@李歐:我喜歡你的方法。謝謝。 –

13

使用地圖的標準字符串轉換爲標準功能。

#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"; 
} 
+0

很好,使用'std :: functional'代碼更乾淨。 –

+0

@AlanValejo:當然是:) C++中很多東西變得更乾淨11 – LihO

0

這有可能在一個較低的水平,如果你以c纏繞組件,並從那裏使用C++編譯器C,然後使用C++,並且可以是反射的一種形式。

它的工作方式是通過函數地址。操作系統將地址隨機化,然後可以相應地放置變量,例如heapAddress + 1,heapAddress + 2等等。這也適用於功能。

DNS是互聯網世界中的域名服務器。它將域名(例如http://www.example.com/)解釋爲IP地址,也可能是10.87.23.9/(以10開頭的IP通常是專用網絡)。您可以使用@LiHO的想法並創建一個映射,而不是函數地址。再次,這是非常低的水平,甚至沒有在C++層面上,並且相當重要。通過地址映射,給定一個字符串,您可以通過引用long或int數組將其解碼爲一個地址。

解碼到地址後,必須將二進制指令放入CPU寄存器中執行。無論你決定使用程序集還是C來完成這項工作,都取決於你的工作。你可以採用操作數堆棧的方法,並用C來執行每條指令的參數,或者你可以使用匯編來將指令,數據或兩者放入CPU的寄存器中執行。

這是我如何將代碼時(提供這是僞代碼):

語言C/C++頭文件:

//reflection.h 
#pragma once 
class Reflection { 
public: 
    extern "C" { 
    static void exec(int& func_addr); 
    } 
} 

語言C++:

//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++函數中通過C函數中的彙編程序的反射運行N!

+1

什麼是Map?您不包含任何包含它的標準或自定義標題。無論如何,請沒有人真的這樣做...... – Grault

+0

@Jesdisciple這是僞代碼。我不需要定義Map,因爲用戶定義了Map類。我將編輯以包含「map.h」。另外,爲什麼勸阻呢?組裝有什麼問題嗎?從CPU寄存器獲取值有什麼問題? –

+0

啊,我以爲你指的是自定義或第三方的東西。我會阻止大會,因爲它很神祕,特別是因爲它很聰明。 – Grault

3

有哪些還沒有被提及的另一種可能性,這是真正的反映

此一個選擇是使用訪問操作系統功能名稱解析爲地址從可執行或共享庫中導出的函數。這有一些有趣的用途,比如將兩個「參賽者」的dll加載到「裁判」程序中,這樣人們可以通過讓他們的實際代碼相互對抗(玩Reversi或Quake,不管)。

另一種選擇是訪問由編譯器創建的調試信息。在Windows下,對於兼容的編譯器來說,這可能會非常容易,因爲所有工作都可以卸載到系統dll或從Microsoft下載的免費dll。部分功能已經包含在Windows API中。

然而,落在更進系統編程的範疇 - 無論何種語言的 - 並且因此它涉及到C++只是因爲它是系統編程語言出類拔萃。

+0

這看起來幾乎就像我上面的答案的一個缺失部分,這正是我的答案,但以一種不同的方式:「使用操作系統函數來解析名稱到地址」除了我的只是直接的,並且需要您自己的'算法'將一個字符串解碼爲一個地址。 –

+0

**反射**思想的要點是訪問由編譯器創建的元數據,而不必使用函數名稱和指針定義表格。除了導出的名稱和調試符號外,還有RTTI(包含一些編譯器)以及混合方法,如處理由編譯器創建的.map文件,用於映射函數名稱和地址。 JEDI項目爲堆棧散步做了這個工作,但它也可以用來查找名稱的地址。這個想法是使用反映源代碼的編譯器生成的數據,而不是必須定義自己的表。 – DarthGizka

相關問題