我正在设计一个实用程序标头,用于从sf::InputStream
中抽取二进制数据。为了便于使用,它包含一个函数名readFromStream
,它有很多(模板化和非模板化)重载,用于自动反序列化标准布局类型和类型化合物,如矢量,元组和我的自定义设计grid
课程。完整的实施可以在这里找到:https://github.com/JoaoBaptMG/ReboundTheGame/blob/master/MainGame/utility/streamCommons.hpp
所以,我已经定义了一个重载readFromStream
,它通过递归再次调用readFromStream
来抽出任何类型的向量:
template <typename T, typename std::enable_if<!is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
for (auto &val : newVal)
if (!readFromStream(stream, val))
return false;
newVal.swap(value);
return true;
}
我想为标准布局类编写一个优化版本,因为readFromStream
没有重载,所以我们可以利用它们的内存布局并将它们插入单个read
电话:
// trait is_optimization_viable is what I'm having trouble to write
template <typename T, typename std::enable_if<is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
if (stream.read(newVal.data(), size*sizeof(T)) != size*sizeof(T))
return false;
newVal.swap(value);
return true;
}
好吧,我可以使用其他答案中描述的解决方案来检测函数的存在,但是有一个问题。当类型是标准布局时,我有一个默认的readFromStream
,其内容如下:
template <typename T, typename std::enable_if<std::is_standard_layout<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, T& value)
{
return stream.read((void*)&value, sizeof(T)) == sizeof(T);
}
所以,总是有一个函数来完成序列化,而不仅仅是我想要的那个。我想在这里解决的问题是:如何检测类型为readFromString
的非默认T
是否存在,以便禁用readFromString
的优化版本std::vector<T>
的
我试图拉一些技巧。我无法将优化限制为POD类型,因为我在某些我要反序列化的类型上使用sf::Vector2<T>
,这不是POD。我尝试比较我使用非模板化和模板化函数时得到的函数地址,如:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
但是,奇怪的是,它没有用。我研究了很多的解决方案,但没有一个我可以适应我需要的东西。也许这在C ++中是不可能的,我将不得不求助于“标记”我不想优化的类型。或许这是一些我没有想到的模糊模板。我怎么能这样做?
答案 0 :(得分:1)
据我所知,你的问题是:
is_optimization_viable<T>;
可以通过以下方式定义:
template<typename T>
using is_optimization_viable<T> = std::is_standard_layout<T>;
但是对于T
的某些值是标准布局的事实
尽管如此,您仍需要自定义bool readFromStream(sf::InputStream &stream, T &value)
,
过载意味着他们不优化 - 可行。
因为你必须编写这些自定义重载,你知道那些
T
的特殊值。说它们是X
,Y
,Z
类型。
然后您可以将特征定义为:
#include <type_traits>
template<typename T, typename ...Us>
struct is_one_of;
template<typename T>
struct is_one_of<T> {
static constexpr bool value = false;
};
template<typename T, typename First, typename ...Rest>
struct is_one_of<T,First,Rest...> {
static constexpr bool value =
std::is_same<T,First>::value || is_one_of<T,Rest...>::value;
};
// ^ C++17: `std::disjunction` does the job
template<typename T>
using has_custom_read_from_stream = is_one_of<T,X,Y,Z>;
template<typename T>
struct is_optimization_viable {
static constexpr bool value = std::is_standard_layout<T>::value &&
!has_custom_read_from_stream<T>::value;
};
我很欣赏你宁愿避免持续维护
硬编码的类型列表X
,Y
,Z
,并且更喜欢SFINAE-probe
是否呼叫readFromStream(s, t)
将是对其中一个呼叫
某些std::declval
-ed s
和t
的自定义重载。
但那是海市蜃楼。你告诉我们,会有一些重载
readFromStream(s, t)
将编译t
的任何类型。
如果是这样,SFINAE探针将始终告诉您是, readFromStream(s, t)
将编译 - 任何T
作为t
的非限定类型。你呢
仍然必须做出编译时决定T
是否是其中之一
自定义类型,如果没有,是否为标准布局。
这就是问题所在。判断T
是否是其中之一
自定义类型,您必须测试它与任何一个身份
如图所示,它们是分离的,或者你必须找到一个独立于它们的特征
所有和唯一的自定义类型都满足的身份。就像你一样
不要告诉我们那些自定义类型是什么,我不能建议任何这样的特性,
但是如果你找到一个它将定义或替换has_custom_read_from_stream<T>
。
顺便说一句,我是第二个@NirFriedman的评论:std::standard_layout
真的是你的意思吗?