Hacker News new | past | comments | ask | show | jobs | submit login
User-defined class qualifiers in C++23 (bannalia.blogspot.com)
23 points by ibobev on Aug 19, 2023 | hide | past | favorite | 8 comments



I must be the only one who doesn’t understand anything in that article


I understood the first code listing, but then lost interest.

In C++, classes can have member functions. Classes can also be derived from each other. A class D derived from B inherits all of B's member functions.

So, when you have an instance of D calling one of its B-derived member functions, the B member function sees "this" as a pointer to a B, even though it is a pointer to a "D". That's fine because pointers to D are convertible to pointers to B.

What is proposed here is a way for one of B's member functions to say "this member function can be called only when the current object ('this') has a particular type." In the example, the particular type is "mut<B>", which is analogous to D.

My lizard brain doesn't see how this might be useful.


> how this might be useful

As always the goal in c++ is to put as much of the specification of your program in the type system and make sure that invalid state is unrepresentable (e.g. does not compile).

This allows to very easily (compared to how'd you do it today) add custom compile-time checks to some variable.


The C++ designers are too obsessed with complexity. Complexity is not a good thing.


This article is not about C++ design as I read it, but rather about implementing a theoretical concept (qualifier subtyping) half-way using new features.

I do not think that this is a real complex feature or something to do with complexity of language design in particular, it is rather like some weird tricks in python to implement obscure feature so they integrate well with general syntax. Or eg. like implementing something likes Duff's device in C to mock co-routines. (Having said that: to me the new C++ features are all too complicated. As someone coming from C C++ really now feels like its own language)


It's a bit of a struggle, and I've been using C++ since '92. My problem is I haven't used C++23 yet. But I'll give my best guess. You might want to skip to the end of my comment and then work backwards. ;)

The first bit on template mut<T>:

mut<X> is a subclass of X so inherits all the methods like foo() and bar(). as_const() converts an X or mut<X> to X& so you can't call bar() because bar's this is of type mut<X>&. This is like Rust and other languages where variables are immutable by default until you declare a mutable reference (mut<> in this example). The bar method is using a new (C++23 I think) feature that unifies function call syntax so that "x.bar()" is the same as "bar(x)". So "void bar(this mut<X>&)" is explicitly declaring the type of "this" instead of the default X*.

The second part is about possibly extending the Boost Metaprogramming Library to support new qualifiers (typical C++ qualifiers being "const" and "volatile"). That library does compile time computation using template metaprogramming. Essentially, the compiler has to be so smart to infer types that you can use recursively defined templates and template specialisation to compute values (not just types) at compile time using recursive algorithms.

Here, the author is using C++23 concepts. Those are constraints on template parameters and the syntax is to use the concept name where you would normally write "class" or "typename" in a template declaration, hence "template<is_mut Self> void bar(this Self&&) {}".

The author is using variadic templates (all the ... stuff) to define new concepts that reflect on whether a type has a specified qualifier (for his own idea of what a qualifier is). "is_mut" is the concept (read "requirement for template instantiation") that a type T has the "mut" qualifier, and "is_synchronized" is the concept that the type has the "synchronized" qualifier.

The "qualified<T,...>" variadic concept takes a type and a list of qualifier types and is true if T is in the list of qualifiers[1] specified by the "...". The implementation of concept is_mut is "qualified<T,mut>" and the list of qualifiers in that case is just "mut".

The implementation of concept qualified<T,...> expects T::qualifier_list to be the list of qualifiers bound to T. "access<T,...>" creates a new type that inherits from T and has all the qualifiers in the "..." list, and that new type stores the list of qualifiers as typedef member "qualifier_list".

When you instantiate "access<T,Q1,Q2>" for qualifiers Q1 and Q2, you are defining a new type that provides access to the methods of T that require access to all the qualifiers Q1 and Q2. Or more qualifiers if you instantiate "access<>" with more.

So, to bring it back to the initial point: C++ has a couple of qualifiers: const and volatile. You can't call a non-"const" method of a class through a "const"-qualified "this" pointer. That effectively partitions the interface of the C++ class into disjoint sets of "const" and non-"const" methods. And a similar thing is happening here, where the author is using "access<X,Q1>" to specify the qualifier Q1 applied to the this pointer, which then allows him to call those methods that require the Q1 qualifier.

[1] https://www.boost.org/doc/libs/1_83_0/libs/mp11/doc/html/sim...


Great example of how C++ is closer and closer to the ideal of a dependant-type language and is probably ignored by some FP purists


With every new feature C++ becomes harder to think in, although I agree it has a growing subset of a quality language.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: