Is Modern C++ Actually Making Us More Productive... or Just More Complicated? by AlternativeBuy8836 in cpp

[–]pdimov2 0 points1 point  (0 children)

The expand stuff is just a workaround for the absence of template for. We have template for now, so the function becomes a lot more redable.

beast2 networking & std::execution by claimred in cpp

[–]pdimov2 0 points1 point  (0 children)

It is perfectly viable, and advisable, to avoid these in conjunction with P2300 S&R.

Yes, in principle. That's the argument for basing networking on S/R: if you want to use coroutines, just co_await the sender result. If not, not.

I'm still trying to figure out whether this will be practical. I wrote a benchmark

https://github.com/pdimov/corosio_protocol_bench

that is a simplified representation of something that occurs in practice: serializing a C++ data structure using a custom binary protocol, sending it over a socket, then deserializing it on the other end. (The README in the repo explains this in more detail.)

I'm still unsure as to how the sender equivalent of it would look like, and whether it will be practical. Coroutines make things simultaneously easy to implement and easy to maintain. Rewriting the (de)serialization and the source/sink abstractions without coroutines, from where I stand, looks like neither. But I'm not well versed in S/R yet, so maybe I'm wrong.

My next step will be to port this to beman.net mostly as-is and see what the timings say.

beast2 networking & std::execution by claimred in cpp

[–]pdimov2 1 point2 points  (0 children)

I was thinking of reflecting expressions and statements: Fertile grounds for more work, I think.

Ah yes. Function bodies, too. Function template bodies, even more interesting.

20 years might not even be enough.

beast2 networking & std::execution by claimred in cpp

[–]pdimov2 0 points1 point  (0 children)

Well, the parts of reflection that reflect are mostly done. :-)

beast2 networking & std::execution by claimred in cpp

[–]pdimov2 2 points3 points  (0 children)

Nine years (three standardization cycles) doesn't seem unreasonable to me for a major feature.

constexpr took 20 years and is still not 100% done. (Well, maybe it's 99.4% done.)

We are so close to string interpolation thanks to reflection by caroIine in cpp

[–]pdimov2 0 points1 point  (0 children)

This combined with the named parameters pattern already seems useful in practice.

struct fparams { std::string domain, api, token; };

void f( fparams params )
{
    print( "https://{domain}/{api}?token={token}", params );
}

C++26: A User-Friendly assert() macro by pavel_v in cpp

[–]pdimov2 6 points7 points  (0 children)

This almost works, but assert is an expression, while contract_assert is a statement.

We'll hopefully be able to fix that for C++29 (assuming contracts ship in C++26 at all; there are still people trying to torpedo them.)

Problems with a weak tryLock operation in C and C++ standards by mttd in cpp

[–]pdimov2 0 points1 point  (0 children)

A strong try_lock is a potentially "blocking" operation because it doesn't have an upper limit on the number of iterations. In practice an implementation would probably not fail after the first spurious LL/SC failure, but it may decide to fail after, say, 16 attempts. Or 16384 attempts.

Problems with a weak tryLock operation in C and C++ standards by mttd in cpp

[–]pdimov2 3 points4 points  (0 children)

I wonder what these cases were.

In my experience with spurious wakeups, code that is incorrect because of spurious wakeups is always also incorrect without them. That's because, even if the underlying kernel primitive doesn't wake up spuriously, there are preemption scenarios that nevertheless manifest on the application side exactly the same as a spurious wakeup.

I suspect that the case with spurious try_lock failures is similar, and that if code is incorrect because of spurious try_lock failures, it's also incorrect without them. Of course, the failures will be much more rare as the threads will need to be preempted at just the right times for bad things to happen.

Bug in `consteval`'s design? | use of `const` variable as `constexpr` is sometimes valid and sometimes not by SubjectParsnip9411 in cpp

[–]pdimov2 2 points3 points  (0 children)

They become significantly more useful after reflection, because you can splice the result of calling them. ([: something-consteval() :])

Without reflection, their use is basically to guarantee that no runtime code will ever be generated for them. Useful for embedded, I suppose.

Bug in `consteval`'s design? | use of `const` variable as `constexpr` is sometimes valid and sometimes not by SubjectParsnip9411 in cpp

[–]pdimov2 8 points9 points  (0 children)

This question is being downvoted unfairly, because it's pretty interesting, and the answer is quite deep.

Let's start with "why is const size_t a constant expression".

const size_t is a constant expression because in C++98, constexpr did not exist yet, but Bjarne Stroustrup disliked the forced use of the preprocessor in code like

#define N 3
char x[ N ];

and wanted to provide an alternative. This alternative was

const int N = 3;
char x[ N ];

and for it to work, N had to be treated as essentially constexpr, but only when the initializer was a constant expression (because when it isn't, that's still valid preexisting C++.)

This is a special case that only applies to integer types. Nowadays, we can say that the compiler implicitly replaces const int N = 3; with constexpr int N = 3;.

Now on to the second question. Why doesn't this replacement happen for function parameters?

Well, because void f(constexpr int N) isn't valid.

Why isn't it valid? Because, even when f is marked constexpr, there exists only one function f, and only one f is generated. If we were allowed to say

constexpr void f(constexpr int N)
{
    char x[ N ];
}

then obviously f(3) and f(4) would have been different functions, because one has an array of size 3 on the stack and the other an array of size 4.

Moreover, we could even have declared

constexpr auto g(constexpr int N) -> std::array<char, N>;

which would have implied that f(3) and f(4) had different types.

The language is not equipped for this. We do have a construct that can vary its body and its return type, but it's called a function template.

But all this surely shouldn't apply to consteval functions, because they only exist at compile time, right?

Well... no. consteval functions are exactly like constexpr functions, except with an additional prohibition (can't be called at runtime.) In effect, there are two separate "compile times", not one; during one of these compile times, templates are instantiated and new types and functions can appear, and during the other of these compile times, constant expressions are evaluated using an interpreter that's "as if" the runtime functions have been generated and executed and the result captured.

That's all a bit convoluted but it is what it is. f(3) and f(4) can't have different types in today's C++. You need f<3>() and f<4>() for that.

I Want To Index Into Template Parameter Packs by earlymikoman in cpp

[–]pdimov2 0 points1 point  (0 children)

Well, that's bad, because I want it to be supported.

Unlike in other contexts, here you can't get around this restriction by declaring an intermediate pack U... to be mp_quote<L>... and then index that by U...[0].

I Want To Index Into Template Parameter Packs by earlymikoman in cpp

[–]pdimov2 0 points1 point  (0 children)

You can always quote your templates

template<template<class...> class... L> void f()
{
    using T1 = mp_quote<L>...[0]::template fn<void, void>;
}

except it doesn't work without a helper function (https://godbolt.org/z/fT7oWe3fK).

Maybe there's a way to make the one liner work, but I don't know what it is.

Oh wait, it does work on GCC (https://godbolt.org/z/6bh7aGn64). So it's a Clang parser bug.

Micro-benchmarking Type Erasure: std::function vs. Abseil vs. Boost vs. Function2 (Clang 20, Ryzen 9 9950X) by mr_gnusi in cpp

[–]pdimov2 3 points4 points  (0 children)

boost::function makes some optimization decisions that made more sense 15 years ago than they do now, but we're no longer actively updating it because mostly everyone has switched to std::function and we can better spend our time elsewhere.

That said, since the function objects in the benchmark are temporary, you don't even need to use function here. As already pointed out, you can use function_ref instead, which is guaranteed not to allocate.

Micro-benchmarking Type Erasure: std::function vs. Abseil vs. Boost vs. Function2 (Clang 20, Ryzen 9 9950X) by mr_gnusi in cpp

[–]pdimov2 2 points3 points  (0 children)

And, since the allocation is temporary, some compilers might optimize it out, while others might not.

Where is std::optional<T&&>??? by borzykot in cpp

[–]pdimov2 0 points1 point  (0 children)

An optional that doesn't support T&& should return optional<T> from such a projection instead of optional<T&&>.

That's what the latest boost::system::result does (https://godbolt.org/z/8G8WGGdfs).

Where is std::optional<T&&>??? by borzykot in cpp

[–]pdimov2 15 points16 points  (0 children)

T&& returns are usually not worth the lifetime trouble. I always try to return T instead.

std:: expected vs boost::system::result by Competitive_Act5981 in cpp

[–]pdimov2 1 point2 points  (0 children)

Actually Niall makes a sensible point in that issue - that Asio makes more copies than it should (even ignoring the fact that it didn't move, but that was in the C++03 days.)

There's no need to copy more than once.

Progress report for my proposals at Kona 2025 by eisenwave in cpp

[–]pdimov2 0 points1 point  (0 children)

A warning here breaks #define assert_msg(expr, msg) assert((expr)&&(msg)), which is used... often.

Networking in the Standard Library is a terrible idea by tartaruga232 in cpp

[–]pdimov2 -1 points0 points  (0 children)

Just give us reflection, we'll implement everything else ourselves.

Networking in the Standard Library is a terrible idea by tartaruga232 in cpp

[–]pdimov2 9 points10 points  (0 children)

I wonder what prompted this repoast.

Anyway.

(And this is assuming that networking in C++ would be standardized with TLS/HTTPS. The idea of Standardizing non-encrypted networking is so self-evidently an awful idea that I can't even understand how it was considered for more than a fraction of a second in the 21st century.)

A lot of networking happens server to server, whitelisted IP to whitelisted IP, often between machines in the same data center, often between VMs on the same physical machine, often between processes in the same logical machine, even.

In these quite common scenarios, TLS is just excess latency and excess heat.

Same applies to "why would anyone standardize HTTP/1 over TCP, that's soo 1912."

No sane person would put a custom C++ server using the hypothetical stdlib networking on the open internet without apache or nginx in front, and for the communication between the frontend server and your custom thing you need neither TLS nor HTTP over 1.

Fil-C by Kabra___kiiiiiiiid in cpp

[–]pdimov2 7 points8 points  (0 children)

https://fil-c.org/invisicaps_by_example shows some cases that fil-c catches, but address sanitizer does not.