- Early returns - 8 Updates
- Dynamic type - 8 Updates
- Early returns - 2 Updates
- Best way to allocate memory pool at compile time - 2 Updates
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 04 10:04AM +0200 I feel that I'm missing something, some way to write this in a more clear way than early returns, without getting into extreme indentation: void check_incoming() override { if( Base::buffer_.pointer_to<Event>() != nullptr ) { return; // Already buffered an event. } winapi::Input_record event_data = {}; winapi::Double_word n_items = 0; // TODO: x propagation winapi::PeekConsoleInputW( keyboard_, &event_data, 1, &n_items ) || fail( "Event_stream::Impl::check_incoming: PeekConsoleInputW failed" ); if( n_items == 0 ) { return; } const auto kind = event_data.EventType; if( kind == winapi::Event_kind::key ) { const auto& ev = event_data.Event.KeyEvent; const auto key_code = static_cast<Keycodes::Enum>( ev.wVirtualKeyCode ); const bool is_autorepeat =(ev.bKeyDown and is_in( pressed_keys_, key_code )); if( not is_autorepeat ) { if( ev.bKeyDown ) { pressed_keys_.insert( key_code ); } else { pressed_keys_.erase( key_code ); } Base::buffer_ = Boxed_event{ Key_event{ Key{ key_code, ev.uChar.UnicodeChar }, (ev.bKeyDown? Key_event::Action::pressed : Key_event::Action::released) } }; return; } } // Best effort to consume the ignored event, to help reduce the // chance of Windows' event buffer becoming full. winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); } Suggestions? Cheers, - Alf |
Ian Collins <ian-news@hotmail.com>: Jul 04 08:12PM +1200 On 07/ 4/16 08:04 PM, Alf P. Steinbach wrote: > I feel that I'm missing something, some way to write this in a more > clear way than early returns, without getting into extreme indentation: It looks like early returns are the clearest solution so why worry? If you want better clarity, throw in a few usings! > { > return; // Already buffered an event. > } Testing preconditions this way is, at least in my opinion, much clearer that if/else blocks. -- Ian |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 04 12:41PM +0300 On 4.07.2016 11:04, Alf P. Steinbach wrote: > I feel that I'm missing something, some way to write this in a more > clear way than early returns, without getting into extreme indentation: What's wrong with early returns? In C++ one may get an early return basically anywhere via exceptions, so all the code must be prepared for early returns anyway (i.e. use RAII). Cheers Paavo |
"Öö Tiib" <ootiib@hot.ee>: Jul 04 06:59AM -0700 On Monday, 4 July 2016 11:06:57 UTC+3, Alf P. Steinbach wrote: > I feel that I'm missing something, some way to write this in a more > clear way than early returns, without getting into extreme indentation: SESE in C++ can be achieved with 'goto's, splitting code into more functions and/or usage of short-circuiting of inbuilt &&, || and ?: operators ... like in C. SESE is generally not considered "more clear" in C++. |
"K. Frank" <kfrank29.c@gmail.com>: Jul 04 11:21AM -0700 Hello Alf! On Monday, July 4, 2016 at 4:06:57 AM UTC-4, Alf P. Steinbach wrote: > clear way than early returns, without getting into extreme indentation: > ... > Suggestions? I agree with several other of your respondents that "early returns" can be perfectly appropriate, and are often the right tool for the job. Öö brought up the "SESE" acronym. I understand the motivation behind this proposed rule, but I never thought it made sense as a "hard and fast" rule, or even one "with rare exception." I often find "early returns" to be the most natural approach to validating input to a function, especially input from an external source where you can't rely on your upstream code to have passed on sane input. E.g., if (crude_input_check_fails) return; if (number_of_fields_not_sane) return; if (tag_1_not_recognized) return; if (value_1_invalid) return; // ... do_real_work_knowing_input_is_valid(); What's not to like? To me, such code is well-organized and clear. Both your intent and mechanism for achieving it is clearly expressed. A "hidden" return buried deep in some code fragment in the bowels of the function could be more error prone, and would probably be bad style, but don't let some "SESE" rule force you to throw out the good-code baby along with the bad-code bath water. Besides, in a similar vein, some of us even use (gasp!) exceptions for non-exceptional code paths (the horror!): try { parse_input(); } catch (BadInputException e) { // parse failed } void parse_input() { if (crude_check_fails) throw BadInputException ("crude check"); if (field_split_fails) throw BadInputException ("bad fields"); parse_field_1(); // ... } void parse_field_1() { if (bad_tag1) throw BadInputException ("bad tag 1"); parse_value_1(); } void parse_value_1() { if (bad_value_1) throw BadInputException ("bad value 1"); // ... } // ... This is like early return, except for a series of nested function calls. (To avoid any semantic discussions, let me state for the record that I do not consider handling ill-formed external input to be an exceptional code path.) > Cheers, > - Alf Cheerio ... And Happy Hacking! K. Frank |
guy.tristram@gmail.com: Jul 04 12:39PM -0700 Perhaps OT, but I did wonder about this construction: > winapi::PeekConsoleInputW( keyboard_, &event_data, 1, &n_items ) > || fail( "Event_stream::Impl::check_incoming:PeekConsoleInputW failed" ); isn't that equivalent to: > { > fail( "Event_stream::Impl::check_incoming:PeekConsoleInputW failed" ); > } ? If so, are there any advantages to the first construction? It took me a moment to work out what it did (perhaps because the result of the or isn't used). Maybe this is an idiom unfamiliar to me. Back on topic: I don't think generally that multiple returns are a problem. The only one that would give me pause is the return slightly buried in the if( kind == winapi::Event_kind::key ) clause. But I think that the obvious way to avoid it - moving that code into a new function - introduces more complexity than it removes. cheers - Guy |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jul 04 08:41PM +0100 On 04/07/2016 09:04, Alf P. Steinbach wrote: > I feel that I'm missing something, some way to write this in a more > clear way than early returns, without getting into extreme indentation: [snip] We have been over this several times before so I am wondering if you are trolling. Again: SESE is a C idiom and SEME is fine in C++ as C++ has RAII (the main motivation for SESE in C is related to resource leaks, a problem solved in C++ by RAII). SEME is unavoidable in C++ thanks to exceptions and multiple return statements are usually fine if the function is not egregiously large. If the function is egregiously large then consider decomposing it. /Flibble |
Daniel <danielaparker@gmail.com>: Jul 04 02:50PM -0700 On Monday, July 4, 2016 at 3:42:09 PM UTC-4, Mr Flibble wrote: > [snip] > We have been over this several times before so I am wondering if you are > trolling. This is why, Alf's previous comments regrading |
Cristiano <cristiapi@NSgmail.com>: Jul 04 01:41PM +0200 Header file: //typedef double TIPO; typedef float TIPO; struct COEFF { TIPO *c[6]; }; struct COEFF *coe; cpp file: for(int i= 0; i < 3; i++) { coe[I].c[i]= new TIPO[ncoe]; if(fread(coe[I].c[i], sizeof(TIPO), ncoe, f) != ncoe) { puts("ERROR"); return 3; } } const TIPO **c= (const TIPO **)coe[idx].c; Is there any memory efficient way to rewrite the code to switch from float to double at runtime? Thank you Cristiano |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 04 04:03PM +0300 On 4.07.2016 14:41, Cristiano wrote: > const TIPO **c= (const TIPO **)coe[idx].c; > Is there any memory efficient way to rewrite the code to switch from > float to double at runtime? Not sure what kind of memory efficiency do you have in mind. Double takes twice as much space as a float, so you will definitely lose memory when switching to double. For supporting both float and double by the same code, make it templated. For storing both floats and doubles in the same array - forget about the idea. For reading in floats from a binary non-portable file and store them in a double array just read them one-by-one and store in a double array. Also note that your code resembles more C than C++ and as a result it is leaking memory (at least if an error occurs). The only C++ feature in the code ('new') should not appear at all in such code, std::vector should be preferred for most usage cases. HTH Paavo |
Cristiano <cristiapi@NSgmail.com>: Jul 04 03:26PM +0200 On 04/07/2016 15:03, Paavo Helde wrote: > takes twice as much space as a float, so you will definitely lose memory > when switching to double. > For supporting both float and double by the same code, make it templated. That is the case. As you noticed, my C++ programming skill is not good (like my English :-)); please, would you post an example? Cristiano |
"Öö Tiib" <ootiib@hot.ee>: Jul 04 07:17AM -0700 On Monday, 4 July 2016 14:41:42 UTC+3, Cristiano wrote: > Header file: Looks like short C header (but without multiple inclusion guards). > TIPO *c[6]; > }; > struct COEFF *coe; The keyword 'struct' is redundant in global variable 'coe' declaration. > } > } > const TIPO **c= (const TIPO **)coe[idx].c; That can not compile. You can not write 'for' cycles directly into global namespace, you have to put those inside of functions. > Is there any memory efficient way to rewrite the code to switch from > float to double at runtime? Achieve that the code compiles, then that it does what you want, then that it does it always and accurately. Only then think how can you achieve efficiency. It is because no one cares how fastly and efficiently a program gives wrong answers. |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 04 08:03PM +0300 On 4.07.2016 16:26, Cristiano wrote: > That is the case. > As you noticed, my C++ programming skill is not good (like my English > :-)); please, would you post an example? Some more or less pseudocode, might give you some ideas. The main point is that at some point you need explicit runtime dispatch to templates (here done inside main()). template<typename TIPO> class COEFF { std::vector<TIPO> c[6]; public: void Read(std::istream& f); for(int i= 0; i < 3; i++) { c[i].resize(ncoe); if(!f.read(c[i].data(), sizeof(TIPO)*ncoe) { throw std::runtime_error("ERROR"); } } } // ... }; // cpp file: template<typename TIPO> void DoWork() { std::vector<COEFF<TIPO>> coe(N); for (int I=0; I<N; ++I) { std::ifstream f(filename, std::ios::binary); coe[I].Read(f); } // ... } int main() { try { // ... if (useFloat> { DoWork<float>(); } else { DoWork<double>(); } } catch(const std::exception& e) { std::cerr << e.what() << "\n"; return EXIT_FAILURE; } } |
Barry Schwarz <schwarzb@dqel.com>: Jul 04 11:54AM -0700 On Mon, 04 Jul 2016 15:26:19 +0200, Cristiano <cristiapi@NSgmail.com> wrote: >That is the case. >As you noticed, my C++ programming skill is not good (like my English >:-)); please, would you post an example? While templates can support multiple types, the decision about which type is determined at compile time, not run time. -- Remove del for email |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 04 10:31PM +0300 On 4.07.2016 21:54, Barry Schwarz wrote: >> :-)); please, would you post an example? > While templates can support multiple types, the decision about which > type is determined at compile time, not run time. Yes, that's why there is an explicit runtime dispatch needed. In one branch of the dispatch one type is determined at compile time, in another branch another, etc. In our codebase we have macros which dispatch ca 10 types to a templated function. That's one remaining usage case for macros in C++. Cheers |
Cristiano <cristiapi@NSgmail.com>: Jul 04 09:52PM +0200 On 04/07/2016 19:03, Paavo Helde wrote: > is that at some point you need explicit runtime dispatch to templates > (here done inside main()). > template<typename TIPO> [...] It seems that I need to completely rewrite my code. Unfortunately, the code is for a DLL that will be used in a program not written by me and I cannot use all your code, but I got the point. Thanks a lot Cristiano |
ram@zedat.fu-berlin.de (Stefan Ram): Jul 04 05:10PM > void check_incoming() My code is a draft, untested and buggy, but you might get the general idea. The new functions should be given sensible names; I was not able to do this as I don't know the application domain. The entry point »void check_incoming() override« is at the end: void check_incoming4 ( winapi::Input_record & event_data, decltype<event_data.Event.KeyEvent> const & ev, Keycodes::Enum key_code ) { if( ev.bKeyDown )pressed_keys_.insert( key_code ); else pressed_keys_.erase( key_code ); Base::buffer_ = Boxed_event { Key_event { Key { key_code, ev.uChar.UnicodeChar }, ( ev.bKeyDown ? Key_event::Action::pressed : Key_event::Action::released )}}; } bool check_incoming3 ( winapi::Input_record & event_data, winapi::Double_word n_items, decltype< event_data.EventType > const kind ) { const auto& ev = event_data.Event.KeyEvent; const auto key_code = static_cast< Keycodes::Enum > ( ev.wVirtualKeyCode ); const bool is_autorepeat = ( ev.bKeyDown and is_in( pressed_keys_, key_code )); if( not is_autorepeat ) { ::check_incoming4( event_data, ev, key_code ); result = true; } else result = false; return result; } void check_incoming2 ( winapi::Input_record & event_data, winapi::Double_word n_items ) { const auto kind = event_data.EventType; if( kind == winapi::Event_kind::key ) { if( check_incoming3( event_data, n_items, kind ) )return; } winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); } void check_incoming1() { winapi::Input_record event_data = {}; winapi::Double_word n_items = 0; winapi::PeekConsoleInputW( keyboard_, &event_data, 1, &n_items )|| fail ( "Event_stream::Impl::check_incoming1: PeekConsoleInputW failed" ); if( n_items )check_incoming2( event_data, n_items ); } void check_incoming() override { if( !Base::buffer_.pointer_to< Event >() ) check_incoming1(); } |
ram@zedat.fu-berlin.de (Stefan Ram): Jul 04 05:29PM > if( kind == winapi::Event_kind::key ) > { if( check_incoming3( event_data, n_items, kind ) )return; } > winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); } There is an early return left above, so: void check_incoming2 ( winapi::Input_record & event_data, winapi::Double_word n_items ) { const auto kind = event_data.EventType; if ( kind == winapi::Event_kind::key && check_incoming3( event_data, n_items, kind ) ); else winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); } . |
bitrex <bitrex@de.lete.earthlink.net>: Jul 04 10:12AM -0400 I'm working on writing my own little event-driven scheduler for a microprocessor in C++, as both a learning exercise for C++ and something that might have some practical use for me down the road. So I have an abstract class called "AbstractEvent" that the different types of user-defined events can derive from. The subclasses are templated with string designators and a class data package that can be templated to store different kinds of data provided by the event generator, which a particular "observer" might require. Since this is for a uP with no MMU, all buffers and required objects are sized and instantiated at compile time and then not touched via placement new, etc. The idea is that there will be a static memory pool of different event types which _can_ be dispatched, and a memory pool of events which have been dispatched that are waiting for consumption by the receivers. When a new event is generated a new "data package" is created, the required event type is copy-constructed from the static pool, the new package placed in its buffer, and the event placed alongside the other events waiting to be processed in a container within a "state object." Since the subclasses of the abstract type are templated, the ideal way to do this would be to instantiate a unique_ptr for each subclass and store the unique_ptrs in a vector. Unfortunately the stlib available for this processor doesn't implement all C++11 features, even though the compiler is C++11 compatible. I've decided that the simplest workaround is to just store the dispatched events in a regular ol' pre-allocated array buffer and then have a vector of base pointers to the event objects for the rest of the code to work with. But that means that I need to know the largest size of all the event classes the user wants to work with to size the cells appropriately. So essentially what I'm asking is that say in the setup code the user wants an "Event<int, foo>" (where "foo" is a string designator), an "Event<char, bar>", and an Event<float, baz>". Since the types of the subclasses of AbstractEvent required by a specific implementation are all known at compile time, I'm wondering if there's a way that I can have a memory pool generated at compile time for them, with cells large enough to hold any subclass instance, such that the compiler will inform me how much data memory is used by the pool (it's a Harvard architecture uP.) Instead of having the pool instantiated at runtime. If there's a better way to go about this I'm open to suggestions as well. Thanks. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 04 04:27PM +0200 On 04.07.2016 16:12, bitrex wrote: > uP.) Instead of having the pool instantiated at runtime. > If there's a better way to go about this I'm open to suggestions as > well. Thanks. You could use Boost solution, but since this is a microprocessor with a limited compiler you may not have Boost available. And anyway, with a Boost dependency the code may need to be updated every third year or so just in order to still compile. I used to love Boost but it's big and hairy, it has at least two competing date-time sub libraries neither of which is up to my standard of usability, and it's forever changing. For the DIY C++ has two mechanisms for generating a "cell" that can hold any of N specific types: * an array of bytes used via placement new and support for alignment, and * a union. The union (keyword) is nice but there is no direct support for a /discriminated union/, a union where there is a member that specifies the main type stored in there, like a Pascal variant record. One way is to store a pointer to dummy polymorphic type representative. This approach leverages default generated copying for the union, while supporting dynamic_cast, which is nice. In the other direction, if you find that client code is frequently discriminating on type then that's a common anti-pattern, something to avoid; it's so bad that it was the main reason why Bertrand Meyers did not include enumerations in Eiffel. Cheers & hth., - Alf |
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