- Finest C++ - 14 Updates
- An idea and a question - 1 Update
- bitfields compact packing within a struct or class - 5 Updates
- Where's the dangling reference, according to gcc 13? - 3 Updates
- Function Pointer from Lambda with Captures - 2 Updates
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 06:15AM +0200 template<typename T> concept scalar = is_scalar_v<T>; template<scalar ... TS> common_type_t<TS ...> min( TS ... values ) { common_type_t<TS ...> min = get<0>( tuple<TS ...>( values ... ) ); ((min = values < min ? values : min), ...); return min; } |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 10:52AM +0200 On 2023-04-17 6:15 AM, Bonita Montero wrote: > ((min = values < min ? values : min), ...); > return min; > } I guess you're posting this because of a perceived elegance in the code. What is the point of the initialization of `min` when the first thing that happens after is that `min` is assigned to? Was perhaps the intent to reduce the number of assignments via e.g. ((values < min ? min = values : 0), ...); And why do you name a local variable the same as the function? One reason to write your own `min` rather than just using `std::min` could be to support a mix of signed and unsigned. But the presented code doesn't do that, so (because it supports a mix of arbitrary scalar argument types) it introduces a bug vector compared to `std::min`. Why? - Alf (somewhat perplexed) |
Frederick Virchanza Gotham <cauldwell.thomas@gmail.com>: Apr 17 02:38AM -0700 On Monday, April 17, 2023 at 9:52:46 AM UTC+1, Alf P. Steinbach wrote: > And why do you name a local variable the same as the function? In my dayjob I find myself having to do the following a lot: #include <.....loads of header files......> #undef min #undef max #include <algorithm> Just making the point that 'min' gets abused a lot. |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 01:08PM +0200 Am 17.04.2023 um 10:52 schrieb Alf P. Steinbach: > What is the point of the initialization of `min` when the first thing > that happens after is that `min` is assigned to? This is optimized away. > Was perhaps the intent to reduce the number of assignments via e.g. > ((values < min ? min = values : 0), ...); You need a fold-expression or a generic recursive function for that. Fold expressions are easier to handle. > And why do you name a local variable the same as the function? Because there's no problem with that since the function name isn't referred from inside. > One reason to write your own `min` rather than just using > `std::min` could be to support a mix of signed and unsigned. std::min accepts only two values or an initializer-list which has more syntax. |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 01:11PM +0200 Am 17.04.2023 um 06:15 schrieb Bonita Montero: > ((min = values < min ? values : min), ...); > return min; > } This is a corrected version: template<typename ... TS> common_type_t<TS ...> min( TS const &... values ) { using ct_t = common_type_t<TS ...>; common_type_t<TS ...> min = get<0>( tuple<TS ...>( values ... ) ); ((min = (ct_t)values < min ? (ct_t)values : min), ...); return min; } I forgot to cast values to ct_t so that the common value is determined pair-wise and not for the whole statement. |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 01:09PM +0200 On 2023-04-17 11:38 AM, Frederick Virchanza Gotham wrote: > #undef max > #include <algorithm> > Just making the point that 'min' gets abused a lot. `-D NOMINMAX` But better make that part of a wrapper for `<windows.h>`. - Alf |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 01:20PM +0200 Am 17.04.2023 um 13:09 schrieb Alf P. Steinbach: >> Just making the point that 'min' gets abused a lot. > `-D NOMINMAX` > But better make that part of a wrapper for `<windows.h>`. And the min/max-macros aren't usable for more than two values. I prefer to define NOMINMAX inside the source file before the include since there's not something to forget for the build -process. Usually when I use numeric_limit<X>::min()/max(). I think that's really awkward for someone who doesn't know that Windows.h defines this macros by default. |
Frederick Virchanza Gotham <cauldwell.thomas@gmail.com>: Apr 17 04:27AM -0700 On Monday, April 17, 2023 at 12:10:13 PM UTC+1, Alf P. Steinbach wrote: > But better make that part of a wrapper for `<windows.h>`. When I need to access the Win32 API directly, I make a separate translation called "windows_bomb_chamber.c", and that's the only source file that includes <windows.h>. Then I put my own function declarations inside "windows_bomb_chamber.h". |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 01:34PM +0200 On 2023-04-17 1:08 PM, Bonita Montero wrote: >> ((values < min ? min = values : 0), ...); > You need a fold-expression or a generic recursive function for that. > Fold expressions are easier to handle. How come fold expressions are easier to handle than a fold expression? >> And why do you name a local variable the same as the function? > Because there's no problem with that since the function name isn't > referred from inside. It's a waste of time + annoyance for a human. Same as the lack of parenthesis around the choice expression. The effective precedence of the ternary operator in various usages is a subtlety of the language that few programmers remember. So when I choose to take you seriously, such as this time, I have to engage in detective work to figure out if you have a bug or just are training for the yearly obfuscated code contest. It's easy to write clear code for this: add parentheses. >> `std::min` could be to support a mix of signed and unsigned. > std::min accepts only two values or an initializer-list which > has more syntax. Writing and using obscure code with a likely-to-manifest standard library name conflict in order to avoid a pair of braces, doesn't make sense to me, but as I noted at the beginning, perhaps you did this for the perceived elegance or beauty, like artwork. - Alf |
David Brown <david.brown@hesbynett.no>: Apr 17 03:02PM +0200 On 17/04/2023 10:52, Alf P. Steinbach wrote: > I guess you're posting this because of a perceived elegance in the code. > What is the point of the initialization of `min` when the first thing > that happens after is that `min` is assigned to? It is needed because, given arbitrary types, there is no general choice of the initial value for the fold. (It is not a "C++ fold" because C++ only supports folds on infix binary operators, but it is a "fold" in the more general programming sense.) But the code then re-uses the first value, which is unnecessary - if comparisons are expensive, it is a waste. It is better to use a head+tail recursion pattern here: template<scalar T, scalar ... TS> auto xmin(T t, TS ... values ) { auto x = xmin(values ...); return t < x ? t : x; } A t3(A a, B b, C c) { return xmin(a, b, c); } > Was perhaps the intent to reduce the number of assignments via e.g. > ((values < min ? min = values : 0), ...); No - see above. > And why do you name a local variable the same as the function? To confuse readers, I expect. I assume it is the same reason there appears to be a "using std;" in effect, and then a function whose name matches one in the "std" namespace (i.e., "std::min"). > could be to support a mix of signed and unsigned. But the presented code > doesn't do that, so (because it supports a mix of arbitrary scalar > argument types) it introduces a bug vector compared to `std::min`. Why? Mixing signed and unsigned types is always a risk in C++. In a reusable function like this, it could make sense to protect against it. Otherwise, gcc's "-Wconversion -Wsign-conversion" are useful tools. |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 03:49PM +0200 On 2023-04-17 3:02 PM, David Brown wrote: > of the initial value for the fold. (It is not a "C++ fold" because C++ > only supports folds on infix binary operators, but it is a "fold" in the > more general programming sense.) My comment was not the smartest one I've ever produced. I think I was still confused by the parsing of the ?:. > A t3(A a, B b, C c) { > return xmin(a, b, c); > } That looks better, yes. But relies on tail recursion optimization that is not guaranteed. So even better just e.g. template< signed_scalar... Args > constexpr auto xmin( Args... args ) -> auto { return min({ static_cast<<common_type_t<First, Rest...>>( args )... }); } Disclaimer: did not let compiler check that. > Mixing signed and unsigned types is always a risk in C++. In a reusable > function like this, it could make sense to protect against it. > Otherwise, gcc's "-Wconversion -Wsign-conversion" are useful tools. I'd just limit the function to signed type arguments, as shown above. Cheers, - Alf |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 03:52PM +0200 On 2023-04-17 3:49 PM, Alf P. Steinbach wrote: > { return min({ static_cast<<common_type_t<First, Rest...>>( args > )... }); } > Disclaimer: did not let compiler check that. template< signed_scalar... Args > constexpr auto xmin( Args... args ) -> auto { return min({ static_cast<<common_type_t<Args...>>( args )... }); } That editing error was of course Donald Trump's work. Same disclaimer. - Alf |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 05:33PM +0200 The following code avoids superfluous temporary-copies. template<typename ... TS> common_type_t<TS ...> xmin( TS const &... values ) { using ct_t = common_type_t<TS ...>; ct_t min = get<0>( tuple<TS ...>( values ... ) ); if constexpr( (same_as<TS, common_type_t<TS ...>> && ...) ) ((min = values < min ? values : min), ...); else { auto nextMin = [&]( TS const &value ) { ct_t cvtValue( value ); if( cvtValue < min ) min = cvtValue; }; (nextMin( values ), ...); } return min; } |
Muttley@dastardlyhq.com: Apr 17 03:41PM On Mon, 17 Apr 2023 13:11:36 +0200 > ((min = (ct_t)values < min ? (ct_t)values : min), ...); > return min; >} One of the reasons Perl has been comprehensively killed by Python except in a few niche and legacy applications is because people got sick of trying to decipher the barely comprehensible line noise that "gurus" had written in order to show off. Unfortunately C++ seems to be heading in that direction. Sometimes more can be less when it comes to code. |
Bonita Montero <Bonita.Montero@gmail.com>: Apr 17 05:41PM +0200 Does anyone know some library that does a move-construction or move -assigment on a foreign type, i.e. not on its own type ? I'm wondering if this makes sense sometimes. |
Paavo Helde <eesnimi@osa.pri.ee>: Apr 17 07:50AM +0300 17.04.2023 02:13 Scott Lurndal kirjutas: > Michael S <already5chosen@yahoo.com> writes: >> On ARM64 Linux uint64_t is the same as unsigned long. > on x86_64 as well. This is true on Linux x86_64, but not in Windows x86_64. A related story: just a week ago I was trying out how smart GPT-4 is, and it generated for me some quite impressive code using the openssl X509_gmtime_adj() function. Very nice, but the manpage suggests against using that function, because of the limited range of a 'long' argument. All our programs are 64-bit anyway, so we should not be worried about this limitation - but we need to support also Windows c86_64, where we do need to worry about that. So basically GPT-4 silently created a Y2K38 bug for me, which would surface only after ca 15 years, and only on Windows. So there ;-) |
MarioCPPP <NoliMihiFrangereMentulam@libero.it>: Apr 17 02:29PM +0200 On 16/04/23 21:21, Öö Tiib wrote: >> my experiment ... >> But any consideration is welcome > We do not precisely know your use-case but it looks unlikely. yes ... I as afraid so > by hand. > To me just writing a class that keeps the data ether in > uint_64_t or uint8_t[5] (depending on requirements) and the problem is to match uint8_t[5] <=> uint5_t[8] with single read / write. uint5_t obviously is not a type, just to stress the concept. > extracts or sets the quintets in those by hand feels > more promising idea than using std::bitset<40>. Ok: not a viable way to explore ! Tnx -- 1) Resistere, resistere, resistere. 2) Se tutti pagano le tasse, le tasse le pagano tutti MarioCPPP |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 17 10:41AM -0400 On 4/16/23 09:18, MarioCPPP wrote: ... > And what about the standard type BITSET <size> ? C++ is a type sensitive language, so you need to be careful about case. It does have a bitset class template, it doesn't have anything named BITSET. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 17 10:51AM -0400 On 4/16/23 09:22, MarioCPPP wrote: > On 14/04/23 20:00, Scott Lurndal wrote: ... >> struct eight_symbols_five_bits_wide { >> uint64_t field0 : 5, ... > width ? I mean, I usually see only plain unsigned. uint64_t > is a "complete" type, suggesting 64 bit width, which is not > the actual size. Why so ? "A bit-field shall have integral or enumeration type; the bit-field semantic property is not part of the type of the class member." (11.4.9p1). A bit-field's declared type determines how values extracted from the bit-field are handled after extraction. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Apr 17 11:20AM -0400 On 4/16/23 09:24, MarioCPPP wrote: > On 14/04/23 20:00, Scott Lurndal wrote: ... > SEMICOLON would have prevented the packing ? > Or was it just a typo ? Sorry for the silly question, but I > have doubts on bit fields Those commas have nothing to do with packing. They are separators for the member-declarator-list part of a member declaration which is based upon the following grammar rule: "member-declaration : attribute-specifier-seq opt decl-specifier-seq opt member-declarator-list opt ;" (11.4) The key part of a declarator is the identifier that names what is being declared. It also includes the syntax that declares whether the identifier identifies a function, a point, a reference, a bit-field; if none of those apply, it identifies an object. The "final" or "overrides" keywords and any "requires" clause or initializer are also part of the declarator. This rule allows you to declare multiple declarators in a single declaration. The attribute and declaration specifiers that precede the declarator list are shared by all of the declarators in the list. The key part of the declaration specifier (9.2) is the defining-type-specifier (9.2.8), which describes the type of the object or bit-field, the type returned by the function, or pointed at by the pointer, or referred to by the reference. It also includes the "friend", "typedef", "constexpr", "consteval", "constinit", "inline", "virtual", and "explicit" keywords. If the decl-specifier-seq is sufficiently complicated, declaring multiple declarators in a single declaration can save a significant amount of typing. However, I would not recommend using that fact as the sole justification for doing so. I recommend using it only to declare a group of closely related declarators (as in the field0 through field 7 members declared above). |
David Brown <david.brown@hesbynett.no>: Apr 17 09:40AM +0200 On 17/04/2023 01:05, Sam wrote: > const auto &v=optional_arg_or<int>(value, def, 0); > return v; > } In "optional_arg_or", you are creating a temporary object of type "ret_type" and returning a reference to it. That's the dangling reference. I believe you are assuming that the optional<ret_type> object is a struct containing a separate "ret_type" object, and it is valid to return a reference to that subobject. But I don't think such an assumption is valid. I would suggest removing the references in "optional_arg_or" and treating these all as values - let compiler optimisation handle the generation of efficient code (copy elision and return value optimisations probably guarantee efficient code). Dealing with values rather than references is often more efficient anyway for small types such as "int". |
Sam <sam@email-scan.com>: Apr 17 07:05AM -0400 David Brown writes: > I believe you are assuming that the optional<ret_type> object is a struct > containing a separate "ret_type" object, and it is valid to return a > reference to that subobject. But I don't think such an assumption is valid. I'm fairly certain that this is exactly what std::optional is. https://en.cppreference.com/w/cpp/utility/optional/operator* "Returns a reference to the contained value." https://en.cppreference.com/w/cpp/utility/optional "If an optional<T> contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place. Thus, an optional object models an object, not a pointer, even though operator*() and operator→() are defined." |
David Brown <david.brown@hesbynett.no>: Apr 17 03:18PM +0200 On 17/04/2023 13:05, Sam wrote: > memory allocation ever takes place. Thus, an optional object models an > object, not a pointer, even though operator*() and operator→() are > defined." OK. I am still not convinced it is safe to assume such pointers are valid. (Maybe someone else will give you a better answer here.) Anyway, return of the "optional_arg_or" call is a temporary, and you're taking a reference to it. Change the line to "const auto v = ..." and you should be okay. When I am in doubt about this kind of thing, I make a little class with an external constructor and destructor and look at what is called where. It's easy to be mislead by the behaviour on simple types like "int". |
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Apr 17 02:31AM +0200 On 2023-04-17 12:02 AM, Frederick Virchanza Gotham wrote: > I was having so much fun writing my own solution that I didn't pay enough attention to yours. > I've edited your code so that the following syntax is possible: > some_library_func( my::Thunked_(my_lambda) ); Just a thought, but does that play well with a lambda like [&]( auto arg ) { cout << arg << endl; } ? > Here's what I've done with your code: > https://godbolt.org/z/G56xbPMWf > What do you reckon about sharing it to the standard proposals mailing list for consideration to be put in the Standard Library? A proposal needs * A working portable implementation. * Tested with multiple compilers/architectures (preferable). * Proposal text expressed in standardese. * Having been /discussed/ on the appropriate mailing list (in the old days it was instead comp.std.c++), before being submitted as a proposal. * A champion or two on the committee. The (SO) Good Puppy's thunks proposal failed on the last point, I believe: he was unable to get anyone to use their time on it, as I remember it even though he appeared in person at a meeting. Anyway, for a proposal it would need to support thread safety. And for my standard C++ only approach I reckon that that involves a least one non-obvious engineering decision of choosing between thread-local variables OR mutual exclusion for thunk creation OR requiring declaration of a thunk pool in each thread using thunks. That's just off the top of my head. I haven't tried it. For that reason the assembly scheme you posted, even though I didn't verify it, but assuming that it worked, would perhaps be better. For with stack allocation one gets automatic thread safety and no discussion about the design and implementation options for that. > into: > void SomeFunc(int b, float a); > or another thunk to reverse the order of arguments. Hm. Off the cuff, template< class Func > auto argreversed( Func& f ) -> auto { return []( auto a, auto b ) { return f( b, a ); }; } Maybe add perfect argument forwarding. - Alf |
Frederick Virchanza Gotham <cauldwell.thomas@gmail.com>: Apr 17 02:04AM -0700 On Monday, April 17, 2023 at 1:32:10 AM UTC+1, Alf P. Steinbach wrote: > thread-local variables OR mutual exclusion for thunk creation OR > requiring declaration of a thunk pool in each thread using thunks. > That's just off the top of my head. I haven't tried it. Over on the mailing list for the Standard, Edward Catmur posted similar code to yours except he used a mutex. I took Edward's code and edited it similarly to how I edited your own. Here is how I replied: I had been working with similar code posted by Alf P. Steinbach over on comp.lang.c++ last week, but I much prefer your code Edward. I have tweaked your code a bit so that the following syntax is possible: SomeLibraryFunc( thunk(f) ); Note that you don't have to explicitly tell it the lambda's function signature, it figures it out itself. I've also done a handful of other things: * Replaced the pair of pointers with just one pointer * Got rid of the 'used' array and instead just check for nullptr's in the 'data' array * Mark all members of 'thunk' as 'noexcept' * Propagate the 'noexcept' from the lambda to the thunk * Accommodate lambdas marked as 'mutable' Here's what I've got: https://godbolt.org/z/5xT7e84WY |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment