1

The following code works as designed with g++ 9.3.1 and the old concepts TS. But I haven't gotten it to work with g++ 10.3.1 and the C++ core language version of concepts:

#if __cpp_concepts < 201707
#  define CONCEPT concept bool
#else
#  define CONCEPT concept
#endif

template<class T>
CONCEPT AcceptsEvents = requires (T t) {
  t.OnEvent();
};

template <AcceptsEvents T>
struct Inner { };

struct Outer {
  void OnEvent();
  Inner<Outer> inner;
};

int main() {
  Outer out;
}

Here's my g++ 9.3.1 compilation with concepts TS:

$ g++ -std=c++2a -fconcepts concepts.cpp

It builds with no errors.

Here's my g++ 10.3.1 compilation with the C++ concepts core language feature:

$ g++ -std=c++2a -fconcepts-diagnostics-depth=2 concepts.cpp

This fails to compile with the following paraphrased error message:

note: constraints not satisfied
required for the satisfaction of 'AcceptsEvents<T>' [with T = Outer]
 in requirements with 'T t' [with T = Outer]
note: the required expression 't.OnEvent()' is invalid, because
error: 't' has incomplete type

I've read both the concepts TS and core language concepts pages at cppreference.com in detail, and I've read this SO answer in an effort to coax class Outer into a complete type. Any suggestions for design improvements to get the 10.3.1 version working like the old 9.3.1 version was?

Barry
  • 286,269
  • 29
  • 621
  • 977
dave_k_smith
  • 655
  • 1
  • 7
  • 22
  • 1
    any reason why you want it to work with the old concepts TS? – bolov Mar 17 '22 at 01:20
  • @bolov No, I just had the code working in g++ 9.3.1 (which didn't yet support C++20 core language concepts), and my code stopped building when I upgraded to g++ 10.3.1 recently. – dave_k_smith Mar 17 '22 at 01:23
  • then it's a red herring. It's fine to mention it, but don't lead with it. I would make the question strictly about the standard concepts. Then if you want you can mention it used to work in the old concepts TS, but as I've said, I don't think it's that relevant. Imho – bolov Mar 17 '22 at 01:31
  • I think you can do it with a static_assert: `template struct Inner { static_assert(AcceptsEvents); };` but I haven't checked – Artyer Mar 17 '22 at 01:39
  • @Artyer, that gives me `error: static assertion failed` with the same eventual detailed explanation (`error: 't' has incomplete type`) – dave_k_smith Mar 17 '22 at 02:14
  • 1
    For what it's worth, I think the fact that this compiled on gcc 9.3 with the TS was a gcc bug, not something about the TS having different rules. – Barry Mar 17 '22 at 15:29

2 Answers2

0

You can separate the event logic. Depending on your data structures this could make sense or not.

Inheritance

struct Event
{
    void OnEvent();
};

struct Outer : Event {
  Inner<Event> inner;
};

Composition

struct Event
{
    void OnEvent();
};

struct Outer {
  Event evt;
  Inner<Event> inner;
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • I moved away from inheritance intentionally towards concepts, because under extremely stressing data loads, the concepts approach proved faster (using old concepts TS and g++ 9.3.1). I can't use your composition suggestion, because my OnEvent() method needs access to Outer's private data. – dave_k_smith Mar 17 '22 at 02:20
0
auto OnEvent(auto& t)->decltype(t.OnEvent()){
  return t.OnEvent();
}

template<class T>
concept AcceptsEvents = requires (T t) {
  OnEvent(t);
};

template <AcceptsEvents T>
struct Inner { };

struct Outer;
void OnEvent(Outer&);

struct Outer {
  void OnEvent();
  Inner<Outer> inner;
};

void OnEvent(Outer&o){o.OnEvent();}

that should work. Not tested, little practical experience with concepts, so it could be wrong; but seems right to my head-compiler.

I changed the concept from requireing a method to a function call. Then added a trampoline template that calls the method. So it checks both; and existing types (when complete) should work.

This lets us define a free function on an incomplete Outer object, so Outer passes the test.

Throw in some namespaces and we can even call the trampoline.

bolov
  • 72,283
  • 15
  • 145
  • 224
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524