我有一个包含函数的库:
// Gets [bytes] number of *bytes* and fills rx_buff
void getData(uint8_t* rx_buff, uint16_t bytes);
我还有一个我正在为其编写接口的库,其结构相当简单(受保护的IP,所有名称都是虚构的):
[libGetData] <-> libMyLib <-> [libDoStuff]
我无法修改libGetData,也不能更改libDoStuff。 libDoStuff具有许多功能,如下所示:
bool isDataGood8(uint8_t input);
bool isDataGood16(uint16_t input);
bool isDataGood32(uint32_t input);
bool isDataGood64(uint64_t input);
我只想要一种干净的方法来实现getData()和所有isDataGoodXX()类型之间的中间函数。
template <typename T>
T getValue()
{
// Create intermediate array for getData
uint8_t temp_array[sizeof(T)];
// Fill temp_array
getData(&temp_array, sizeof(T));
// Create value type to hold byte-array
T return_val;
// Copy array data into integer type
std::memcpy(&return_val, temp_array, sizeof(T));
return return_val;
}
此模板有效吗?
答案 0 :(得分:3)
是的,此代码几乎是正确的,只有一点例外。您对getData的调用应如下所示:
getData(temp_array, sizeof(T));
您不应将 array 的地址传递到getValue函数中。除此之外,它还不错,并且不会遇到常见的严格混叠违规行为。
您还避免了任何对齐问题,并且代码是API所能提供的最佳代码。
答案 1 :(得分:0)
我看到的一个问题是,因为您仅在模板返回的值,所以您将需要显式指定类型以使用模板方法,即
auto val = getValue<uint32_t>();
isDataGood(val); // Should be!
如果不使用auto,则容易出现不匹配错误,并非所有错误都会报告:
uint32_t val = getValue<uint16_t>();
isDataGood(val); // perhaps not!
这取决于您希望如何避免这种情况,但是一种方法是使返回值成为一个参数,该参数允许推导模板类型:
uint32_t val;
getValue(val);
isDataGood(val); // Must be!
另一种方法是不使用原始整数,而始终使用包含该值的结构作为正确大小的成员。您无法像在整数大小之间那样在结构之间轻松切换。
另一种方法是让getValue返回一个包装,该包装针对给定类型知道如何强制转换为它,而对于其他类型,则在编译时强制转换失败:
struct FailIntBase
{
operator uint8_t()const = delete;
operator uint16_t()const = delete;
operator uint32_t()const = delete;
operator uint64_t()const = delete;
// etc
};
template <class Castable> struct SafeInt: FailIntBase
{
Castable val;
SafeInt(Castable val): val(val) {}
operator Castable()const
{ return val; }
};
template <typename T>
SafeInt<T> getValue() { //...
// Testing:
auto X = getValue<uint16_t>();
uint16_t Y = getValue<uint16_t>();
uint32_t Z = getValue<uint16_t>(); //error: use of deleted function 'FailIntBase::operator uint32_t() const'
如果您需要涵盖更多类型(包括带符号类型),则可以使用信息量较少的错误消息来实现FailIntBase的更小实现:
conversion from 'SafeInt<short unsigned int>' to 'uint32_t' {aka 'unsigned int'} is ambiguous
note: candidate: 'SafeInt<Castable>::operator Castable() const [with Castable = short unsigned int]'
note: candidate: 'FailIntBase::operator uint64_t() const' <deleted>