将宏定义的函数转换为C++中的模板

问题描述:

我正在处理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是一个使用非静态成员变量的成员函数。另外我认为我遗漏的代码也使用成员变量。您可以在链接 –

这里的一个棘手问题是,现有的代码定义了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

这是非常可能的,而且非常简单。

  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',甚至没有。 –