- Dynamic type - 4 Updates
- Array member initialization, gcc vs. clang - 5 Updates
- Early returns - 3 Updates
- Early returns - 2 Updates
Cristiano <cristiapi@NSgmail.com>: Jul 05 01:44AM +0200 On 04/07/2016 19:03, Paavo Helde wrote: > template<typename TIPO> > class COEFF { > std::vector<TIPO> c[6]; [...] Thank to your help, I wrote a working version of my code, but since I need to call several functions like: if (useFloat) DoWork_1<float>(); else DoWork_1<double>(); ... if (useFloat) DoWork_2<float>(); else DoWork_2<double>(); ... is there any way to set <float> or <double> once and for all and then simply call DoWork_1(), DoWork_2(), ... ? Cristiano |
Christian Gollwitzer <auriocus@gmx.de>: Jul 05 08:37AM +0200 Am 05.07.16 um 01:44 schrieb Cristiano: > ... > is there any way to set <float> or <double> once and for all and then > simply call DoWork_1(), DoWork_2(), ... ? You cannot set it like a global switch. Think like a compiler: the compiler has to generate completely different code, therefore you must pass that information around at compile time. However, you need such a switch only once. You can "pass" the type as a template parameter either explicitly or implicitly: template <typename T> void Subtask1 (T x) { } template <typename T> void Subtask2 (T x) { } template <typename T> void Subtask3 () { } template <typename T> void MyMain () { T f = 0; Subtask1(f); Subtask2(f); Subtask3<T>(); } int main() { if (useFloat) { MyMain<float>(); } else { MyMain<double>(); } } See? The if switch is only in the main function. All functions then decide on the type via overload resolution (Subtask1 & Subtask2) or via an explicit template parameter (Subtask3), in case if there is no argument where the compiler gets the type from. Christian |
Paavo Helde <myfirstname@osa.pri.ee>: Jul 05 09:38AM +0300 On 5.07.2016 2:44, Cristiano wrote: > ... > is there any way to set <float> or <double> once and for all and then > simply call DoWork_1(), DoWork_2(), ... ? Yes, you can use a virtual interface: #include <memory> class Base { public: virtual void DoWork1() = 0; virtual void DoWork2() = 0; }; template<class TIPO> class COEFF: public Base { virtual void DoWork1() override { /*...*/ } virtual void DoWork2() override { /*...*/ } }; int main() { bool useDouble = (rand()%2==0); std::unique_ptr<Base> p; if (useDouble) { p.reset(new COEFF<float>()); } else { p.reset(new COEFF<double>()); } p->DoWork1(); p->DoWork2(); } hth Paavo |
Cristiano <cristiapi@NSgmail.com>: Jul 05 11:00PM +0200 On 05/07/2016 08:38, Paavo Helde wrote: >> is there any way to set <float> or <double> once and for all and then >> simply call DoWork_1(), DoWork_2(), ... ? > Yes, you can use a virtual interface: [...] I used your solution because it better fits my original code (I already use a class). Thank you very much for your valuable help. Many thanks also to Christian. Cristiano |
Juha Nieminen <nospam@thanks.invalid>: Jul 05 11:00AM Consider the following code: //-------------------------------------------------- struct A { int values[5]; A(): values{} {} }; struct B { int values[5] = {}; }; int main() { const A a; // Works const B b; // Compiler error } //-------------------------------------------------- This compiles with all recent versions of gcc. However, all recent versions of clang complain about the "const B b;" line, saying: "error: default initialization of an object of const type 'const B' without a user-provided default constructor" Which one is right? --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 05 01:54PM +0200 On 05.07.2016 13:00, Juha Nieminen wrote: > "error: default initialization of an object of const type 'const B' without > a user-provided default constructor" > Which one is right? Offhand, strong gut feeling, clang's error is correct. Because the rationale for the old rule is that a default initialized instance is not very useful as a constant, so that a constant needs to be explictly specified, hence, user provided default constructor or (not mentioned in the error message) explicit initializer. Anyway it's IMHO best to proceed as if clang is correct here. ;-) Cheers & hth., - Alf |
Alain Ketterlin <alain@universite-de-strasbourg.fr.invalid>: Jul 05 06:22PM +0200 > "error: default initialization of an object of const type 'const B' without > a user-provided default constructor" > Which one is right? In N4296: §8.5p7 is: | To default-initialize an object of type T means: | | (7.1) — If T is a (possibly cv-qualified) class type (Clause 9), | constructors are considered. The applicable constructors are enumerated | (13.3.1.3), and the best one for the initializer () is chosen through | overload resolution (13.3). The constructor thus selected is called, | with an empty argument list, to initialize the object. | | (7.2) — If T is an array type, each element is default-initialized. | | (7.3) — Otherwise, no initialization is performed. | | If a program calls for the default initialization of an object of a | const-qualified type T, T shall be a class type with a user-provided | default constructor. So I think clang is right. -- Alain. |
woodbrian77@gmail.com: Jul 05 09:36AM -0700 On Tuesday, July 5, 2016 at 11:23:10 AM UTC-5, Alain Ketterlin wrote: > | default constructor. > So I think clang is right. > -- Alain. It sounds like clang is right according to the letter of the law. I'd prefer a warning though in that case rather than an error. Brian Ebenezer Enterprises - In G-d we trust. http://webEbenezer.net |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jul 05 06:29PM +0100 On Tue, 5 Jul 2016 13:54:48 +0200 > instance is not very useful as a constant, so that a constant needs > to be explictly specified, hence, user provided default constructor > or (not mentioned in the error message) explicit initializer. Zero initialization of the member array with either of these will work with clang: const B b{}; const B b = {}; I suppose the error is sort-of useful where the struct has member data, but somewhat annoying when you are only interested in compile time template values. So given a list of compile-time integers as follows struct int_list<int...> {}; the following will also fail with clang unless you zero initialize the (non-existent) member data of the struct, or remove the explicit constexpr specifier: constexpr int_list<1, 3, 5, 7, 9> odds; // won't compile As you say I suspect (but don't know) that this behaviour is what the standard requires. Chris |
Christian Gollwitzer <auriocus@gmx.de>: Jul 05 09:48AM +0200 Am 05.07.16 um 06:31 schrieb Stefan Ram: > And the first might be more "fluent", i.e., in English > one says "If you are hungry, eat something!", > not "If you are not hungry, leave! Eat something!« It depends on the predicate. If instead of hungry() you have full(), it's exactly the opposite: if (!full()) eat(); if(full()) return; eat(); You can't always influence the sign of the predicates, e.g. if it comes out of a library. Christian |
"Öö Tiib" <ootiib@hot.ee>: Jul 05 01:24AM -0700 > > fail( "Event_stream::Impl::check_incoming:PeekConsoleInputW failed" ); > > } > ? Yes it is. > 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). There are several mild advantages. For example it avoids ! that is sometimes hard to notice and it is some characters more terse and it can be used in places where expression is allowed but not 'if' statement. Reading it out "peek-console-input or fail" ... does not feel too obscure. > Maybe this is an idiom unfamiliar to me. Alf likes to invent such idioms. I like this one better than his "hopefully", that he used sort of like "hopefully peek-console-input or throw". In general it is part of engineer's work to invent. C++ is itself a work in and so the set of its usage idioms can't be complete. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jul 05 03:57PM +0200 On 04.07.2016 19:29, Stefan Ram wrote: > ( kind == winapi::Event_kind::key && > check_incoming3( event_data, n_items, kind ) ); else > winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); } I used a slightly more course-grained functional decomposition, leaving one early return and transforming the other returns to more meaningful value returns, so that now the abstract event stream class is like class Event_stream : public Non_copyable , public Non_movable { private: class Impl; protected: Boxed_event buffer_; struct Check_result{ enum Enum{ event_ignored, event_accepted, no_available_event }; }; virtual auto accept_or_ignore_incoming_if_any() -> Check_result::Enum = 0; void check_incoming() { if( buffer_.pointer_to<Event>() != nullptr ) { return; // Already buffered an event. } while( accept_or_ignore_incoming_if_any() == Check_result::event_ignored ) {;} } virtual ~Event_stream() {} Event_stream() {} public: template< class Event_kind > auto has() -> bool { check_incoming(); return (buffer_.pointer_to<Event_kind>() != nullptr); } auto next_event() -> Boxed_event { Boxed_event result{ buffer_ }; buffer_ = Boxed_event{}; // Very empty. return result; } static auto singleton() // An `Impl` defined by the system's impl. -> Ref_<Event_stream>; }; … and then in the Windows implementation class there is auto accept_or_ignore_incoming_if_any() -> Check_result::Enum override try { winapi::Input_record event_data = {}; winapi::Double_word n_items = 0; winapi::PeekConsoleInputW( keyboard_, &event_data, 1, &n_items ) || fail( "PeekConsoleInputW failed" ); if( n_items == 0 ) { return Check_result::no_available_event; } 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) } }; winapi::ReadConsoleInputW( keyboard_, &event_data, 1, &n_items ); return Check_result::event_accepted; } } // 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 ); return Check_result::event_ignored; } CPPX_RETHROW_X // Prepends the qualified function name to the message. … which doesn't yet deal with mouse events, but's like in the old days one bought FM receivers "ready for stereo": they could more or less easily be upgraded to stereo, or at least that was intended and hoped. I think the names of the functions are now awkward, though. :) Cheers!, and thanks, - Alf (ungood at naming non-simple functions) |
ram@zedat.fu-berlin.de (Stefan Ram): Jul 05 03:36AM >Back on topic: I don't think generally that multiple returns are a problem. About 10 - 20 years ago, under one C compiler, early returns like »return 2;« where faster than one single »return result;« at the end. Modern optimizers might have changed this. But another problem might be one additional level of negation that might make code harder to read. I.e., instead of the natural if( hungry ) eat(); one needs to write if( !hungry )return; eat(); . |
ram@zedat.fu-berlin.de (Stefan Ram): Jul 05 04:31AM >one needs to write >if( !hungry )return; >eat(); And the first might be more "fluent", i.e., in English one says "If you are hungry, eat something!", not "If you are not hungry, leave! Eat something!« |
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