Monday, July 4, 2016

Digest for comp.lang.c++@googlegroups.com - 20 updates in 4 topics

"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: