【问题标题】:Converting macro-defined functions to a template in C++将宏定义的函数转换为 C++ 中的模板
【发布时间】:2017-10-12 23:11:58
【问题描述】:

我正在处理a C++ program 中的以下难以调试的代码。我对 C++ 很陌生,但我认为这可能是转换为模板的好选择。但是,我很难理解它会是什么样子以及如何调用新函数。我还会有多个 load_func 函数,然后只调用具有指定显式类型的模板函数吗?或者是否可以将所有 8 个函数替换为单个函数模板?哪种方法会更好?我相信第一种方法需要对其他文件的修改最少,而第二种方法需要我更新对这些函数的所有调用以明确指定类型。

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

【问题讨论】:

  • 也许只是简化了,但如果函数根本不使用this,则应标记为static。 (如果一个类的所有成员都是static,那么它不应该是一个类。)
  • @aschepler load_slow_path 是一个使用非静态成员变量的成员函数。另外我认为我遗漏的代码也使用了成员变量。您在链接中查看它

标签: c++ class templates macros member-functions


【解决方案1】:

这很有可能,而且非常简单。

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

就是这样。除了调整呼叫站点外,无需执行任何其他操作。但我认为因为您获得的简洁性,这是值得的。

如果这太令人生畏,那么您可以创建模板,并将宏定义更改为简单的转发函数,如您在帖子中所述。

只有你才能权衡成本和收益。

【讨论】:

  • 类型是type##_t,而不是##type## 是预处理器中的二元运算符; ##type 甚至没有任何意义。
  • @DanielH - type 并不是真正的类型。这是一个令牌,我知道 :)
  • 不,但type##_t 成为类型名称的标记,而type 本身类似于uint8,因此甚至不是。
  • @DanielH - 注意我明确提到了呼叫站点。重点是将load_uint8 变成load&lt;uint8_t&gt;
  • 我明白 StoryTeller 的意思,这里的重点无论如何都不是预处理器/宏标记化和连接。
【解决方案2】:

这里的棘手问题是,现有代码定义了 8 个函数,具有 8 个不同的名称(load_uint8load_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&lt;uint8_t&gt;(addr) 等等。

因此,为了向后兼容,同时提供旧功能可能是个好主意:

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

在那一点上,除了稍微改进该课程的风格之外,您是否进行了任何更改?生成的可执行代码可能根本没有什么不同。您确实获得了一个好处:现在可以从另一个模板函数调用 load_integer&lt;T&gt;,该函数对不止一种类型的整数数据进行操作。

【讨论】:

  • “在那一点上,除了稍微改进一下那个类的风格之外,你有没有改变什么?”我正在尝试使用 gdb 调试此代码库并逐步执行代码。使用模板,通过(gdb) next 单步执行代码变得更加容易,对吧?现在next 跨过整个街区。在这个代码库中有 很多 地方似乎过度使用了这样的宏。在此处查看示例:github.com/riscv/riscv-fesvr/blob/…
  • @EvanCox - 是的,模板是可调试的,不像宏。因此,即使是转发解决方案也将帮助您管理您的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-28
  • 2023-01-22
  • 2016-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多