当模板重载可用时,检查函数的自定义重载是否存在
Check presence of custom overload of function when template overload is available
我正在设计一个实用程序 header 从 sf::InputStream
. For ease of use, is comprises a single function name, readFromStream
, that has a lot of (templated and non-templated) overloads for automatically deserializing standard-layout types and type compounds like vectors, tuples and my custom-designed grid
class. The complete implementation can be found here: 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;
}
我想为 standard-layout 类 编写一个优化版本,因为 readFromStream
没有重载,所以我们可以利用它们的内存布局并在单个 read
调用中将它们 blit:
// 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;
}
好吧,我可以使用其他答案中描述的解决方案来检测函数的存在,但有一个问题。当类型是 standard-layout 时,我有一个默认的 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);
}
所以,总是有一个函数 进行序列化,而不仅仅是我想要的那个。我想在这里解决的问题是:如何检测类型 T
的 non-default readFromString
的存在,以禁用 [=] 的优化版本22=] 对于 std::vector<T>
?
我尝试了一些技巧。我不能将优化限制为 POD 类型,因为我在某些我想反序列化的类型上使用 sf::Vector2<T>
,这不是 POD。我尝试比较使用 non-templatized 和模板化函数时获得的函数地址,例如:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
但是,奇怪的是,它没有用。我研究了 很多 的解决方案,但是 none 我可以适应我需要的东西。也许这在 C++ 中是不可能的,我将不得不诉诸 "marking" 我不想优化的类型。或者它可能是一些我没想到的晦涩模板。我该怎么做?
据我了解你的问题是:
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 探针将始终告诉您 Yes, readFromStream(s, t)
将编译 - 对于任何 T
作为 t
的非限定类型。和你
仍然必须就 T
是否是其中之一做出编译时决定
自定义类型,如果不是,是否是标准布局。
这就是问题所在。判断 T
是否是其中之一
自定义类型,您必须测试它与任何一个的身份
如图所示,它们是分离的,或者你必须找到一个独立于它们的特征
满足所有且仅自定义类型的身份。如你
不要告诉我们那些自定义类型是什么,我不能建议任何这样的特征,
但是如果你找到一个,那么 它 将定义或替换 has_custom_read_from_stream<T>
.
顺便说一句,我支持@NirFriedman 的评论:std::standard_layout
真的是你的意思吗?
我正在设计一个实用程序 header 从 sf::InputStream
. For ease of use, is comprises a single function name, readFromStream
, that has a lot of (templated and non-templated) overloads for automatically deserializing standard-layout types and type compounds like vectors, tuples and my custom-designed grid
class. The complete implementation can be found here: 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;
}
我想为 standard-layout 类 编写一个优化版本,因为 readFromStream
没有重载,所以我们可以利用它们的内存布局并在单个 read
调用中将它们 blit:
// 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;
}
好吧,我可以使用其他答案中描述的解决方案来检测函数的存在,但有一个问题。当类型是 standard-layout 时,我有一个默认的 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);
}
所以,总是有一个函数 进行序列化,而不仅仅是我想要的那个。我想在这里解决的问题是:如何检测类型 T
的 non-default readFromString
的存在,以禁用 [=] 的优化版本22=] 对于 std::vector<T>
?
我尝试了一些技巧。我不能将优化限制为 POD 类型,因为我在某些我想反序列化的类型上使用 sf::Vector2<T>
,这不是 POD。我尝试比较使用 non-templatized 和模板化函数时获得的函数地址,例如:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
但是,奇怪的是,它没有用。我研究了 很多 的解决方案,但是 none 我可以适应我需要的东西。也许这在 C++ 中是不可能的,我将不得不诉诸 "marking" 我不想优化的类型。或者它可能是一些我没想到的晦涩模板。我该怎么做?
据我了解你的问题是:
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 探针将始终告诉您 Yes, readFromStream(s, t)
将编译 - 对于任何 T
作为 t
的非限定类型。和你
仍然必须就 T
是否是其中之一做出编译时决定
自定义类型,如果不是,是否是标准布局。
这就是问题所在。判断 T
是否是其中之一
自定义类型,您必须测试它与任何一个的身份
如图所示,它们是分离的,或者你必须找到一个独立于它们的特征
满足所有且仅自定义类型的身份。如你
不要告诉我们那些自定义类型是什么,我不能建议任何这样的特征,
但是如果你找到一个,那么 它 将定义或替换 has_custom_read_from_stream<T>
.
顺便说一句,我支持@NirFriedman 的评论:std::standard_layout
真的是你的意思吗?