2009-10-29 117 views
0

我想做一些簡單而快速的控制檯調試器。這個小的lib應該嵌入到主程序中。如何使用帶變量名稱的字符串獲取變量地址?

所以我想這樣做的東西一樣運行在控制檯模式程序這一點的同時:

「輸入:打印我」 「輸出:15.53」 「輸入:設色255」 「輸入:打印顏色「 」輸出:255「

」i「和」color「都是預先聲明的變量in-code。它不是一個解釋器,只是檢查和修改變量內容的方便方法。

GDB不是我的問題的有效解決方案,因爲我將使用此代碼用於我將編碼的計算機圖形程序,因此它需要能夠以「發佈模式」運行。

到目前爲止,我發現的一個非常簡單的解決方案是製作一個包含void指針,指針數據類型和表示變量名稱的字符串的列表。但它不會像我想象的那樣自動化。

有沒有什麼辦法來轉換一個字符串,讓我們說「顏色」,以獲得在C++中命名爲顏色的整數變量的地址?如果不是,我怎麼能解決這個問題?

+4

GDB的確是最小的必要「控制檯調試器」。 「轉換一個字符串讓我們說」color「來獲得名爲color的整型變量的地址的唯一方法是讀取調試符號。這就是GDB所做的,當你處於「發佈模式」時,這就是你失去的東西 – Arkadiy 2009-10-29 18:19:33

+0

所以是的,它是一個解釋器。您想要解釋一個字符串值並檢索變量的地址。您需要手動完成這項工作。所有源符號信息都從可執行文件中刪除。 – 2009-10-29 18:42:22

+1

@Martin:但調試信息_can_可以存儲在外部文件中,所以不必丟失。 – xtofl 2009-10-29 20:33:52

回答

3

變量名是針對程序員的,C++不存儲元數據。 (關於源代碼的數據)。運行時不能通過名稱訪問變量。這就是說,你可以使用類似std::map或unordered_map(tr1boost),它給你一個關鍵和價值。問題在於容器中的值必須是同質的。這可以通過使用Boost.Any來緩解。

+0

無論如何,你會想要存儲類型,因爲腳本語句「foo = 7」需要根據foo的類型 - int,float甚至字符串的不同來解釋嗎? 'boost :: variant <>'是一個更好的選擇;您可以枚舉您的腳本語言支持的所有類型。 – MSalters 2009-10-30 10:21:16

2

不,你不能這樣做。一旦將源代碼編譯爲二進制形式並將其去除符號,就不會在二進制文件中記錄人類可讀的名稱。

C++中的一個選項是使用std :: string中的std :: map指向變量的任何數據類型。然後你可以參考my_map["my_variable_name"] = new_value。但是這涉及到不重要的性能影響,並且在大多數情況下不是一個好主意。

1

AFAIK,沒有通用的解決方案。您可以使用編譯器生成的調試信息來執行此操作。顯然,即使在發佈版本中,也必須包含調試信息。你將不得不找出這些調試信息的格式爲您的特定平臺/編譯器,讀取它,解析它等...

我想,你提到的方式(明確聲明你想訪問的位置從你的「調試器」)似乎是最好的方式去這裏。您可以使用宏作爲語法糖,以稍微減輕負擔:

struct descriptor { 
    const char* name; 
    const char* type; 
}; 

#define DEFGLOBAL(type, name) \ 
    static struct descriptor name ## _descriptor = { \ 
     # name, # type \ 
    }; \ 
    type name 

DEFGLOBAL(int, some_number); 

我建議你在看看Emacs的源代碼。他們沿着上述方法做了一些事情來將C變量導出到Lisp解釋器。他們還使用它將原始函數等導出到Lisp級別。

0

你可以對其進行優化,並(在GCC可能)與調試符號建立它,然後找出如何讀取調試符號,這樣只是調試自己像gdb會..但這真的好像過度殺傷..你確定這是一個可行的設計,並使用適當的語言做這樣的事情?該控制檯調試器到底何時會被使用?

4

您可能不知道這個成語,這可能使你更容易實現這一點:

#include <stdio.h> 
#define REPORT(V) printf("%s: %i\n", #V, V); 
int main() { 
    int i = 5; 
    REPORT(i); 
    return 0; 
} 

輸出是

i: 5 
+0

問題是,如果它不是你想要打印的int,那麼'%i'將不起作用。 – 2009-10-30 07:36:52

+0

小細節,使用C++很容易解決。例如。 'std :: clog <<(#V)<<(V)<< std :: endl' – MSalters 2009-10-30 10:18:04

4

不適合心臟佯攻,但你可以使用dlopendlsym來獲取全局聲明變量的地址。

0

您認爲的簡單解決方案實質上是可用於非調試C/C++代碼的唯一解決方案。不過,我認爲您應該考慮兩個絆腳石:

  1. 您將只能觀察全局變量。您的檢查器中的任何局部變量都將無法提供給您的檢查器,因爲當您從命令行處理輸入時,這些堆棧框架可能不存在,可能是作爲頂級處理循環的一部分。 (除非在每個函數中處理命令行輸入,我不建議這樣做,在這種情況下,當您鍵入命令時,您將永遠不會知道將檢查哪個函數的堆棧幀)。
  2. 您將防止編譯器確定註冊優化。如果你的代碼獲取了一個變量的地址(例如,在你的檢查器的符號表中包含一個指向該變量的指針),那麼編譯器必須將該變量放置在一個內存位置,即使該變量只會將該變量保存在一個寄存器中爲了速度。
1

可以使用帶外部符號文件的GDB(symbol-file命令)。因此,如果您使用外部文件中的調試信息構建可執行文件,則甚至不必編寫自己的調試程序......只要在調試客戶的安裝可執行文件時不要忘記帶上符號文件。

0

Lua的另一種選擇是使用Boost :: Python;它也允許你創建一個quick-n-dirty腳本API。

0

什麼讓你覺得你不能在「發佈模式」下運行GDB? GDB用來識別變量的是調試符號,無論指定了哪些其他編譯器標誌,都可以構建它們。您可以啓用優化以及構成您的「發佈模式」的其他任何內容,並且仍然會生成供GDB使用的調試符號。