请帮助我了解我做错了什么。
我正在尝试使用 RapidJSON 制作一个可序列化的变量包装器。我想提供用户定义的转换函数或使用默认的文字转换函数。另外,我想写一个交叉编译的解决方案(MSVS & MinGW)。
当 UserType 不是文字类型时,在编译时选择用户定义的
toJson / fromJson
,否则使用定义的构造函数 rapidjson::Value ()
和 rapidjson::Value ().template Get <LiteralType>
函数。
在尝试将 SFINAE 或概念应用于自定义用户类型时,我遇到了困难。但对于文字类型(int、double 等)它工作得很好。
问题:尝试通过 SFINAE 定义带有转换器的结构由 UserType 解析失败,因为我想通过 SFINAE 忽略内部 RapidJSON 模板错误:
error: 'Get' is not a member of 'rapidjson::internal::TypeHelper<rapidjson::GenericValue<rapidjson::UTF8<> >, CustomType1>'
32 | T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
为最小示例重新创建的rapidjson类型系统
namespace rapidjson {
template <class T = int>
class UTF8 { };
namespace internal
{
template <typename ValueType, typename T>
struct TypeHelper {};
template<typename ValueType>
struct TypeHelper<ValueType, double> { static double Get(const ValueType& v) { return v.GetDouble(); } };
template<typename ValueType>
struct TypeHelper<ValueType, int> { static double Get(const ValueType& v) { return v.GetInt(); } };
}
template <typename BaseAllocator = void>
class MemoryPoolAllocator { };
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericValue
{
typedef GenericValue<Encoding, Allocator> ValueType;
public:
explicit GenericValue() = default;
explicit GenericValue(double d) { }
explicit GenericValue(int i) { }
template <typename T>
T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
double GetDouble() const { return 333.3; }
int GetInt() const { return 333; }
};
//! GenericValue with UTF8 encoding
typedef GenericValue<UTF8<> > Value;
}
我的包装部分
namespace Serializable {
// Base template for literal types, no constrains
// Assume that user for his type will define the same as the static function at UserJsonConversions class
template<class UserType>
void fromJson(UserType& userValue, const rapidjson::Value& initializer)
{ userValue = initializer.template Get<UserType>(); }
template <class UserType>
using FromJsonFuncPtrType = void(*)(UserType&, const rapidjson::Value&);
// As I know, if while checking statement for validity the error will be thrown,
// the concept must fails and belongs to it declaration expression must be skipped by SFINAE
template <class UserType>
concept HasFromJsonCast = requires {
rapidjson::Value().Get<UserType>();
};
template <class UserType>
concept NoFromJsonCast = !HasFromJsonCast<UserType>;
// SFINAE helper,
// In the base template suppose we received a custom user type
template <class UserType, class ExternalConversions, class = void>
struct Distinct {
inline static constexpr const FromJsonFuncPtrType<UserType> fromJson = &ExternalConversions::fromJson;
};
template <class UserType, class ExternalConversions>
struct Distinct<UserType, ExternalConversions, std::void_t<std::enable_if<HasFromJsonCast<UserType>, void>>> {
inline static constexpr const FromJsonFuncPtrType<UserType> fromJson = &fromJson;
};
}
用户程序
// User-related types and conversions
struct CustomType1 { };
struct CustomType2 { };
struct UserJsonConversions
{
static rapidjson::Value fromJson(CustomType1& customValue, const rapidjson::Value& initializer)
{ return rapidjson::Value(); }
static rapidjson::Value fromJson(CustomType2& customValue, const rapidjson::Value& initializer)
{ return rapidjson::Value(); }
};
template<class UserType>
inline auto attempt()
{
rapidjson::Value json;
UserType customValue;
Serializable::Distinct<UserType, UserJsonConversions>::fromJson(customValue, json);
}
int main() {
// attempt<double>();
attempt<CustomType1>();
}
在我的案例中如何正确使用SFINAE?
所以我解决了。首先,我改变了主要概念
template <class UserType>
concept HasFromJsonCast = requires {
rapidjson::Value().Get<UserType>();
};
到
template <class UserType, class ExternalConverters>
concept HasFromJsonUserCast = requires (UserType& userValue) {
{ ExternalConverters::fromJson(userValue, rapidjson::Value()) } -> std::convertible_to<void>;
};
辅助类 Distinct 看起来像这样
template <class UserType, class ExternalJsonConverters>
class Distinct {
using FromJsonFuncPtrType = void(*)(UserType&, const rapidjson::Value&);
using ToJsonFuncPtrType = rapidjson::Value(*)(const UserType&);
static constexpr auto GetFromJsonConverter() -> FromJsonFuncPtrType
{
if constexpr(HasFromJsonUserCast<UserType, ExternalJsonConverters>)
{ return &ExternalJsonConverters::fromJson; }
else
{ return &fromJsonInternal<UserType>; };
}
static constexpr auto GetToJsonConverter() -> ToJsonFuncPtrType
{
if constexpr(HasToJsonUserCast<UserType, ExternalJsonConverters>)
{ return &ExternalJsonConverters::toJson; }
else
{ return &toJsonInternal<UserType>; };
}
public:
inline static constexpr const FromJsonFuncPtrType fromJson = GetFromJsonConverter();
inline static constexpr const ToJsonFuncPtrType toJson = GetToJsonConverter();
};
因此调用者可以自由地以一致的方式序列化他的类型和内部类型
template <class UserType>
inline auto attempt()
{
rapidjson::Value json;
UserType val;
Converters::Distinct<UserType, UserJsonConversions>::fromJson(val, json);
Converters::Distinct<UserType, UserJsonConversions>::toJson(val);
}
inline void f()
{
attempt<sf::Uint32>();
attempt<sf::VideoMode>();
}