C++20 use concept to restrict an ‘auto’ non-type template argument?

C++20 use concept to restrict an ‘auto’ non-type template argument?


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?

Share
Improve this question

2

  • 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.

    – alfC

    11 hours ago


  • I'm pretty certain DigitalOutput auto output should compile and do what you are after.

    – StoryTeller – Unslander Monica

    11 hours ago

3 Answers
3

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
}

Working example

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
}

Working example

Share
Improve this answer

10

  • Having a set and reset member functions declared as const 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…

    – Klaus

    9 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;
}

Share
Improve this answer

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.

    – Klaus

    11 hours ago

  • @Klaus Indeed could be this restricts a function argument not a struct template argument (me bad)

    – Pepijn Kramer

    11 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 { };

Share
Improve this answer



Leave a Reply

Your email address will not be published. Required fields are marked *