9
I have a template class taking several non-type arguments (GPIO descriptors). Auto is used to allow several different GPIO implementations to be used. These implementations can’t derive from a common base class.
Can I use a concept to restrict these arguments to ones with the right interface?
I got a concept but it’s not working, GCC is telling me "error: ‘DigitalOutput’ does not constrain a type":
template <auto gp>
concept DigitalOutput = requires() { gp.set(); gp.reset(); };
// template <auto output>
template <DigitalOutput output>
struct driver {...};
Is it possible to make this work?
2
3 Answers
Reset to default
8
There are 2 problems:
First you used the wrong syntax, which was already explained in the answer of 康桓瑋.
The next problem is, that your concepts will always fail, because your set
and reset
functions are not const
. Because the non type template parameter must always be a compile time constant, you can’t access member functions on it, which are non const.
The working example is given here:
template <auto gp>
concept DigitalOutput = requires()
{
gp.set();
gp.reset();
};
template <auto output> requires DigitalOutput<output>
struct driver
{
};
struct A
{
void set() const {} // must be const!
void reset() const {} // must be const!
};
struct B
{
};
int main()
{
A a;
driver<a> da;
B b;
driver<b> db; // fails as expected
}
For the problem of the const
of the member functions, we can do the following trick to overcome that problem:
#include <utility>
#include <type_traits>
template <auto gp>
concept DigitalOutput = requires()
{
// create a new value which is not const from the given non type template parm:
std::remove_const_t<decltype(gp)>().set();
std::remove_const_t<decltype(gp)>().reset();
};
template <auto output> requires DigitalOutput<output>
struct driver
{
};
struct A
{
void set() {}
void reset() {}
};
struct B
{
};
int main()
{
A a;
driver<a> da;
B b;
driver<b> db; // fails as expected
}
10
-
Having a
set
andreset
member functions declared asconst
is a bit strange, though.– Bob__10 hours ago
-
1
I see. May I suggest a slightly less verbose approach? The error message seems, IMHO, more readable.
– Bob__9 hours ago
-
1
"But we can solve that problem …" which can be simplified to
auto(gp).set();
in C++23.– 康桓瑋9 hours ago
-
1
"Bad that the combination of auto and the var declaration in the parameter list is not working" which can be done by
requires(decltype(auto(gp)) g) { }
.– 康桓瑋9 hours ago
-
1
@康桓瑋 Not relying on a lib can be a good point for some embedded platforms, good point! "Not working" was for me in the sense of
requires( auto(gp) gp )
. Interesting discussions with all the new stuff of C++23…– Klaus9 hours ago
5
The syntax for your concept is not correct, here is a working example.
#include <cstdint>
#include <concepts>
template <typename gp_t>
concept DigitalOutput = requires(gp_t gp)
{
gp.set();
gp.reset();
};
struct output_t
{
void set() {};
void reset() {};
};
struct not_output_t
{
};
void fn(DigitalOutput auto output)
{
}
int main()
{
output_t output;
not_output_t not_output;
fn(output);
// fn(not_output); does not compile
return 0;
}
2
-
For me it is far away from the original code. OP asks for non type template argument while your example works with type check. Maybe it is not what OP asks for.
– Klaus11 hours ago
-
@Klaus Indeed could be this restricts a function argument not a struct template argument (me bad)
– Pepijn Kramer11 hours ago
4
Since the concept constrains the non-type template argument rather than the type, you can check it by specifying it as a template parameter of the concept through the requires
keyword
template <auto output> requires DigitalOutput<output>
struct driver { };
The problem is that there is no "argument" to requires so, it is not constraining anything. godbolt.org/z/zTKdnT878 . Otherwise, there is no problem with the second part.
11 hours ago
I'm pretty certain
DigitalOutput auto output
should compile and do what you are after.11 hours ago