【问题标题】:Check presence of custom overload of function when template overload is available当模板重载可用时检查是否存在自定义函数重载
【发布时间】:2017-04-30 21:36:17
【问题描述】:

我正在设计一个实用程序标头,可以将二进制数据从sf::InputStream 中抽取出来。为了便于使用,它包含一个函数名称readFromStream,它有很多(模板化和非模板化)重载,用于自动反序列化标准布局类型和类型复合,如向量、元组和我定制设计的@987654327 @ 班级。完整的实现可以在这里找到: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);
}

所以,总是有一个函数来进行序列化,而不仅仅是我想要的那个。我想在这里解决的问题是:如何检测到类型 T 的非默认 readFromString 的存在,以禁用 readFromString 的优化版本 std::vector&lt;T&gt;?

我尝试了一些技巧。我无法将优化限制为 POD 类型,因为我在某些要反序列化的类型上使用了 sf::Vector2&lt;T&gt;,这不是 POD。我尝试比较使用非模板化和模板化函数时得到的函数地址,例如:

using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;

但是,奇怪的是,它不起作用。我研究了很多解决方案,但没有一个能适应我的需要。也许这在 C++ 中是不可能的,我将不得不求助于“标记”我不想优化的类型。或者,也许这是我没有想到的一些晦涩的模板。我怎么能这样做?

【问题讨论】:

  • 首先你需要一些补充的std::enable_if 子句,因为std::is_standard_layout&lt;T&gt; 可能不是is_optimization_viable&lt;T&gt; 的反义词,而且你手头有歧义。
  • @HenriMenke 是的,这不是相反的;实际上,后一种特征需要前者才能满足我的需要。我可能没有正确描述我想要什么。
  • 为什么要“禁用 readFromString 的优化版本”?如果要检查是否存在异常序列化函数,则检测代码与检查是否存在常规序列化函数的检测代码基本相同,只是调整了签名。
  • @VTT 有一些标准布局函数(如 std::string、我自己的 GameObjectDescriptor 以及基本上所有带有指针的函数)应该被快速反序列化位图;所以我想检查是否存在特定的重载,以便使用它更安全。

标签: c++ templates c++14 template-meta-programming sfinae


【解决方案1】:

据我了解,您的问题是:

is_optimization_viable<T>;

可以定义为:

template<typename T>
using is_optimization_viable<T> = std::is_standard_layout<T>;

但事实上,对于T 的某些值,标准布局 您仍然需要自定义bool readFromStream(sf::InputStream &amp;stream, T &amp;value), 重载意味着它们优化可行。

既然你必须编写这些自定义重载,你知道那些 T 的特殊值是。假设它们是XYZ 类型。 然后您可以将特征定义为:

#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;
};

感谢您宁愿避免对 硬编码类型列表XYZ,并且以某种方式更喜欢 SFINAE-probe 呼叫readFromStream(s, t) 是否会呼叫其中一个 一些 std::declval-ed st 的自定义重载。

但那是海市蜃楼。你告诉我们,会有一些过载 readFromStream(s, t) 将编译 t 的任何类型。 如果是这样,SFINAE 探测将始终告诉您是的, readFromStream(s, t) 将编译 - 对于任何 T,将其作为 t 的非限定类型。和你 仍然需要在编译时决定 T 是否是其中之一 自定义类型,如果不是,是否为标准布局。

这就是问题所在。判断T 是否是其中之一 您必须测试它与任何一种的身份的自定义类型 如图所示,或者你必须找到一个独立于它们的特征 所有且仅由自定义类型满足的标识。正如你 不要告诉我们那些自定义类型是什么,我不能建议任何这样的特征, 但如果你找到一个,那么 it 将定义或替换 has_custom_read_from_stream&lt;T&gt;

顺便说一句,我赞同@NirFriedman 的评论:std::standard_layout 真的是你的意思吗?

【讨论】:

  • 如果不是那个讨厌的sf::Vector2&lt;T&gt;,我想is_trivial 甚至is_pod 就足够了,但这是我无法改变的依赖。
猜你喜欢
  • 2018-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多