简单的解决方案 - 检查函数指针是否相互依赖
这实际上通过模式匹配非常简单。我们可以编写一个constexpr 函数,我将其命名为checkInverse,如果类型反转则返回true,否则返回false:
template<class S, class T>
constexpr bool checkInverse(S(*)(T), T(*)(S)) {
return true;
}
template<class S, class T, class Garbage>
constexpr bool checkInverse(S(*)(T), Garbage) {
return false;
}
因为第一种情况比较特殊,如果满足则函数返回true,否则返回false。
然后我们可以使用它来检查一个类的 Serialize 和 Deserialize 方法是否相互匹配:
template<class T>
constexpr bool isValidPolicy() {
return checkInverse(T::Serialize, T::Deserialize);
}
如果我们不确定该类是否有 Serialize 和 Deserialize 方法?
我们可以扩展 isValidPolicy 以使用 SFINAE 进行检查。现在,只有当这些方法存在并且它们满足类型共同依赖时,它才会返回 true。
如果我调用isValidPolicy<Type>(0),那么它将尝试使用int 重载。如果Serialize 和Deserialize 不存在,它将回退到long 重载,并返回false。
template<class Policy>
constexpr auto isValidPolicy(int)
-> decltype(checkInverse(Policy::Serialize, Policy::Deserialize))
{
return checkInverse(Policy::Serialize, Policy::Deserialize);
}
template<class Policy>
constexpr auto isValidPolicy(long)
-> bool
{
return false;
}
这个解决方案有什么缺点?
从表面上看,这似乎是一个很好的解决方案,尽管它确实存在一些问题。如果Serialize 和Deserialize 被模板化,它将无法转换为函数指针。
此外,未来的用户可能希望编写Deserialize 方法来返回可以转换为序列化类型的对象。这对于在不复制的情况下直接将对象构造到向量中非常有用,从而提高了效率。此方法不允许以这种方式编写 Deserialize。
高级解决方案-检查Serialize是否存在特定类型,以及Deserialize返回的值是否可以转换为该类型
这个解决方案更通用,最终也更有用。它为Serialize 和Deserialize 的编写方式提供了很大的灵活性,同时确保了某些约束(即Deserialize(Serialize(T)) 可以转换为T)。
检查输出是否可以转换为某种类型
我们可以使用 SFINAE 来检查这一点,并将其包装到 is_convertable_to 函数中。
#include <utility>
#include <type_traits>
template<class First, class... T>
using First_t = First;
template<class Target, class Source>
constexpr auto is_convertable_to(Source const& source, int)
-> First_t<std::true_type, decltype(Target(source))>
{
return {};
}
template<class Target, class Source>
constexpr auto is_convertable_to(Source const& source, long)
-> std::false_type
{
return {};
}
检查一个类型是否代表一个有效的序列化器
我们可以使用上面的转换检查器来做到这一点。这将检查给定类型,该类型必须作为参数传递给模板。结果以静态布尔常量的形式给出。
template<class Serializer, class Type>
struct IsValidSerializer {
using Serialize_t =
decltype(Serializer::Serialize(std::declval<Type>()));
using Deserialize_t =
decltype(Serializer::Deserialize(std::declval<Serialize_t>()));
constexpr static bool value = decltype(is_convertable_to<Type, Deserialize_t>(std::declval<Deserialize_t>(), 0))::value;
};
惰性反序列化器示例
我之前提到过,可以依靠重载转换运算符来进行序列化/反序列化。这是一个非常强大的工具,我们可以用它来编写惰性序列化器和反序列化器。例如,如果序列化的表示是 char 的 std::array,我们可以这样编写惰性反序列化器:
template<size_t N>
struct lazyDeserializer {
char const* _start;
template<class T>
operator T() const {
static_assert(std::is_trivially_copyable<T>(), "Bad T");
static_assert(sizeof(T) == N, "Bad size");
T value;
std::copy_n(_start, N, (char*)&value);
return value;
}
};
一旦我们有了它,编写一个适用于任何可简单复制的类型的Serialize 策略就相对简单了:
#include <array>
#include <algorithm>
class SerializeTrivial {
public:
template<class T>
static std::array<char, sizeof(T)> Serialize(T const& value) {
std::array<char, sizeof(T)> arr;
std::copy_n((char const*)&value, sizeof(T), &arr[0]);
return arr;
}
template<size_t N>
static auto Deserialize(std::array<char, N> const& arr) {
return lazyDeserializer<N>{&arr[0]};
}
};