- overuse of auto is dangerous - 14 Updates
- Structured binding considered harmful - 11 Updates
Ian Collins <ian-news@hotmail.com>: Mar 16 01:37PM +1300 On 16/03/2020 11:41, Keith Thompson wrote: > [...] > Good reasons for using a different name for the same parameter in a > declaration and definition of a function? I can't think of any. I was thinking along the same lines. As C++ gets more Python like, there are some really useful Python features that get left out.. -- Ian. |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Mar 16 07:26AM On Mon, 2020-03-16, Ian Collins wrote: >> declaration and definition of a function? I can't think of any. > I was thinking along the same lines. As C++ gets more Python like, > there are some really useful Python features that get left out.. Stroustrup wrote about that in "Design and Evolution ..." -- named parameters had been requested more than 25 years ago. I don't remember his arguments for not having them, but he had them. Of course things may have changed since then. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
David Brown <david.brown@hesbynett.no>: Mar 16 09:11AM +0100 On 15/03/2020 23:41, Keith Thompson wrote: > [...] > Good reasons for using a different name for the same parameter in a > declaration and definition of a function? I can't think of any. First, let me note that I personally would prefer that parameters in declarations and definitions had to match, and that the benefits (even excluding the ease with which named parameters could be added to the language) outweigh the cases where a difference is perhaps useful. And I am careful to be consistent in my own code. So these are reasons other people have. Parameter names can come in conflict with macros. In particular, system headers will often have parameter names that use implementation namespace to avoid conflict: extern void *memset(void *__s, int __c, size_t __n) It is perfectly allowed to write: #define s 1234 #define c 5678; #define n Humpty Dumpty #include <string.h> So the declaration in <string.h> can't use "s, c, n" as the parameters. However, it is reasonable that the person implementing the function would want more normal parameter names, and that is what you find in library implementations. Sometimes you have a more generic function type, with generic names, but want specific names in specific implementations: typedef int (*callback_f)(int id, void * params); int got_new_name(int id, void * name) { ... } Sometimes you want to leave a parameter without a name, to indicate that it is not used - but you need to name it in a C definition of the function. There are easy ways to get around all of these, such as: int got_new_name(int id, void * params) { const char * name = params; ... } I have read other reasons, but can't recall them at the moment - there have been a number of proposals for adding named parameters to C++ that all come up against these issues. Certainly there are plenty of cases in real world code where parameter names don't match between definitions and declarations, and that always makes it hard to change. |
Sam <sam@email-scan.com>: Mar 16 07:05AM -0400 Jorgen Grahn writes: > parameters had been requested more than 25 years ago. I don't > remember his arguments for not having them, but he had them. Of > course things may have changed since then. I think you can get pretty close to named arguments (even if you don't really want to) as long as you have flexible fingers that don't mind a lot of practice. Start with a helper template: template<typename name_t, value_t> struct arg : public value_t { typedef value_t value_type; using value_t::value_t; }; And then define your placeholder classes. struct name; struct age; Your argument names are: arg<name, std::string>, arg<age, int> All your functions are now variadic templates that construct a helper class and then invoke the real function (or method): void to_you(function_args<arg<name>, arg<age>>); template<typename ...Args> void happy_birthday(Args && ...args) { to_you({std::forward<Args>(args)...}); } The calling sequence would be: happy_birthday( name{"Frodo"}, age{120} ); function_args seems to be straightforward (on the C++ scale): a template whose parameters must all be specializations of arg<>, and with a variadic constructor that walks through each one of its parameters, one by one, and constructs them, and provide a structured binding-compatible interface so that to_you() can simply: void to_you(function_args<arg<name>, arg<age>> my_args); { auto &[name, age} = my_args; } Probably the most nastiest aspect of this would be to have each individual template not participate in overload resolution, unless its parameters are constructible for the function_args. I heard that something like "concepts" might help with that… |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Mar 16 11:27AM On Mon, 2020-03-16, Jorgen Grahn wrote: > parameters had been requested more than 25 years ago. I don't > remember his arguments for not having them, but he had them. Of > course things may have changed since then. I got bored so I dug it out -- section 6.5.1. There was a working proposal for keyword arguments, by Roland Hartinger, in the 1980s. Stroustrup presents a number of problems (one relating to the one above) but for himself the main objection seemed to be: "Would it be sensible to introduce a new feature that primarily supported programming styles that we would prefer to see decline?" Meaning functions with tons of parameters, instead of one or two more refined parameters. Perhaps the current proposal addresses this; I haven't read it. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
David Brown <david.brown@hesbynett.no>: Mar 16 12:32PM +0100 On 16/03/2020 12:05, Sam wrote: > parameters are > constructible for the function_args. I heard that something like > "concepts" might help with that… Without going into detail of this method, it is certainly fair to say there are a variety of different ways to get sort-of named parameters. These include such template solutions (which concepts can improve), having a struct for the the parameters, having partial functions with chains "happy_birthday().name("Frodo").age(120);", and various other solutions. None are good - they are all ugly and wordy compared to an obvious "happy_birthday(name : "Frodo", age : 120);" or similar. None allow you to have a function that can be used with named parameters or placement parameters, none play well with optional or default parameters. Many will have lower efficiency or hinder optimisations. |
Bart <bc@freeuk.com>: Mar 16 11:44AM On 16/03/2020 08:11, David Brown wrote: > #define n Humpty Dumpty > #include <string.h> > So the declaration in <string.h> can't use "s, c, n" as the parameters. There's something very wrong then. Parameter names are supposed to live in their own scope, which in a function definition extends to the body of the function. It also makes it impossible to use ANY identifier, since someone could have used it for some macro. Or even any keyword, as someone could write '#define if 1234'. It would anyway make keyword parameters look very ugly if all those double underlines are going to end up in user code. > #define s 1234 > #define c 5678; > #define n Humpty Dumpty So whose job is it to say DON'T DO THIS - language, compiler or guidelines? And aren't macro names supposed to be in capitals? > Certainly there are plenty of cases in real world code where parameter > names don't match between definitions and declarations, and that always > makes it hard to change. This an example from a script language of mine from the 1990s (this at the time also needed separate function declarations and definitions). It allows a choice of names for certain parameters, when they can be used in different ways. All parameters are variant (dynamic), and ? means all are optional. These functions are intended to be used primarily with keyword arguments: function gxwindow (?var pos, dim, caption, style$options, owner, handlers$handler)var function gxbutton (?var pos, dim, data$caption, style, id, linkvar, onvalue)var The '$' separates alternatives. (I no longer use such a feature, and names now allow $ anyway.) Default values here are assigned inside the function as missing parameters have value 'void'. When I use C libraries, I need to write my own sets of bindings, and then I take the opportunity to use my own names anyway. Here's an example of defining a C function from WinAPI: windows proc "MessageBoxA" (int hwnd=0, ichar message, caption="Caption", int flags=0) The names used in the official API are hWnd, lpText, lpCaption, uType. With my approach, I can use this function with both default and keyword parameters, features that don't exist in the implementation language. I don't even need to use the exact case: messageboxa(message:"Hello World") (Here I would sometimes create an alias 'messagebox', without the final 'a'.) |
Sam <sam@email-scan.com>: Mar 16 08:21AM -0400 David Brown writes: > have a function that can be used with named parameters or placement > parameters, none play well with optional or default parameters. Many will > have lower efficiency or hinder optimisations. Keep in mind that range iteration itself is just a shorthand for canned boilerplate. With the basic implementation in the core C++ library already, some syntactic sugar will square that away. |
David Brown <david.brown@hesbynett.no>: Mar 16 01:29PM +0100 On 16/03/2020 12:44, Bart wrote: > There's something very wrong then. > Parameter names are supposed to live in their own scope, which in a > function definition extends to the body of the function. Macros are not scoped. > It also makes it impossible to use ANY identifier, since someone could > have used it for some macro. Or even any keyword, as someone could write > '#define if 1234'. Defining macro identifiers that are keywords, or any reserved identifier, is undefined behaviour in C and C++ (i.e., it's likely that things will go wrong, and the standards can't guarantee anything). Like it or not (and I know you don't like it!) that's the way macros in the preprocessor work in C and C++. It is a reason why more modern languages don't have a preprocessor of the same kind C and C++ do, and why modern C and C++ programmers usually avoid macros and the preprocessor if there are better alternatives. > > #define n Humpty Dumpty > So whose job is it to say DON'T DO THIS - language, compiler or > guidelines? And aren't macro names supposed to be in capitals? Guidelines. (No, macro names are /not/ "supposed to be in capitals". It is a common guideline, which is often useful - but often used as an alternative to thinking properly and using good names.) Every language has ways to write bad code. Does your compiler for your language complain if you call every function "f1", "f2", "f3", etc., and name your variables "O", "OO", "OOO", etc. ? I assume not - but if you were to write a set of guidelines for your language you would ban these (under a general "use sensible names" rule). (I'm saying how things are, not how I think they should be. C macros are extremely useful, and you need to understand them to write good C and to avoid some kinds of mistakes. It would be nicer if more mistakes were impossible to make in the language, but C is the way it is.) |
"Öö Tiib" <ootiib@hot.ee>: Mar 16 05:35AM -0700 On Monday, 16 March 2020 10:12:09 UTC+2, David Brown wrote: > However, it is reasonable that the person implementing the function > would want more normal parameter names, and that is what you find in > library implementations. Implementation can name its crap as "_Reasonable_name" not "__r". And the whole reason that there are macros and so some code is broken is pointless. Macros are junky feature and so everybody have to keep their macros at bay and period. Most attempts to remove or even reduce usage of macros feel to be sabotaged. Great ideas like ... constexpr, if constexpr or modules are added in watered down, confusing, limited and full of unneeded undefined behaviors manner. Plus if there was accidentally a way to make a feature less useless (even if with using macros!) then they block that way couple redactions later. > int got_new_name(int id, void * name) { ... } > Sometimes you want to leave a parameter without a name, to indicate that > it is not used - but you need to name it in a C definition of the function. These are weak reasons, use comments or attributes like [[maybe_unused]]. . > Certainly there are plenty of cases in real world code where parameter > names don't match between definitions and declarations, and that always > makes it hard to change. Similar reasoning is that some legacy code may have used something as name and so there are no way to add keywords into language unless these are in ugliest dark speech of wormtongues. Huh? Maintain your legacy code or if you don't have budget then do not upgrade tool-sets. I still remember how I did decade ago hate those "ovrdecl", "finaldecl", "hidedecl" and "strictdecl" from n3151. Fortunately there were more such people so result was half-solution that "override" and "final" were added with watered down effect, "base_check" and "hiding" were not added at all and proposed safety usage of "explicit" about class wasn't also added. From where comes that dark speech? Logical would be to add "_Hiding" when oh so afraid to add keyword "hiding". How that "hidedecl" was reached? |
James Kuyper <jameskuyper@alumni.caltech.edu>: Mar 16 09:16AM -0400 On 3/16/20 4:11 AM, David Brown wrote: ... > #define n Humpty Dumpty > #include <string.h> > So the declaration in <string.h> can't use "s, c, n" as the parameters. Yes, and that's a problem with a simpler and better solution - in particular, it can be used by user-defined libraries that can't fall use identifiers with names reserved to the implementation: extern void *memset(void *, int, size_t); There is no mismatch between parameter names in the declaration and in the definition, for the simple reason that the declaration doesn't have any parameter names. You can insert comments rather than parameter names, if you want to document the meaning of each parameter. |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 16 03:31PM +0100 >> Is there any IDE that can expand an auto to the usual declarations ? > VS? VS only supports these tooltips when you fly over an auto. But it shows the internal types to which the containers are mapped and not the types you used in the definition / declaration. |
Vir Campestris <vir.campestris@invalid.invalid>: Mar 16 09:35PM On 13/03/2020 08:14, Öö Tiib wrote: <snip> > Same way MapKeyToData::const_iterator can be declared if it is > type of important citizens in code. Then that "poo" can be declared as; > MapKeyToData::const_iterator poo(); </snip> This is the kind of thing I like. I use auto when I have to: auto l = []() etc - lambdas have an unknown type and when the meaning is obvious auto p = std::make_unique< ... Otherwise I avoid it. A lot of the time auto simplifies writing code, but makes reading it harder. And I expect to read it more often than I write it. Andy |
Ian Collins <ian-news@hotmail.com>: Mar 17 11:49AM +1300 On 17/03/2020 10:35, Vir Campestris wrote: > Otherwise I avoid it. A lot of the time auto simplifies writing code, > but makes reading it harder. And I expect to read it more often than I > write it. Those are excellent use cases, the first would be really messy to write without auto and the second would be needless repetition. As a guideline, this is pretty much what we follow. Another good use is for the return value from the likes of std::find. -- Ian. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Mar 16 05:23AM +0100 On 15.03.2020 23:15, Keith Thompson wrote: >> underlying type are kosher also formally. > What is "std::bool"? (Since bool is a keyword, the grammar would have > to be updated just to allow that.) Sorry, I meant to type `std::byte`. The perils of having a brain with low level automation of things like typing up what I was thinking, when higher levels have moved on to other things. If Odin & his brothers had meant for us to type on QWERTY keyboards they would have equipped Ask and Embla with seven fingers on each hand. The time is long overdue for more rational input equipment. I particularly liked the 80's idea of a little thingy with five buttons that you pressed in combinations. You could even have it in your wallet and type unseen. If you were not afraid of the impression it could give. >> If they have undermined the language in yet another direction it's, >> well, disconcerting. - Alf |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 16 05:24AM +0100 > Using structured binding you have to specify the number of fields of the > class and many other IRRELEVANT things that make more dependencies that > make the code more prone to failure. People who can handle C++ also can handle this. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Mar 16 05:54AM +0100 On 16.03.2020 05:23, Alf P. Steinbach wrote: > I particularly liked the 80's idea of a little thingy with five buttons > that you pressed in combinations. You could even have it in your wallet > and type unseen. If you were not afraid of the impression it could give. Oh dang it happened again. I ordered typing of "pocket", not "wallet". |
"Öö Tiib" <ootiib@hot.ee>: Mar 16 12:20AM -0700 On Sunday, 15 March 2020 20:27:51 UTC+2, Alf P. Steinbach wrote: > What I recall is the opposite, that the introduction of `std::bool` > forced a cleanup where now all enumerator values that fit within the > underlying type are kosher also formally. Yes, seems that the resulting broken std::byte of C++17 caused them to fix enums in C++20. |
Sam <sam@email-scan.com>: Mar 16 06:46AM -0400 Bonita Montero writes: >> class and many other IRRELEVANT things that make more dependencies that make >> the code more prone to failure. > People who can handle C++ also can handle this. Furthermore, having delicate code that requires substantial work to implement a tiny change is one of the key defining features of C++, and this simply makes it even better. |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 16 12:52PM +0100 > Furthermore, having delicate code that requires substantial work to > implement a tiny change is one of the key defining features of C++, > and this simply makes it even better. The issue here to complain about is the use of auto which results in intransparent types. I use auto only when absolutely necessary. In the "auto considered harmful" thread someone give a weird example which could get seemingly readable by using auto. But I rather prefer a 40 character type than an auto. |
Sam <sam@email-scan.com>: Mar 16 08:32AM -0400 Bonita Montero writes: > In the "auto considered harmful" thread someone give a weird example > which could get seemingly readable by using auto. But I rather prefer > a 40 character type than an auto. So, your argument boils down to mostly personal preference. If you prefer to explicitly declare every type, you can still do so. This hasn't been outlawed. You can continue writing out and spelling everything out. But now, there's an option to simply avoid doing that. And, as an extra bonus, I can do: auto e=container.end(); auto p=std::find_if(container.begin(), e, [](const auto &value) { return value == 42; }); if (p != e) { std::cout << "Find it" << std::endl; } Now, when I suddenly realize that my container must be a std::list, instead of a std::vector, since there's a new requirement to be able to modify it without invalidating all existing iterators, you can simply change the declaration only, and not have to change a shitload of code. If you prefer to do that, instead, because you believe you gain something from the pain of doing that, you are welcome to continue writing the same verbose, explicit declarations you're already doing. But, I'm glad I have another option, now. |
Bonita Montero <Bonita.Montero@gmail.com>: Mar 16 01:51PM +0100 > So, your argument boils down to mostly personal preference. If you're not the designer of the code or a visionary you've to find the container-type for which you're getting the auto variable(s). And that's more work than reading the concrete type. > instead of a std::vector, since there's a new requirement to be able to > modify it without invalidating all existing iterators, you can simply > change the declaration only, and not have to change a shitload of code. Having the concrete type maintains readability - that's more important than having less typing. As someone said in another thread he got tired of overly use of auto in a source he didn't write himself. |
jacobnavia <jacob@jacob.remcomp.fr>: Mar 16 04:11PM +0100 Le 16/03/2020 à 11:46, Sam a écrit : > Furthermore, having delicate code that requires substantial work to > implement a tiny change is one of the key defining features of C++, and > this simply makes it even better. That's the point. C++ started with many good ideas like "information hiding", in the sense that you limit the access to irrelevant detail to specify interfaces that are more robust to changes in the underlying implementation. Structured binding goes against that principle head on: you expose everything and any change to the implementation of the base class needs a rewrite of user code! The existing way of "binding", i.e. declaring the variables and assigning to those variables the fields you want to access should be prefered. Structured binding is a mistake. |
Ian Collins <ian-news@hotmail.com>: Mar 17 08:17AM +1300 On 17/03/2020 04:11, jacobnavia wrote: > Structured binding goes against that principle head on: you expose > everything and any change to the implementation of the base class needs > a rewrite of user code! Ah but if you are using a type with named members, you wouldn't need to use structured binding, would you? Structured binding is useful for objects with unnamed members (arrays, tuples) or objects with generic names (map, pair etc). So no, structured binding is not a mistake. -- Ian. |
Daniel <danielaparker@gmail.com>: Mar 16 01:12PM -0700 On Monday, March 16, 2020 at 11:11:32 AM UTC-4, jacobnavia wrote: > that you limit the access to irrelevant detail to specify interfaces > that are more robust to changes in the underlying implementation. > Structured binding goes against that principle head on: Not necessarily > you expose everything and any change to the implementation of the base > class needs a rewrite of user code! Consider std::map<std::string, int> m = { {"one",1}, {"two",2} }; for (const auto& [key, value] : m) // (*) { std::cout << "key: " << key << ", value: " << value << "\n"; } for (const auto& kv : m) // (**) { std::cout << "key: " << kv.first << ", value: " << kv.second << "\n"; } (*) reveals less of the implementation detail of std::map than (**), in particular, it does not depend on the particular representation of the value_type being std::pair<const std::string,int>. Daniel |
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