我无法解决以下问题。我什至都不知道该怎么办。
考虑以下代码:
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");
我想知道,是否有可能让编译器根据传递给path
函数的shader
参数找出类型,所以我只需要编写:>
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 };
}
但是显然,这不能编译。只是为了弄清楚我要做什么。
答案 0 :(得分:4)
如果在编译时知道所有路径,我有解决方案。事实证明,使用静态链接声明的固定大小char
数组可以用作模板参数(与字符串文字相反),因此您可以使函数根据该模板参数返回两种不同的类型:< / p>
这是一个帮助函数,可以在编译时确定文件结尾是否为.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;
}
此函数根据文件结尾返回两种不同的类型。当您用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。
答案 1 :(得分:0)
有可能吗?
简短回答:否。
长答案。
C ++是一种静态类型的语言,编译器需要在编译时确定函数的返回类型。
与您有关的情况
auto fragmentShader = shader("some_shader.frag");
auto vertexShader = shader("some_shader.vert");
您试图从同一个函数中获取两种不同的返回类型,并从运行时已知值中确定返回类型。
我知道"some_shader.frag"
是在编译时已知的char const [17]
,但是问题在于shader()
还会收到仅在运行时知道的std::string
std::string s;
std::cin >> s;
auto foo = shader(s); // which type, in this case, at run-time ?