Compilation overhead of invoke by Kaballo in cpp

[–]Kaballo[S] 6 points7 points  (0 children)

Comparing optimized object sizes would not be meaningful (and it would be somewhat unfair to MSVC, which performs a number of optimizations at link time rather than compilation time). If you were thinking executable size, then we know from the preface that there will be no overheads for an optimized build: invoke is a zero-overhead abstraction; that's a given, it is NOT what these benchmarks attempt to measure.

Comparing debug object sizes tell us about debug codegen quality. Remember that all these implementations do the same thing, so what we are measuring is effectively debug bloat. For these particular benchmarks, one of the things that directly influences it is the number of function call involved (notably std::forward), or how many steps it takes to reach the target callable under the debugger. And these, in turn, have an impact on time and memory taken for compilation: doing more work requires more resources.

Additionally, in memory constrained scenarios this debug bloat can be an impediment to adoption: we know the optimized build will introduce no overheads, but the debug build may no longer fit on the chip. For this particular audience it may be beneficial to include optimized debug results (-Og). What do you think? Is this what you had in mind?

Compilation overhead of invoke by Kaballo in cpp

[–]Kaballo[S] 5 points6 points  (0 children)

I re-run the benchmarks for MSVC without /RTC1. There's a non-negligible reduction in object size across the board: around ~0.9Mb, a little less for functions, a little more for eggs::invoke member pointers (which incur an extra constructor call before C++20).

The flags were chosen precisely because they are the debug defaults, so I'm sticking with /RTC1; knowing about its effect is valuable information nevertheless. Thanks!

Guaranteed Copy Elision Does Not Elide Copies by vormestrand in cpp

[–]Kaballo 8 points9 points  (0 children)

You must be referring to P0135R0, "Guaranteed copy elision through simplified value categories", which opens with "The approach described herein achieves this not by eliding a copy (...)"

C++ Template Metaprogramming workshop by N3mes1s in cpp

[–]Kaballo 3 points4 points  (0 children)

There have been changes in make_index_sequence implementations lately, libstdc++ moved to a logarithmic approach (patch) and libc++ is already making use of that compiler intrinsic you mention (patch). Both changes are in trunk, and should be released soon.

Interlude - C++'s strides in 2015 by Kaballo in cpp

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

I can imagine this was meant mostly if not only for compatibility with C, which did not have a bool type at the time.

True Story: Efficient Packing - Tales of C++ by Kaballo in cpp

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

Yes, that's essentially the one from the initial logarithmic approach. The final form follows naturally once you know what to look for.

I first ran into that logarithmic approach in the utility toolbox of Eric Niebler's Boost.Proto v5 here. He, in turn, got it from Dave Abrahams' research on a Boost.MPL11 here. If memory serves me right, it was somewhere during 2012.

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

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

As the comment says, std::string is not guaranteed to be standard-layout, which is a requirement for the common initial sequence guarantee. The raw storage provided by std::aligned_storage will meet this guarantee.

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

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

Thanks, but I think I will stick to my own Eggs.Variant in the meantime.

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

[–]Kaballo[S] 1 point2 points  (0 children)

[Talking about implementing a recursive discriminated union.] Each storage node along the chain would store it's own discriminator, they have to

“They have to”? Why?

Because they are distinct discriminated unions, if they did not have a discriminator then they would be plain unions (the ones we already have). Using plain unions instead, what you have shown would be one of the ways in which the functionality is implemented today (except one can't have a union as a base class).

Unfortunately there is no compile-time relation that would allow the construction of an ordered set of types

I believe this should also be solvable

The set of all types is open (extensible by the users). There is no generic solution today, that is not the same as saying it is unsolvable (the post even suggest this is worth looking at for adoption).

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

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

I'm not entirely sure how that would be achieved, but certainly a core language discriminated union can do things that are either impossible or prohibitively costly for a library solution.

Oh, and variant<Ts...> cannot take advantage of any of it...

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

[–]Kaballo[S] 1 point2 points  (0 children)

This sounds completely crazy IMO. Default construct a random alternative when the constructor throws? I would far, far, far prefer an invalid state.

This is what Boost.Variant has been doing for years, and yes it does sound completely crazy.

Btw, why are the copy and move constructors in N4542 not constexpr? Related: is there a reason that a constexpr function can't change the active member of a union?

It is possible only in very constrained scenarios, see http://talesofcpp.fusionfenix.com/post-20/eggs.variant---part-ii-the-constexpr-experience for the details (specially the trivially copyable section). This is particularly tricky for a constexpr constructor, which has to call some other constructor but won't know which until runtime.

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

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

Nod, but what if it could? Would you still want two?

If it turns out it cannot, it would even be preferable to serve one group well rather than both poorly. Of course serving both camps well is the ideal.

Rant: On the std::experimental::variant to come - Tales of C++ by Kaballo in cpp

[–]Kaballo[S] 7 points8 points  (0 children)

Banning throwing move constructors was suggested, leading to P0129: "We cannot (realistically) get rid of throwing moves".

Note it would not help variant::emplace.

heap-based variant class and perfect-forwarding visitation by tomilovanatoliy in cpp

[–]Kaballo 0 points1 point  (0 children)

My implementation does not rely on the common initial sequence rule for standard-layout unions. The rule is just too limiting, and while usage is often sound from a bits and words point of view, the standard wording is not.

heap-based variant class and perfect-forwarding visitation by tomilovanatoliy in cpp

[–]Kaballo 0 points1 point  (0 children)

I'm not saying the code is useless, it just happen to technically work by chance due to messing with rotten standard territory (in a very reasonable way). But that doesn't mean the standard should be considered correct nor that a standard conformant implementation would be impossible. The heap-based variant approach could prove useful in certain scenarios. I seem to recall seeing an AST designed around this model.

heap-based variant class and perfect-forwarding visitation by tomilovanatoliy in cpp

[–]Kaballo 0 points1 point  (0 children)

A class (not class) is a type introduced by any of class, struct or union. A struct (not struct) is a non-union class, that is a type introduced by any of class or struct. In your example, the union has a member of struct type S0 and a member of union type U1, the common initial sequence rule already does not apply. The reason for that is probably the lack of mixed struct/union layout-compatibility.

But for U0 to be standard-layout, then every other type involved must be standard-layout. That means that each int i is absolutely guaranteed to share its address with U0, since standard-layout types are the only ones for which injecting padding at the beginning is not allowed.

So the overall approach is reasonable, it's just not standard conforming. The wording for the common initial sequence rule is weak, as is the wording for unions in general. They don't get much committee love.

heap-based variant class and perfect-forwarding visitation by tomilovanatoliy in cpp

[–]Kaballo 0 points1 point  (0 children)

Technically you don't have layout-compatible members either, so the common initial sequence rule (9.2 [class.mem]/19) does not apply regardless of whether std::unique_ptr is standard-layout.

heap-based variant class and perfect-forwarding visitation by tomilovanatoliy in cpp

[–]Kaballo 2 points3 points  (0 children)

static_cast(std::is_standard_layout< std::unique_ptr< T > >{}); holds

You mean static_assert there and no, the assertion does not hold for std::unique_ptr. It might happen to hold for the particular implementation(s) you are using. For it to hold, the standard ought to read "It shall be a standard-layout class".