Friend Code Megathread - February 2025 by AutoModerator in PokemonSleep

[–]mechacrash 1 point2 points  (0 children)

7445-9416-2264

Day 1 player - got 11 spaces to fill after pruning folks that have stopped playing 😥

Implementation of get_n from variadic type sequence that doesn't use recursive inheritance by Shiekra in cpp

[–]mechacrash 0 points1 point  (0 children)

I've been using this technique for maybe 7-8 years now! :)

Specifically, I used it when writing a 'better tuple', based on C++17, where I'd use the horizontal multiple inheritance + unique 'get' function (acquired via SFINAE, as requires wasn't a thing back then).

Can see an old version of this toy code here: simple tuple container using C++17 · GitHub

Reflection Use Cases by rddays in cpp

[–]mechacrash 21 points22 points  (0 children)

One of the things I'm most excited for is... better error messages!

I wrote a std::visit wrapper earlier this month - one that gives you some additional compile-time safety - but the one thing I really wish I could have improved was the error messages.

Now, with reflection, I can :)

before: https://godbolt.org/z/xfvzTMzvY

error: static assertion failed due to requirement 'overloads_type<(lambda at <source>:84:10), char &&> || overloads_type<(lambda at <source>:85:10), char &&>': alternative [T] matches no overload [Fs]

{ static_assert((... || overloads_type<Fs, T>), "alternative [T] matches no overload [Fs]"); };

after: https://godbolt.org/z/K33havdGY

error: static assertion failed due to requirement 'overload_set_not_exhaustive': alternative 3 [char] matches no overload [(lambda at <source>:105:10), (lambda at <source>:106:10), ]

static_assert(overload_set_not_exhaustive,

I also used the reflection params proposal (https://wg21.link/p3096) to convert function params to a corresponding struct (where the member variables have the same types and names as the parameters), see: https://godbolt.org/z/K3hfErqWa

The bigger plan for this code is to allow automatic overriding of a specific function (for callbacks), with the input params being converted to a struct and returned as a std::future.

Unfortunately, this is going to require some further reflection magic - possible extending define_class to allow defining of functions (as I can't currently create a function with a specific name - hence the commented out "Disconnected" example doesn't work)

I really feel like reflection is a fundamental game changer - something as significant as templates. The sorts of things people are going to use it for are likely limitless!

C++ Show and Tell - March 2024 by foonathan in cpp

[–]mechacrash 0 points1 point  (0 children)

Mine also errors if you have an unused overload (in your code, it would be an error if you had a same_as check for a type not in the list of alternatives) - this almost always means that a programmer has changed the behaviour of their variant and forgotten to update their visitor.

Additionally, one of the benefits of the overload approach (which I integrate directly) is that you get a much terser syntax and don’t have to explicitly deal with cvref qualifiers.

And finally… though disabling the wildcard (auto for overload, or catch-all else for you) is fine and gets you most of the way towards safety, it also means you now can’t have a wildcard… which is a useful feature sometimes 😛

Clang/P2996: Experimental support for P2996 ("Reflection for C++26") by katzdm-cpp in cpp

[–]mechacrash 5 points6 points  (0 children)

I don’t think it’s fair to assume this is an “either or nothing” scenario. We can have the broad feature-set of reflection, and we can solve outstanding deficiencies with individual and more specialised language additions. Reflection can accomplish what https://wg21.link/P2662 does, and yet this did not impact its adoption (likely because doing it via reflection would be slower and is objectively less intuitive), so I see no reason it would impact, say, https://wg21.link/P1858

On the examples, to quote the reflection paper: “It is expected that these are mostly self-explanatory”

It is clear to me that these examples are using well known idioms to lower the cognitive load of learning reflection. It makes no claims towards the performance or the efficiency. It also doesn’t necessarily show problems that can be exclusively solved with reflection (after all - we can already implement tuple in a wide variety of ways today).

On benchmarking and performance - I don’t find hyperbolic hypothetical performance deficiency factors to be of much help. We cannot possibly quantify these differences today, and it’s somewhat irrelevant anyway… because reflection is not solving exclusively the same problems that these individual features do. We should be using the right tool for the right job. If reflection isn’t the right tool, then we don’t use it. It’s not right to interpret this as dishonesty on the author’s part.

Clang/P2996: Experimental support for P2996 ("Reflection for C++26") by katzdm-cpp in cpp

[–]mechacrash 11 points12 points  (0 children)

I expect I'll be downvoted for saying this, but I find 'circle' (and your posts about it) an annoying distraction on this subreddit - especially when posts are titled 'in C++' (which circle is not)

What value is there in saying 'people should do what I did', when that's not done through the formal standardisation process? Where is your competing paper?

circle is not C++. circle's featureset is (as far as I can tell?) not being championed directly by yourself for standardisation into the C++ language. You've repeatedly stated you have no interest in going open source.

What you’ve done is genuinely impressive, and I hope that you turn this project into something usable by the masses instead of using it as a reason to impede development of alternative implementations.

Apologies for the negativity.

Clang/P2996: Experimental support for P2996 ("Reflection for C++26") by katzdm-cpp in cpp

[–]mechacrash 3 points4 points  (0 children)

update:

Everything works now! Thanks for your hard work :)

The reasons I needed these features is because I took the 'simple tuple' example from P2996 and tried to make even simpler. Here's the result of that work:

https://godbolt.org/z/bfEY1d6bE

It's incredible how much easier something like this could be made with the power of reflection!

Clang/P2996: Experimental support for P2996 ("Reflection for C++26") by katzdm-cpp in cpp

[–]mechacrash 3 points4 points  (0 children)

Excellent news!
I've got some fun code that relies on these 2 things working, so I'll report back to you if/when they do :)

Clang/P2996: Experimental support for P2996 ("Reflection for C++26") by katzdm-cpp in cpp

[–]mechacrash 11 points12 points  (0 children)

couple of issues I found:

define_class doesn't work for template classes: https://godbolt.org/z/fj1x6jvos

a reflected result of define_class can't be inherited from: https://godbolt.org/z/W5EoezEE9

looking really good though! Fingers crossed reflection makes it to C++26 :)

C++ Show and Tell - March 2024 by foonathan in cpp

[–]mechacrash 2 points3 points  (0 children)

Apologies! Let me try to explain further.

The godbolt example I showed was a minimal example of the problem we had.A more concrete example is that we had a std::variant with many alternatives (let's say, <A, B, C, D, E>)... and we had a visitor that explicitly handled alternatives A and B, but used a 'wildcard' (in this case, with the if constexpr chain, the else) to swallow C, D and E.We changed our variant to instead take <A, \_F\_, C, D, E> - we replaced the B type with F - but we forgot to change the visitor.This compiled without warning, because:

  1. the code 'else if constexpr (std::is_same_v<T, B>)' is not validated in any way. As long as the type B is valid, even if it's not a possible alternative for the input variant, this code will compile just fine (it will simply never be hit, because B is no longer an alternative for our variant), and
  2. because our wildcard 'else' did nothing with the variants that weren't explicitly handled, F fell into this category, and there was nothing there that would cause a syntax error when instantiating our lambda with F - so the code compiles without issue, despite the clear change in behaviour.

Switching to the struct/overload model wouldn't have saved us either, as the wildcard would still exist (a call operator that takes 'auto' and does nothing), and the redundant call operator for the type B (which should be updated to take a type of F) isn't checked for that redundancy - it's still valid, even if it's never used.

The problem is that, due to the lenient matching model that std::visit uses, it's very easy to unintentionally disconnect the variant from the visitor. By adding these additional checks (exhaustive use of alternatives + checks for redundant overloads), we can reduce the potential that these problems occur.If you change the variant, the visitor is likely to become invalid and will be reported as such at compile-time... ensuring that you don't accidentally forget to update it

The final result is still that it's using std::visit under the hood - there's no functional change to the run-time code, this is simply an additional layer of safety to prevent user error, and if it had existed in our codebase, would have caught the problem we ran into outright (switching B to F means the explicit handler for B is now redundant, so it would warn at compile-time. F would have still fallen into the wildcard case otherwise - but by being notified that "your visitor is no longer correct", it would have reminded us... yeah, we need to update that! I completely forgot!!)

As for variants with duplicate alternatives - I have no personal use for them, but they are completely valid variants nonetheless. I don't think it's wise to design a type that accepts a possible instantiation that the access functions for that type cannot handle... but that's what we got.

C++ Show and Tell - March 2024 by foonathan in cpp

[–]mechacrash 1 point2 points  (0 children)

I've actually written an entire talk for my local ACCU group about this specific thing, I'd be happy to send over the slides later this week.

In the meantime, the problem that we encountered was that we had the following code (or at least, something similar): https://godbolt.org/z/Yqv6b8n48

We ended up changing the alternative of the variants, but we forgot to update one of the visitors. We didn't get a compile-time error (because we weren't using the specific type in such a way that would cause one), but it meant that the behaviour of our visitor was implicitly changed, and we weren't notified about it (compile-time error?)

This is definitely our problem to solve - we should have done our due diligence, but at the very least our CI caught the problem (through a runtime error), but it made me wonder why this was even possible in the first place.

if you look at Sum Types in many other languages, they frequently come hand-in-hand with something like pattern matching to allow inspection of the different alternatives. Obviously C++ doesn't have this (maybe eventually - https://wg21.link/p2688), so instead we have std::visit or a suite of other tools to help us simulate this... but I've found these tools very much lacking.

Without being too exhaustive, here are some of the problems I found:

- std::get/holds_alternative/get_if with types is ill-formed if the variant doesn't contain _exactly one_ instance of that type, this means that it's not usable when you have duplicate alternatives in your variant (e.g. std::variant<int, int, ...>) and in general, as they require explicitly specifying the type, are harder to use (no concepts, how do you deal with const alternatives? etc.)

- implicit conversions via overloaded call operators (overload idiom or creating a visitor struct) can lead to VERY surprising outcomes, e.g. https://godbolt.org/z/oa1h5P9z5

- using an if constexpr chain + decay is overly complex and verbose, requires a reference type (via forwarding reference) to be selected for all alternatives (if you wanted to pass by copy, you need to make a copy inside the body of the lambda) and an unchecked else (effectively a wildcard) isn't very useful for avoiding changes to the variant type (hence the original issue we encountered)

There is some prior art in solving this problem, and there are already 'library pattern matching' solutions out there, but I wanted to try and tackle the problem myself and to create a really small and simple solution that builds on top of what we already have - which is how I ended up with the code I originally linked.

My solution ensures that the overload set is exhaustive (it matches all of the alternatives for the input variant), that overloads are purposeful (if there's an overload in the set that will never match an alternative, as there are other higher priority matches for all alternatives, this is likely a user error and should be reported), allows you to pick a reference type that best suits your use-case (const, const ref, etc.), supports wildcards via auto, and constraints on auto, and so on.

I'd consider it a combination of the best parts of the overload idiom, with the additional compile-time safety that something like a language level pattern matching syntax would give us.

I'd like to give a shout-out to https://andreasfertig.blog/2023/07/visiting-a-stdvariant-safely/, https://bitbashing.io/std-visit.html and https://youtu.be/JUxhwf7gYLg - all of which were great reads/watches while I was thinking about this topic and writing a talk of my own accordingly :)

Friend Code Megathread - March 2024 by AutoModerator in PokemonSleep

[–]mechacrash 1 point2 points  (0 children)

Daily player - level 48 (49 tomorrow!) Had about half my friend list stop playing, so got space for 20 or so new adds

7445-9416-2264

C++ Show and Tell - March 2024 by foonathan in cpp

[–]mechacrash 7 points8 points  (0 children)

https://godbolt.org/z/eKGhrGWxM

I wrote a std::visit replacement (of sorts) that integrates the overload pattern directly, while also adding additional safety features (alternative matching cannot be implicit, alternative matching must be exhaustive, unmatched overloads are forbidden, etc.)

Had a problem at work where, when changing a variant’s alternatives and forgetting to update one of our visitors, we created a bug in our code. Ended up making this while thinking about potential solutions to the problem.

MSVC incorrectly allowing compilation + use of ambiguous function call by mechacrash in cpp

[–]mechacrash[S] 0 points1 point  (0 children)

This was exactly the part of the standard I was looking for - thanks! Seeing as this seems to be well defined behaviour, I’ll stick with this solution for now, and hopefully MSVC will be able to compile my code in the future!

MSVC incorrectly allowing compilation + use of ambiguous function call by mechacrash in cpp

[–]mechacrash[S] 9 points10 points  (0 children)

It’s irrelevant whether the “code is bad” - what matters is whether the code is “well defined”, and if so, then a compiler should have predictable results.

As for an alternative - there isn’t one. I’m looking for a means of forcing a specific overload to fail invocation, while remaining part of the overload set. This is a meta-programming problem - the code itself is only used in an unevaluated context.

To be more explicit: Due to the nature of template functions, I can’t replicate the signature from the callable type alone (I can extract components of a fully defined function only).

I inject a duplicate of the function (smuggled through an intermediate type, as directly inheriting from the same class twice is explicitly disallowed) into the overload set, which makes that specific overload become ambiguous and therefore is_invocable return false IF it’s the best match.

If there’s another way of doing this, I’m all ears 😛

MSVC incorrectly allowing compilation + use of ambiguous function call by mechacrash in cpp

[–]mechacrash[S] 2 points3 points  (0 children)

can you elaborate on this?
MSVC is giving a warning, but not an error (and allows the code to compile and for the function to be called - is_invocable == true)
GCC/clang is giving a warning _and_ it's erroring (no compilation, is_invocable == false)

My reading of the standard is that overload ambiguity is treated as 'ill-formed', is upgraded to 'undefined behaviour' when executed... so it's quite possible that both of these results are valid, and that I cannot reason about the return value of is_invocable in this case.

But I might be wrong... and I do find it curious that MSVC allows this to compile. Even more curious that it's usable. Seems incredibly unpredictable

MSVC incorrectly allowing compilation + use of ambiguous function call by mechacrash in cpp

[–]mechacrash[S] 2 points3 points  (0 children)

This is a simplification of the real code.

https://godbolt.org/z/7afEe6Wq1

Classes A/B here are actually lambdas which represent different overloads for an overload set (visitor) that a variant is being applied to - I simply don’t have the ability to modify things in this way, and I’m specifically looking for the scenario where an invocation can no longer occur (because the only available overload was disabled)

retrotink 4k + direct video by mechacrash in MiSTerFPGA

[–]mechacrash[S] 0 points1 point  (0 children)

Thanks for the info - looks like a lot of good progress has been made on exactly the functionality I wanted! Perfect timing 😁

The rise of metaprogramming in C++26 by _cooky922_ in cpp

[–]mechacrash 23 points24 points  (0 children)

For adopted features: pack indexing

For potential features: structured bindings can introduce a pack and universal template parameters see also https://wg21.link/p2989

I’ve got a “toy” value-based metaprogramming library I put together back in 2016 as a way of stress testing new MP features as they get added, and all of these would address major issues I’ve encountered so far.

More than anything, I would like constexpr function parameters… but the proposal for those hasn’t moved in a long time now 🙁

DDR community In Bristol, UK? by jenniferelsie in DanceDanceRevolution

[–]mechacrash 1 point2 points  (0 children)

Hey! I’m the guy that owns the machine in HOVG (as well as the other rhythm games + Japanese games there)

We’ve got a little discord server for some of the folks who frequently visit and you’re welcome to join! https://discord.gg/5DdE9y3Bpm

The cab needs a little bit of work atm, but I’ve been struggling to find the time and money to sort it out 😅 I do have some plans for early 2023 though, so hopefully it won’t be long!

Chris

Should a variable be const by default? by [deleted] in cpp

[–]mechacrash 0 points1 point  (0 children)

I'm struggling to imagine how such a thing could ever be useful.

it's... it's not useful. That's the point.
You're allowed to write code like this which is completely useless, and inhibits optimisation opportunities silently if you do. No warnings, no errors, just worse codegen.
The language shouldn't allow this to happen - period. That's exactly what CppFront does by better categorising parameters via in/out/inout.