My goal for this code is to constrain a function parameter's passed value to a select few possibilities, being checked at compile time in C++20. My original broken first attempt looked something like this:
template<GLenum shader_type>
requires requires
{
shader_type == GL_VERTEX_SHADER ||
shader_type == GL_FRAGMENT_SHADER ||
shader_type == GL_COMPUTE_SHADER;
}
GLuint create_shader(std::filesystem::path const& shader_path, GLenum shader_type)
This would not work, and I'm not going to argue with the compiler, but I'm including it here to help illustrate my aims for this declaration. My spec:
GLenum
to a set of three currently supported types. Preferably in an ad-hoc way, e.g. not requiring much boilerplate.create_shader("my/path", GL_FRAGMENT_SHADER)
. No templates in sight.The main issues with this code were:
declaration of 'shader_type' shadows a template parameter
. I can see this is because of the specification of GLenum shader_type
in both the template parameters and function arguments, but I was hoping that somehow have the compiler might infer the type from function args.shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER || shader_type == GL_COMPUTE_SHADER
is not doing it's job at all. I could pass GL_GEOMETRY_SHADER
in with no compile-time issue. (I wish I could say the same for run-time)After looking at this Question I came up with this next:
template<GLenum shader_type>
requires requires
{
requires shader_type == GL_VERTEX_SHADER ||
shader_type == GL_FRAGMENT_SHADER ||
shader_type == GL_COMPUTE_SHADER;
}
GLuint create_shader(std::filesystem::path const& shader_path)
This seems to work as I desire when it comes to constraining the passed value to those values, however, it does require shader_type
being passed in as a template parameter. This is fine really, but it leads me to my questions:
shader_type
as a template parameter explicitly, instead preferring a function argument?requires
? I suspect not, but it would be nice if I can cut one out.Appendix:
constexpr
and that I could probably do the check inside the function, but doing it through constraints and concepts is also a bit of exploration for me, as a learner.GLenum
or any of the other non-standard code:typedef unsigned int GLenum;
typedef unsigned int GLuint;
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPUTE_SHADER 0x91B9
If you wanted to constrain a template parameter, a single requires
is enough:
template <GLenum shader_type>
requires(shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER)
void foo() {}
A function parameter can be constrained like this, at the cost of requiring it to be a compile-time constant:
struct ShaderType
{
GLenum value;
consteval ShaderType(GLenum value) : value(value)
{
if (shader_type != GL_VERTEX_SHADER && shader_type != GL_FRAGMENT_SHADER)
throw "Invalid value!";
// Normally it's not a good idea to throw pointers,
// but here it just used to stop the compilation.
}
};
void foo(ShaderType type) {}