SFINAE / enable_if是否基于字符串参数的内容? [英] SFINAE/enable_if based on the contents of a string parameter?

查看:80
本文介绍了SFINAE / enable_if是否基于字符串参数的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法解决以下问题。我什至都不知道该怎么办。

I can not get my head around the following problem. I don't even really know how I could approach it.

考虑以下代码:

struct fragment_shader {
    std::string mPath;
};

struct vertex_shader {
    std::string mPath;
};

template <typename T>
T shader(std::string path) { 
    return T{ path };
}

要创建不同的结构,我可以编写以下内容:

To create the different structs, I can write the following:

auto fragmentShader = shader<vertex_shader>("some_shader.frag");
auto vertexShader = shader<fragment_shader>("some_shader.vert");

我想知道,是否有可能让编译器根据<$找出类型c $ c> path 参数传递给 shader 函数,因此我只需要编写:

I am wondering, if it is possible to let the compiler figure out the type based on the path parameter which is passed to the shader function, so I would only have to write:

auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");

,由于文件结尾为 .frag,因此类型 fragment_shader 会被推断出来,对于以 .vert结尾的路径,会推断出 vertex_shader

and because of the file ending ".frag", the type fragment_shader would be inferred, and for a path ending with ".vert", vertex_shader would be inferred.

有可能吗?

我在 enable_if 上阅读了一下,但实际上我没有想出如何利用它来实现我想要实现的目标。我会尝试如下操作:

I was reading up a bit on enable_if, but actually I have no idea how I could use that to achieve what I am trying to achieve. I would try something like follows:

template<> 
typename std::enable_if<path.endsWith(".frag"), fragment_shader>::type shader(std::string path) {
    return fragment_shader{ path };
}

template<> 
typename std::enable_if<path.endsWith(".vert"), vertex_shader>::type shader(std::string path) {
    return vertex_shader{ path };
}

但是显然,这不能编译。

But obviously, this doesn't compile. It's just to make clear what I am trying to do.

推荐答案

如果在编译时知道所有路径,则有解决方案。事实证明,使用静态链接声明的固定大小 char 数组可用作模板参数(与字符串文字相反),因此可以使函数返回两个取决于模板参数的不同类型:

If all paths are known at compile time, I have a solution. It turns out that fixed size char arrays that are declared with static linkage can be used as template arguments (as opposed to string literals), and thus you can make a function return two different types depending on that template argument:

这是一个辅助函数,可以在编译时确定文件结尾是否为 .frag (您可能希望具有与 .vert 等效的功能):

This is a helper function that can determine at compile time if the file ending is .frag (you may want to have an equivalent function for .vert):

template <std::size_t N, const char (&path)[N]>
constexpr bool is_fragment_shader()
{
    char suf[] = ".frag";
    auto suf_len = sizeof(suf);

    if (N < suf_len)
        return false;

    for (int i = 0; i < suf_len; ++i)
        if (path[N - suf_len + i] != suf[i])
            return false;

    return true;
}

此函数根据文件结尾返回两种不同的类型。当您用 C ++ 17 标记问题时,我使用了 if constexpr 而不是 enable_if 我发现它更具可读性。但是通过 enable_if 具有两个重载也可以工作:

This function returns two different types depending on the file ending. As you tagged the question with C++17, I used if constexpr instead of enable_if which I find much more readable. But having two overloads via enable_if will work, too:

template <std::size_t N, const char (&path)[N]>
auto shader_impl()
{
    if constexpr (is_fragment_shader<N, path>())
        return fragment_shader{ path };
    else
        return vertex_shader{ path };
}

最后,要使用它,您需要执行以下操作:

And finally, to use it, you need to do this:

static constexpr const char path[] = "some_shader.frag"; // this is the important line
auto frag = shader_impl<sizeof(path), path>();

这当然有点烦人。如果可以使用宏,则可以定义一个定义包含静态字符串的lambda并立即执行该宏,如下所示:

This is of course a little annoying to write. If you are OK with using a macro, you can define one that defines a lambda holding the static string and executes that immediately like so:

#define shader(p) \
[]{ \
    static constexpr const char path[] = p; \ // this is the important line
    return shader_impl<sizeof(path), path>(); \
}() \

然后,调用语法随您所愿:

Then the call syntax is just as you want it:

auto frag = shader("some_shader.frag");
static_assert(std::is_same_v<decltype(frag), fragment_shader>);

auto vert = shader("some_shader.vert");
static_assert(std::is_same_v<decltype(vert), vertex_shader>);

请找到完整的示例此处

事实证明,仅MSVC如果在全局命名空间中声明了 char 数组作为模板参数,我能想到的最好的解决方案是在此声明所有需要的路径。

As it turns out that MSVC only allows char arrays as template arguments if they are declared in the global namespace, the best solution I can think of is to declare all needed paths just there.

static constexpr char some_shader_frag[] = "some_shader.frag";
static constexpr char some_shader_vert[] = "some_shader.vert";

如果您稍稍更改宏,则调用看起来仍然很不错(尽管必须在其他地方声明字符串仍然是大PITA,当然):

If you slightly alter the macro, the calls can still look quite nice (although having to declare the strings elsewhere remains being a big PITA, of course):

#define shader(p) \
[]{ \
    return shader_impl<sizeof(p), p>(); \
}() \

void test()
{
    auto frag = shader(some_shader_frag);
    static_assert(std::is_same_v<decltype(frag), fragment_shader>);

    auto vert = shader(some_shader_vert);
    static_assert(std::is_same_v<decltype(vert), vertex_shader>);
}

查看它的运行方式此处

在VS 2019版本16.4(msvc v19.24)中已修复: https ://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html

This issue has been fixed in VS 2019 version 16.4 (msvc v19.24): https://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html

请参见此处

这篇关于SFINAE / enable_if是否基于字符串参数的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆