我们有一些如下所示的代码:
class Serializer
{
public:
template<class Type> void Write(const Type& value)
{
internal_write((byte*)value, sizeof(Type));
}
// some overloads of Write that take care of some tricky type we have defined
private:
// implementation of internal_write
};
正如人们可能猜测的那样,这会将数据写入磁盘。我们有类似的 Read 函数,或多或少地将一些字节转换为类型。虽然不像它应有的那样健壮,但它确实有效,因为我们在与读取相同的平台上写入——这意味着我们写入的字节与我们需要读取的字节相匹配。
我们现在正在转向支持多个平台。我们在几个地方都有这样的代码:
unsigned long trust_me_this_will_be_fine = get_unsigned_long();
a_serializer.Write(trust_me_this_will_be_fine);
这在当前世界中工作得很好,但如果我们假设我们想要支持的平台之一有 unsigned long
一个是 32 位,另一个是 64 位,我们都被淹没了。
我想更改Serializer::Write
仅采用显式大小的类型作为参数。我想到了这一点:
class Serializer
{
public:
void Write(uint32_t value) { ... }
void Write(uint64_t value) { ... }
};
但我认为这并不能真正解决问题,因为在 32 位系统上,unsigned long
会自动转换成 uint32_t
但在 64 位系统上它将自动转换为 uint64_t
.
我真正想要的是 Write(uint32_t)
仅接受 uint32_t
类型的参数——这意味着它需要显式的强制转换。我认为没有直接的方法可以做到这一点 - 如果我错了,请告诉我。
除此之外,我可以想出两种方法来解决这个问题。
- 声明(但不定义)
Serializer::Write
的私有(private)版本对于可以自动转换为我们支持的类型的每种类型。 - 不要接受
uint32_t
直接,但是一个持有uint32_t
的类并且只有uint32_t
的显式构造函数.
选项 2 看起来像这样:
class only_uint32
{
public:
uint32_t _value;
explicit only_uint32(uint_32 value) : _value(value) { }
};
class Serializer
{
public:
void Write(only_uint32 value) { ... }
};
然后调用代码如下所示:
unsigned long might_just_work = get_unsigned_long();
a_serializer.Write(static_cast<uint32_t>(might_just_work)); // should work, and be explicitly sized.
a_serializer.Write(might_just_work); // won't compile
我想很多人都已经解决了此类问题。有没有我没有想到的首选方法?我的一个想法是可怕的、伟大的、可行的还是什么?
PS:是的,我知道这是一篇超长的文章。不过,这是一个有点复杂和详细的问题。
更新:
感谢您的想法和帮助。我认为我们将采用这样的解决方案:
class Serializer
{
public:
template<class Type> void Write32(const Type& value)
{
static_assert(sizeof(Type) == 4, "Write32 must be called on a 32-bit value.");
internal_write(reinterpret_cast<byte*>(value), 4);
}
// overloads like Write64 and various tricky types as before.
private:
// implementation of internal_write
};
这是一个相当低成本(就工程时间而言)的解决方案,它成功地将您实际保存的内容推送给调用者,并迫使调用者知道他们在调用什么。
最佳答案
你可以这样做:
template<typename T>
class MyClass {
public:
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid type");
MyClass(T t) : data(t) { }
private:
uint32_t data;
};
或者,如果您只想接受仅 uint32_t
:
template<typename T>
class MyClass {
public:
static_assert(is_same<T, uint32_t>::value, "Invalid type");
MyClass(T t) : data(t) { }
private:
uint32_t data;
};
关于c++ - 强制内部 API 的调用者使用固定大小类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9107690/