对于我正在开发的二进制协议(protocol)库,我广泛使用了 StrongType<T>
,我正在将这个概念扩展到枚举。
我有一个 InBitStream
实现逐位读取字节数组的对象,我的意思是,每个字段都可以以固定和预定义的位数从数组中读取。
例如,我可以读取 StrongType
d 字段以 5 位存储,然后以这种方式存储其他 3 位:
class SomePacketType {
void readFromStream(DataPacket const &p) {
InBitStream stream(p);
auto field1 = makeBitField<5>(mFirstField); // references to mFirstField, using 5 bits
auto field2 = makeBitField<3>(mSecondField); // same, 3 bits
stream >> field1 >> field2;
}
当然,某处定义了这个运算符
class InBitStream {
template<int N, typename T, typename TAG>
friend InBitStream &operator >> (InBitStream &stream, BitField<N,StrongType<T,TAG>> &value) { ...
}
}
枚举也是如此。枚举是这样定义的:
struct XTag {
};
enum class XEnum {
N = 0, A = 1, B = 2, C = 3, D = 4,
};
using X = utils::BitAwareEnum<3, XEnum, XTag>;
我现在的想法是添加一个验证函数,以检查是否从流中读取枚举(或强类型)并根据某些特殊值进行验证。例如X
不应收到大于 4 的值。
我的意图是这样使用它,假设有一些 ValidatedField
理念:
template <???>
InStream &operator >> (InStream &stream, ValidatedField<???> &v) {
v.validate();
return stream >> static_cast<some_base_class?>(v);
}
ValidatedField 应该如何定义? 请注意,验证函数取决于模板化类型。例如,对于 StrongType,它会检查它是否为正数,而对于枚举,它必须检查最大值、最小值和一些内部无效值。
要求 ValidatedField 与类型具有相同的接口(interface),所以我无法组合它,也没有 virtual
函数是允许的,因为类型从不以多态方式使用。
(模式应该类似于装饰器,也许)。
有什么想法吗?谢谢。
最佳答案
然后我找到了一个好的解决方案。
这是 ValidatedField
类。
template<typename Type, typename Validator>
struct ValidatedType : Type {
friend InBitStream &operator>>(InBitStream &stream, ValidatedType<Type, Validator> &v)
{
stream >> static_cast<Type &>(v);
Validator::validate(v);
return stream;
}
};
这只是为验证类型创建一个标签。流运算符 >>>
是使用它调度的。 Type
模板类型用于公开嵌入类型,Validator
类型用作仿函数
。验证器必须公开一个静态函数 validate(Type &const)
。
在流运算符实现中,v
参数必须向上转换为 Type &
,才能在“装饰”类型上正确调用相同的操作。
这是它的用法:
struct XTag {
};
enum class XEnum {
A = 1, B = 2, C = 3, D = 4,
};
using XE = utils::BitAwareEnum<4, XEnum, XTag>;
struct XValidator {
static void validate(XE const &x)
{
if (x.underlyingValue() <= 0 || x.underlyingValue() > 4) {
throw utils::InBitStream::IllegalValueException("Illegal XE value");
}
}
};
using X = utils::ValidatedType<XE, XValidator>;
X x;
我喜欢这个解决方案的地方: - 我有一个装饰类型的“树”,其功能可以根据需要“插入”
我不喜欢这个解决方案的地方:
- XValidator
的结构有点冗长,我希望有一种更易读的方式来实现它。我需要记住 validate()
函数必须声明为静态的。
请随时评论改进或替代解决方案。
对于那些好奇的人,该项目在此处开源:https://gitlab.com/ffuga/gutils
更新:
正如@Incomputable 指出的那样,这是一个 Policy Based design .
按照维基百科页面中的示例重新排列代码,我修复了“静态”问题。
这是非常明确的代码:
template<typename Type, typename Validator>
struct ValidatedType : Type, Validator {
using Validator::validate;
friend InBitStream &operator>>(InBitStream &stream, ValidatedType<Type, Validator> &v)
{
stream >> static_cast<Type &>(v);
v.validate(v);
return stream;
}
};
用法:
struct XValidator {
void validate(XE const &x)
{
if (x.underlyingValue() <= 0 || x.underlyingValue() > 4) {
throw utils::InBitStream::IllegalValueException("Illegal XE value");
}
}
};
关于c++ - 如何在 C++ 中正确实现类型 "extension"函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54236863/