Home > Net >  How to do a logical 'or' of requirements in a single concept?
How to do a logical 'or' of requirements in a single concept?

Time:10-24

I have the following case


template<typename Class>
concept has_member = requires (Class t)
{ 
    // How can I write only either of the following conditions be satisfied?
    {t.isInterface() }->std::same_as<bool>;
    // or
    {t.canInterface() }->std::same_as<bool>;
    // or
    // ... more conditions!
};

struct A {
    bool isInterface() const { return true; }
};

struct B {
    bool canInterface() const { return true; }
};

void foo(const has_member auto& A_or_B)
{
    // do something
}

int main() {
    foo(A{}); // should work
    foo(B{}); // should work
}

Like I mentioned in the comments, I would like to logically or the requirements (in a single concepts), so that the class A and B can be passed to the doSomething().

As per my knowledge, the the current concept is checking all the requirements, that means a logical and. If I take it apart to different concepts everything works, but I would need more concepts to be written tosatify the intention.

Is it possoble to combne into one? something like pseudocode

template<typename Class>
concept has_member = requires (Class t)
{ 
    {t.isInterface() }->std::same_as<bool>  ||  {t.canInterface() }->std::same_as<bool>;
    // ... 
};

CodePudding user response:

Anytime you want to start building concepts, you should start with the point-of-use. That is, the place where you are going to use some template parameters for some purpose.

That means, you start with foo. How does foo "do something" with this interface? It can't be as simple as:

void foo(const has_member auto& A_or_B)
{
    A_or_B.isInterface();
}

That will be a compile error for some of the has_member objects. So you will need to do this:

void foo(const has_member auto& A_or_B)
{
    if constexpr(<stuff>)
      A_or_B.isInterface();
    else
      A_or_B.hasInterface();
}

Where <stuff> is some compile-time conditional check to see which actual interface the object has. You could spell out the requires clause, but that would be unnecessarily wordy. So just stick them in a concept:

template<typename T>
concept has_is_interface = requires (Class t)
{
  {t.isInterface() }->std::same_as<bool>;
};

template<typename T>
concept has_can_interface = requires (Class t)
{
  {t.canInterface() }->std::same_as<bool>;
};

And now, your question answers itself:

template<typename Class>
concept has_member =
  (has_is_interface || has_can_interface) &&
  requires (Class t)
  { 
    // ... more conditions!
  };

That having been said, this design is generally wrongheaded. The reason is that it puts a lot of verbosity at the point of use.

What you probably want is to have a few free functions which can call the appropriate member interface:

bool do_is_interface(has_is_interface auto const& is_if)
{
  return is_if.is_interface();
}

bool do_is_interface(has_can_interface auto const& is_if)
{
  return is_if.can_interface();
}

And then your concept looks like:

template<typename Class>
concept has_member =
  requires (Class t)
  { 
    { do_is_interface(t) } -> std::same_as<bool>
    // ... more conditions!
  };

CodePudding user response:

Is it possoble to combine into one?

Yes, it is possible. You can write the conjunction of two requires as follows:

template<typename Class>
concept has_member = 
    requires (Class t) { {t.isInterface() }->std::same_as<bool>; }
    || //---> like this
    requires (Class t) { {t.canInterface() }->std::same_as<bool>;};
// ... more conditions!

Demo

CodePudding user response:

You can use the following concept form stolen from the standard

#include <concepts>

template<typename Class>
concept has_member = requires (Class t) { 
  requires (requires { {  t.isInterface() } -> std::same_as<bool>; } ||
            requires { { t.canInterface() } -> std::same_as<bool>; }
            // add more functions
  );
};

which allows us to add different member functions directly in the requires-clause.

  • Related