将宏定义的函数转换为 C++ 中的模板
Converting macro-defined functions to a template in C++
我在 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)
}
很有可能,而且非常简单。
##type
成为调用站点的模板参数,例如 load<uint8_t>
.
函数定义大同小异。只是现在模板参数是类型的替代。
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;
}
就是这样。除了调整呼叫站点外,无需执行任何其他操作。但我认为你获得的简洁是值得的。
如果这太令人生畏,那么您可以创建模板,并将宏定义更改为简单的转发功能,如您在 post.
中所述
只有您才能权衡成本与收益。
这里棘手的问题是,现有代码定义了 8 个具有 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); }
// ...
那时,除了稍微改进一下 class 的样式外,您是否进行了任何更改?生成的可执行代码可能根本没有什么不同。您确实获得了一个好处:现在 load_integer<T>
可以从另一个模板函数调用,该模板函数对不止一种类型的整数数据进行操作。
我在 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)
}
很有可能,而且非常简单。
##type
成为调用站点的模板参数,例如load<uint8_t>
.函数定义大同小异。只是现在模板参数是类型的替代。
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; }
就是这样。除了调整呼叫站点外,无需执行任何其他操作。但我认为你获得的简洁是值得的。
如果这太令人生畏,那么您可以创建模板,并将宏定义更改为简单的转发功能,如您在 post.
中所述只有您才能权衡成本与收益。
这里棘手的问题是,现有代码定义了 8 个具有 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); }
// ...
那时,除了稍微改进一下 class 的样式外,您是否进行了任何更改?生成的可执行代码可能根本没有什么不同。您确实获得了一个好处:现在 load_integer<T>
可以从另一个模板函数调用,该模板函数对不止一种类型的整数数据进行操作。