SFINAE/enable_if 基于字符串参数的内容?
SFINAE/enable_if based on the contents of a string parameter?
我无法解决以下问题。我什至不知道该如何处理它。
考虑这段代码:
struct fragment_shader {
std::string mPath;
};
struct vertex_shader {
std::string mPath;
};
template <typename T>
T shader(std::string path) {
return T{ path };
}
要创建不同的结构,我可以编写以下内容:
auto fragmentShader = shader<vertex_shader>("some_shader.frag");
auto vertexShader = shader<fragment_shader>("some_shader.vert");
我想知道,是否可以让编译器根据传递给 shader
函数的 path
参数找出类型,所以我只需要写:
auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");
并且由于文件以“.frag”结尾,类型将被推断为 fragment_shader
,对于以“.vert”结尾的路径,将推断出 vertex_shader
。
这可能吗?
我阅读了一些关于 enable_if
的内容,但实际上我不知道如何使用它来实现我想要实现的目标。我会尝试如下操作:
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 };
}
但显然,这无法编译。这只是为了弄清楚我要做什么。
Is that possible?
简答:没有。
长答案。
C++ 是静态类型语言,编译器需要在编译时决定函数的 returned 类型。
你的情况是
auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");
您正在尝试从同一函数获取两种不同的 return 类型,并根据 run-time 已知值确定 return 类型。
我知道 "some_shader.frag"
是编译时已知的 char const [17]
,但问题是 shader()
也接收 std::string
仅在 run-time 时已知
std::string s;
std::cin >> s;
auto foo = shader(s); // which type, in this case, at run-time ?
如果在编译时所有路径都已知,我有一个解决方案。事实证明,使用静态链接声明的固定大小 char
数组可以用作模板参数(与字符串文字相反),因此您可以根据需要使函数 return 成为两种不同的类型模板参数:
这是一个辅助函数,可以在编译时确定文件结尾是否为 .frag
(您可能需要 .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;
}
此函数 return 有两种不同的类型,具体取决于文件结尾。当您用 C++17
标记问题时,我使用 if constexpr
而不是 enable_if
,我发现后者更具可读性。但是通过 enable_if
进行两次重载也可以工作:
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 };
}
最后,要使用它,您需要执行以下操作:
static constexpr const char path[] = "some_shader.frag"; // this is the important line
auto frag = shader_impl<sizeof(path), path>();
这样写当然有点烦。如果您可以使用宏,则可以定义一个定义一个包含静态字符串的 lambda 并立即执行的宏,如下所示:
#define shader(p) \
[]{ \
static constexpr const char path[] = p; \ // this is the important line
return shader_impl<sizeof(path), path>(); \
}() \
那么调用语法就如你所愿:
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>);
请找到一个完整的示例 here。
编辑:
事实证明,MSVC 只允许 char
数组作为模板参数,如果它们是在全局命名空间中声明的话,我能想到的最好的解决方案是在那里声明所有需要的路径。
static constexpr char some_shader_frag[] = "some_shader.frag";
static constexpr char some_shader_vert[] = "some_shader.vert";
如果您稍微改变宏,调用看起来仍然相当不错(当然,必须在别处声明字符串仍然是一个很大的 PITA):
#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>);
}
看到它工作 here。
编辑 2:
此问题已在 VS 2019 版本 16.4 (msvc v19.24) 中修复:https://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html
看到它工作 here。
我无法解决以下问题。我什至不知道该如何处理它。
考虑这段代码:
struct fragment_shader {
std::string mPath;
};
struct vertex_shader {
std::string mPath;
};
template <typename T>
T shader(std::string path) {
return T{ path };
}
要创建不同的结构,我可以编写以下内容:
auto fragmentShader = shader<vertex_shader>("some_shader.frag");
auto vertexShader = shader<fragment_shader>("some_shader.vert");
我想知道,是否可以让编译器根据传递给 shader
函数的 path
参数找出类型,所以我只需要写:
auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");
并且由于文件以“.frag”结尾,类型将被推断为 fragment_shader
,对于以“.vert”结尾的路径,将推断出 vertex_shader
。
这可能吗?
我阅读了一些关于 enable_if
的内容,但实际上我不知道如何使用它来实现我想要实现的目标。我会尝试如下操作:
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 };
}
但显然,这无法编译。这只是为了弄清楚我要做什么。
Is that possible?
简答:没有。
长答案。
C++ 是静态类型语言,编译器需要在编译时决定函数的 returned 类型。
你的情况是
auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");
您正在尝试从同一函数获取两种不同的 return 类型,并根据 run-time 已知值确定 return 类型。
我知道 "some_shader.frag"
是编译时已知的 char const [17]
,但问题是 shader()
也接收 std::string
仅在 run-time 时已知
std::string s;
std::cin >> s;
auto foo = shader(s); // which type, in this case, at run-time ?
如果在编译时所有路径都已知,我有一个解决方案。事实证明,使用静态链接声明的固定大小 char
数组可以用作模板参数(与字符串文字相反),因此您可以根据需要使函数 return 成为两种不同的类型模板参数:
这是一个辅助函数,可以在编译时确定文件结尾是否为 .frag
(您可能需要 .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;
}
此函数 return 有两种不同的类型,具体取决于文件结尾。当您用 C++17
标记问题时,我使用 if constexpr
而不是 enable_if
,我发现后者更具可读性。但是通过 enable_if
进行两次重载也可以工作:
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 };
}
最后,要使用它,您需要执行以下操作:
static constexpr const char path[] = "some_shader.frag"; // this is the important line
auto frag = shader_impl<sizeof(path), path>();
这样写当然有点烦。如果您可以使用宏,则可以定义一个定义一个包含静态字符串的 lambda 并立即执行的宏,如下所示:
#define shader(p) \
[]{ \
static constexpr const char path[] = p; \ // this is the important line
return shader_impl<sizeof(path), path>(); \
}() \
那么调用语法就如你所愿:
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>);
请找到一个完整的示例 here。
编辑:
事实证明,MSVC 只允许 char
数组作为模板参数,如果它们是在全局命名空间中声明的话,我能想到的最好的解决方案是在那里声明所有需要的路径。
static constexpr char some_shader_frag[] = "some_shader.frag";
static constexpr char some_shader_vert[] = "some_shader.vert";
如果您稍微改变宏,调用看起来仍然相当不错(当然,必须在别处声明字符串仍然是一个很大的 PITA):
#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>);
}
看到它工作 here。
编辑 2:
此问题已在 VS 2019 版本 16.4 (msvc v19.24) 中修复:https://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html
看到它工作 here。