2017-10-12 151 views
3

我正在處理a C++ program中的以下難以調試的代碼。我對C++很陌生,但我認爲這可能是轉換爲模板的好選擇。但是,我很難理解這將會是什麼樣子,以及如何調用這些新功能。我是否仍然有多個load_func函數,然後用指定的顯式類型調用模板函數?或者這些功能中的所有8個功能都可以用一個功能模板來代替?哪種方法會更好?我相信第一種方法需要對其他文件進行最少的修改,而第二種方法則需要我更新對這些函數的所有調用以明確指定類型。將宏定義的函數轉換爲C++中的模板

class mmu_t 
{ 
public: 
//... 
    // template for functions that load an aligned value from memory 
    #define load_func(type) \ 
    inline type##_t load_##type(reg_t addr) { \ 
     // ... Other code elided for clarity 
     type##_t res; \ 
     load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res); \ 
     return res; \ 
    } 

    // load value from memory at aligned address; zero extend to register width 
    load_func(uint8) 
    load_func(uint16) 
    load_func(uint32) 
    load_func(uint64) 

    // load value from memory at aligned address; sign extend to register width 
    load_func(int8) 
    load_func(int16) 
    load_func(int32) 
    load_func(int64) 
} 
+1

也許這只是簡單化,但如果函數根本不使用'this',它們應該被標記爲'static'。 (如果一個類的所有成員都是'static',它不應該是一個類。) – aschepler

+0

@aschepler load_slow_path是一個使用非靜態成員變量的成員函數。另外我認爲我遺漏的代碼也使用成員變量。您可以在鏈接 –

回答

2

這裏的一個棘手問題是,現有的代碼定義了8個不同名稱的函數(load_uint8,load_uint16,...,load_int64),但函數模板只有一個名稱。

所以,當然,你可以改變這

template <typename T> 
inline T load_integer(reg_t addr) { 
    static_assert(std::is_integral<T>::value, "T must be an integer type"); 
    // Other code... 
    T res; 
    load_slow_path(addr, sizeof(T), reinterpret_cast<uint8_t*>(&res)); 
    return res; 
} 

但如果這一切你,是的,代碼的其餘部分將需要改變從mmu.load_uint8(addr)mmu.load_integer<uint8_t>(addr)等取代它。

所以它很可能是一個好主意,也提供舊功能向後兼容性:

inline uint8_t load_uint8(reg_t addr) { return load_integer<uint8_t>(addr); } 
// ... 

在這一點上,你有沒有改變任何東西,但提高該類一點的風格?生成的可執行代碼可能完全不同。您獲得了一個好處:現在可以從另一個模板函數調用load_integer<T>,該模板函數對多種類型的整數數據進行操作。

+0

「那個時候,你改變了什麼,但改進了那個班的風格?」我試圖用gdb調試這個代碼庫,然後逐步執行代碼。使用模板,通過'(gdb)next'遍歷代碼變得容易得多,對吧?現在「下一步」跨越整個街區。 在這個代碼庫中有很多地方似乎像這樣過度使用宏。看看這裏的例子:https://github.com/riscv/riscv-fesvr/blob/6330e8668092e8a9c8af97f75313261d8e39b001/fesvr/elfloader.cc#L37 –

+1

@EvanCox - 是的,模板是可調試的,不像宏。所以即使是轉發解決方案也能幫助你管理你的代碼。 – StoryTeller

4

這是非常可能的,而且非常簡單。

  1. ##type成爲呼叫站點的模板參數,例如load<uint8_t>
  2. 函數定義幾乎相同。只有現在,模板參數纔是該類型的代表。

    template<typename T> 
    inline T load(reg_t addr) { 
        // ... Other code elided for clarity 
        T res; 
        load_slow_path(addr, sizeof(T), (uint8_t*)&res); 
        return res; 
    } 
    

就是這樣。除了調整呼叫站點之外,不需要做其他任何事情。但我認爲這是值得的,因爲你獲得了簡潔。

如果這太令人生畏,那麼您可以創建該模板,並將宏定義更改爲簡單的轉發函數,如您在文章中所述。

只有您可以權衡成本與收益。

+0

中查看它的類型是「type ## _ t」,而不是「## type」。 '##'是預處理器中的二元運算符; '## type'甚至沒有任何意義。 –

+0

@DanielH - 'type'並不是真正的類型。這是一個令牌,我知道:) – StoryTeller

+0

不,但'type ## _ t'成爲類型名稱的標記,而'type'本身就像'uint8',甚至沒有。 –