Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #55792 > unrolled thread

Is this poor style?

Started by"S.K.R. de Jong" <SKRdJ@nowhere.net>
First post2015-01-04 00:46 +0000
Last post2015-01-08 12:03 -0500
Articles 20 on this page of 102 — 30 participants

Back to article view | Back to comp.lang.c


Contents

  Is this poor style? "S.K.R. de Jong" <SKRdJ@nowhere.net> - 2015-01-04 00:46 +0000
    Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-03 16:58 -0800
      Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-04 20:37 +1300
        Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-04 02:32 -0800
          Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-05 09:39 +1300
            Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-04 22:53 +0000
    Re: Is this poor style? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-01-04 01:29 +0000
    Re: Is this poor style? Morris Dovey <mrdovey@iedu.com> - 2015-01-04 00:00 -0600
      Re: Is this poor style? Morris Dovey <mrdovey@iedu.com> - 2015-01-04 02:20 -0600
    Re: Is this poor style? August Karlstrom <fusionfile@gmail.com> - 2015-01-04 15:46 +0100
      Re: Is this poor style? Ike Naar <ike@iceland.freeshell.org> - 2015-01-04 15:10 +0000
        Re: Is this poor style? August Karlstrom <fusionfile@gmail.com> - 2015-01-04 17:06 +0100
    Re: Is this poor style? Clark Smith <noaddress@nowhere.net> - 2015-01-04 15:30 +0000
      Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-04 07:44 -0800
        Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-05 00:35 +0000
          Re: Is this poor style? BartC <bc@freeuk.com> - 2015-01-05 01:02 +0000
            Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-05 01:43 +0000
              Re: Is this poor style? BartC <bc@freeuk.com> - 2015-01-05 11:35 +0000
              Re: Is this poor style? raltbos@xs4all.nl (Richard Bos) - 2015-01-05 12:58 +0000
                Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-05 13:14 +0000
            Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-05 22:02 +0000
              Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-05 14:24 -0800
                Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-06 12:26 +1300
                  Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-06 02:40 -0800
                Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-07 19:32 +0000
                  Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-07 20:03 +0000
                    Re: Is this poor style? BartC <bc@freeuk.com> - 2015-01-07 20:27 +0000
                      Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-08 09:55 +1300
                        Re: Is this poor style? Les Cargill <lcargill99@comcast.com> - 2015-01-07 18:13 -0600
                          Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-08 13:17 +1300
                      Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-07 21:02 +0000
                        Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 11:22 +0100
                          Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-08 11:33 +0000
                            Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 13:08 +0100
                              Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-08 12:54 +0000
                        Re: Is this poor style? David Brown <david.brown@hesbynett.no> - 2015-01-08 14:04 +0100
                      Re: Is this poor style? Keith Thompson <kst-u@mib.org> - 2015-01-07 13:57 -0800
                      Re: Is this poor style? Les Cargill <lcargill99@comcast.com> - 2015-01-07 18:12 -0600
                    Re: Is this poor style? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-01-07 20:56 +0000
                      Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-07 21:18 +0000
                    Re: Is this poor style? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-01-07 21:28 +0000
              Re: Is this poor style? BartC <bc@freeuk.com> - 2015-01-06 11:28 +0000
                Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-07 20:19 +0000
                  Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-07 16:05 -0800
                    Re: Is this poor style? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-01-08 08:29 +0000
                    Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-12 22:08 +0000
                      Re: Is this poor style? Tim Rentsch <txr@alumni.caltech.edu> - 2015-03-24 20:35 -0700
                        Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-03-25 08:43 -0700
                          Re: Is this poor style? Tim Rentsch <txr@alumni.caltech.edu> - 2015-03-25 16:38 -0700
                          Re: Is this poor style? Stephen Sprunk <stephen@sprunk.org> - 2015-03-26 15:43 -0500
                            Re: Is this poor style? Tim Rentsch <txr@alumni.caltech.edu> - 2015-03-28 08:57 -0700
                  Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-08 13:23 +1300
                    Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-07 16:53 -0800
                      Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-08 14:11 +1300
                Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-08 11:10 +1300
          Re: Is this poor style? John Bode <jfbode1029@gmail.com> - 2015-01-05 15:54 -0800
            Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-06 11:03 +0100
              Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-06 11:10 +0100
              Re: Is this poor style? John Bode <jfbode1029@gmail.com> - 2015-01-06 06:03 -0800
                Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-06 14:44 +0000
                  Re: Is this poor style? "Osmium" <r124c4u102@comcast.net> - 2015-01-06 09:10 -0600
                    Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 11:14 +0100
                      Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-08 11:31 +0000
                        Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 13:06 +0100
                          Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 13:19 +0100
                          Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-08 12:53 +0000
                            Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-08 05:49 -0800
                      Re: Is this poor style? Keith Thompson <kst-u@mib.org> - 2015-01-08 08:18 -0800
                        Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-09 07:58 +0100
                        Re: Is this poor style? Tim Rentsch <txr@alumni.caltech.edu> - 2015-03-24 09:28 -0700
                          Re: Is this poor style? Keith Thompson <kst-u@mib.org> - 2015-03-24 13:57 -0700
                            Re: Is this poor style? Tim Rentsch <txr@alumni.caltech.edu> - 2015-03-25 08:28 -0700
                              Re: Is this poor style? Keith Thompson <kst-u@mib.org> - 2015-03-25 12:17 -0700
                            Re: Is this poor style? Öö Tiib <ootiib@hot.ee> - 2015-04-05 17:03 -0700
                      Re: Is this poor style? John Bode <jfbode1029@gmail.com> - 2015-01-14 11:25 -0800
                        Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-14 19:41 +0000
                        Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-15 09:01 +0100
                        Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-15 09:35 +0100
      Re: Is this poor style? James Kuyper <jameskuyper@verizon.net> - 2015-01-04 11:15 -0500
        Re: Is this poor style? Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-01-04 21:38 +0000
        Re: Is this poor style? Dr Nick <nospam-4@temporary-address.org.uk> - 2015-01-05 07:27 +0000
          Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-05 06:33 -0800
      Re: Is this poor style? Vincenzo Mercuri <nomail@yahoo.it> - 2015-01-04 18:56 +0100
      Re: Is this poor style? rouben@shady.(none) (Rouben Rostamian) - 2015-01-04 18:22 +0000
      Re: Is this poor style? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-01-04 21:35 +0000
      Re: Is this poor style? Ian Collins <ian-news@hotmail.com> - 2015-01-05 10:46 +1300
        Re: Is this poor style? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-01-04 15:36 -0800
      Re: Is this poor style? asetofsymbols@gmail.com - 2015-01-05 12:05 -0800
        Re: Is this poor style? asetofsymbols@gmail.com - 2015-01-05 14:18 -0800
          Re: Is this poor style? asetofsymbols@gmail.com - 2015-01-05 14:32 -0800
            Re: Is this poor style? asetofsymbols@gmail.com - 2015-01-05 14:37 -0800
          Re: Is this poor style? James Kuyper <jameskuyper@verizon.net> - 2015-01-05 18:23 -0500
            Re: Is this poor style? Richard Heathfield <invalid@see.sig.invalid> - 2015-01-05 23:32 +0000
        Re: Is this poor style? Keith Thompson <kst-u@mib.org> - 2015-01-05 15:31 -0800
      Re: Is this poor style? Phil Carmody <pc+usenet@asdf.org> - 2015-01-13 15:00 +0200
    Re: Is this poor style? Les Cargill <lcargill99@comcast.com> - 2015-01-04 15:07 -0600
    Re: Is this poor style? jadill33@gmail.com - 2015-01-05 15:03 -0800
    Re: Is this poor style? buja <buja66@gmail.com> - 2015-01-06 21:48 +0100
      Re: Is this poor style? Rosario193 <Rosario@invalid.invalid> - 2015-01-08 11:21 +0100
        Re: Is this poor style? buja <buja66@gmail.com> - 2015-01-08 12:42 +0100
    Re: Is this poor style? "S.K.R. de Jong" <SKRdJ@nowhere.net> - 2015-01-08 15:29 +0000
      Re: Is this poor style? James Kuyper <jameskuyper@verizon.net> - 2015-01-08 12:03 -0500

Page 3 of 6 — ← Prev page 1 2 [3] 4 5 6  Next page →


#56025

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-01-07 21:28 +0000
Message-ID<m8k8dd$4qo$1@speranza.aioe.org>
In reply to#56016
Richard Heathfield <invalid@see.sig.invalid> wrote:
> Jorgen Grahn wrote:
(snip) 
>> The idea that every function should be "library-quality" may be one
>> reason people write few, big and ugly functions. (Although a more
>> important psychological reason probably is "but function calls are
>> inefficient ... !").
 
> This is an astoundingly persistent meme. It's on a par with malloc 
> being costly. On a (relatively) recent collaboration with a 
> Canadian university, I asked the Good Professor (who will remain 
> firmly nameless, to protect the guilty) if it would not be better 
> to allow the problem size to be defined at runtime rather than at 
> compile time. (It was an NP problem for which we very much needed 
> a "good enough" solution *right the heck now* rather than a perfect 
> solution some time next millennium, so we were using GAs in an 
> attempt to short-cut our way to fame and glory.) He said that he 
> couldn't do that because it would take too much time to do the 
> allocation dynamically.  Fortunately, he was open to persuasion. 
> As it turned out, the dynamic allocation - even for seriously 
> pathological cases - was blitzingly fast, of course. In a program 
> whose runtime budget was an hour or so, it took a tiny handful 
> of milliseconds.

Dynamic allocation is slow if you do too much of it. In most cases,
that means avoiding it in the inner loop. In your case, it sounds 
like you allocated once, outside all loops. That should be fast.

> Function calls are, as you clearly understand, most definitely 
> *not* the bottleneck either. To my way of thinking, if you can 
> do X a brazillion times a second, X does not have a performance 
> problem. What's more, large, butt- ugly functions are far *less* 
> efficient, because they take too long for maintenance programmers 
> to grok. Obviously, a plethora of tiny weeny functions isn't going 
> to help either, because it takes too long to understand how they 
> all fit together. It's a trade-off, a balance.

Again, you might avoid them in the innermost loop if you really
need to be fast. It does seem that as computers have gotten faster,
there has been even more work to speed up function calls. 
 
> It is impossible (well, all right, it's possible, but unwise) 
> to generalise on just how big a function can be before it needs 
> re-factoring. Some enormous functions are extraordinarily simple 
> and can be left in one piece (for example, S-boxes), and it is 
> easy to imagine how quite a small function might still be 
> too complex and could perhaps benefit from being broken into even 
> smaller functions.

Yes it is hard to generalize, and it might even depend on how many
times it is called. 

-- glen

[toc] | [prev] | [next] | [standalone]


#55981

FromBartC <bc@freeuk.com>
Date2015-01-06 11:28 +0000
Message-ID<sFPqw.487139$7A5.252993@fx17.am4>
In reply to#55958
On 05/01/2015 22:02, Jorgen Grahn wrote:
> On Mon, 2015-01-05, BartC wrote:
>> On 05/01/2015 00:35, Richard Heathfield wrote:
> ...
>>> Whilst it is certainly true that I've heard that goto isn't good, I actually
>>> have personal experience of its not being good.
>>
>> You might, nevertheless, prefer reading other people's code that uses
>> gotos sensibly and consistently, to alternates that involve tortuous
>> logic or where the action keep shifting to dozens of extra functions
>> (with the complicated interfaces required to allow them to share common
>> data).
>
> I think I was the one who mentioned using functional decomposition
> instead of goto.  I can assure you, that doesn't end up as "dozens of
> extra functions", "complicated interfaces" or shared common data.

Could you take any function, and turn any block of block within it into 
a second function? Could you do that several times to create several new 
functions?

I would have these problems:

(1) I would need to think of meaningful names for these function, ones 
that are unique because now I'm in the global name-space.

(2) I would need to decide where to place those functions. Perhaps the 
original function was part of nice, tidy cluster with related 
functionality, but it's not going to be tidy much longer!

(3) I'd need to document the functions and explain why each is only 
called from one place. Also what it actually does which is harder 
because it might not be meaningful outside its original context

(4) I'd have to document the new calls in the original function.

(5) If the blocks of code made use of local variables (using or setting 
their values) then this is where the interface between the original 
function and the new splinter functions can get awkward (not to mention 
inefficient unless inlined. But relying on inlining a function you went 
to all that trouble to 'out-line' seems perverse!).

(6) If the code is under development, this gets more hairy if it now 
involves statements being moved across functions. (Imagine trying to 
reorganise your bookshelves but half your books are now with your 
neighbours.)

(7) If I want to move the entire function, I now have all these extra 
dependencies. And any tools that lists all the functions is going to 
cluttered with all these new functions with ill-defined roles.

In short, creating all this mess for the sake of a avoiding gotos is 
crazy. (There are ways of encapsulating blocks of code but keeping them 
within the function, such as local functions or something like the 'flow 
blocks' that Rick was on about, but those are not standard.)

-- 
Bartc

[toc] | [prev] | [next] | [standalone]


#56018

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2015-01-07 20:19 +0000
Message-ID<slrnmar573.4et.grahn+nntp@frailea.sa.invalid>
In reply to#55981
On Tue, 2015-01-06, BartC wrote:
> On 05/01/2015 22:02, Jorgen Grahn wrote:
>> On Mon, 2015-01-05, BartC wrote:
>>> On 05/01/2015 00:35, Richard Heathfield wrote:
>> ...
>>>> Whilst it is certainly true that I've heard that goto isn't good, I actually
>>>> have personal experience of its not being good.
>>>
>>> You might, nevertheless, prefer reading other people's code that uses
>>> gotos sensibly and consistently, to alternates that involve tortuous
>>> logic or where the action keep shifting to dozens of extra functions
>>> (with the complicated interfaces required to allow them to share common
>>> data).
>>
>> I think I was the one who mentioned using functional decomposition
>> instead of goto.  I can assure you, that doesn't end up as "dozens of
>> extra functions", "complicated interfaces" or shared common data.
>
> Could you take any function, and turn any block of block within it into 
> a second function? Could you do that several times to create several new 
> functions?

Yes, but that would suck.  You sometimes see people do that: take a
big function, aim roughly and split it into two or three equal-sizes
parts.  The result: smaller functions, but even worse code!

So that's not at all what I'm arguing for.  I probably should explain
what I /really/ mean, but I find it difficult.

An example will have to do: a hexdump routine I use in many places. I
split it three times ... dump() being split out mostly to keep the
user interface types (void*) away from the logic. You could say that
the interface to hexdump() is a fourth split -- I expect the caller to
loop until the input is consumed.

Note that I only document the public function in this case -- there's
not a lot more that needs to be said.


    static char nybble(uint8_t n)
    {
        if(n<10) return '0' + n;
        return 'a' + (n-10);
    }

    static const uint8_t* dump(char* buf, size_t size,
                               const uint8_t* const begin,
                               const uint8_t* const end)
    {
        const uint8_t* p = begin;

        if(size>2 && p!=end) {
            *buf++ = nybble((*p) >> 4);
            *buf++ = nybble((*p) & 0x0f);
            size -= 2;
            p++;
        }
        while(size>3 && p!=end) {
            *buf++ = ' ';
            *buf++ = nybble((*p) >> 4);
            *buf++ = nybble((*p) & 0x0f);
            size -= 3;
            p++;
        }
        *buf++ = '\0';
        return p;
    }

    /**
     * Format [begin .. end) as hex octets, as much of it
     * which fits in 'buf', of size 'size'.  'size' must
     * allow room for the '\0' terminator (for example, be
     * non-zero).
     *
     * Returns the first octet not formatted.
     *
     * This way, you can e.g. specify a buffer of 3*8 characters,
     * loop until hexdump() returns 'end', printing the buffer + '\n'
     * at every iteration -- and you have a hexdump with 8 octets
     * per line.
     *
     */
    const void* hexdump(char* buf, size_t size,
                        const void* begin, const void* end)
    {
        return dump(buf, size, begin, end);
    }


I have better examples -- where pages of messy code boiled down to
almost nothing, and boring bookkeeping became separated from the
"business logic" -- but that's code I wrote at work.

> I would have these problems:

We meant different things, but I still have some comments.

> (1) I would need to think of meaningful names for these function, ones 
> that are unique because now I'm in the global name-space.

Unless the public function is defined inline in a header, they will be
static, so it's not too bad.

> (2) I would need to decide where to place those functions. Perhaps the 
> original function was part of nice, tidy cluster with related 
> functionality, but it's not going to be tidy much longer!

Right before their first use is my rule.  I don't think of the result
as worse than a series of too-complex functions.

If corporate coding standards say you must place all of your static
functions at the top or bottom of the file -- then I give up and go
for too-complex functions instead.  You can't navigate a ghetto of
random helper functions: they won't make sense outside their context,
and the context is the one or two functions which use them.

> (3) I'd need to document the functions and explain why each is only 
> called from one place. Also what it actually does which is harder 
> because it might not be meaningful outside its original context

Solution: keep it in its original context.

> (4) I'd have to document the new calls in the original function.

If you document at that detailed level, I guess you had to document
the code replaced by the call as well?

> (5) If the blocks of code made use of local variables (using or setting 
> their values) then this is where the interface between the original 
> function and the new splinter functions can get awkward

Yeah, you cannot split in a such a way that you're splitting "across"
a lot of variables.  I'm not saying it's always easy to avoid.  This
way of improving code by splitting things out isn't a silver bullet in
any way.  It's just a tool, and quite often I find I cannot apply it.

> (not to mention 
> inefficient unless inlined. But relying on inlining a function you went 
> to all that trouble to 'out-line' seems perverse!).

Don't understand what you're saying here: I assume that when I write a
static function, my compiler will inline as it sees fit.

> (6) If the code is under development, this gets more hairy if it now 
> involves statements being moved across functions. (Imagine trying to 
> reorganise your bookshelves but half your books are now with your 
> neighbours.)
>
> (7) If I want to move the entire function, I now have all these extra 
> dependencies.

You just move it, together with the helper functions.

> And any tools that lists all the functions is going to 
> cluttered with all these new functions with ill-defined roles.

You'll need better tools, then: tools that can highlight a translation
unit's public functions and downplay the static ones.

> In short, creating all this mess for the sake of a avoiding gotos is 
> crazy.

That's not the main reason I do it -- I just noted that IME doing it
seems to also help the error handling logic.

> (There are ways of encapsulating blocks of code but keeping them 
> within the function, such as local functions or something like the 'flow 
> blocks' that Rick was on about, but those are not standard.)

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#56032

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2015-01-07 16:05 -0800
Message-ID<265c9e8c-9af4-4bbc-82e9-0801d11ad853@googlegroups.com>
In reply to#56018
On Wednesday, January 7, 2015 8:19:58 PM UTC, Jorgen Grahn wrote:
> On Tue, 2015-01-06, BartC wrote:
> > On 05/01/2015 22:02, Jorgen Grahn wrote:
> >> On Mon, 2015-01-05, BartC wrote:
> >>> On 05/01/2015 00:35, Richard Heathfield wrote:
> >> ...
> >>>> Whilst it is certainly true that I've heard that goto isn't good, I actually
> >>>> have personal experience of its not being good.
> >>>
> >>> You might, nevertheless, prefer reading other people's code that uses
> >>> gotos sensibly and consistently, to alternates that involve tortuous
> >>> logic or where the action keep shifting to dozens of extra functions
> >>> (with the complicated interfaces required to allow them to share common
> >>> data).
> >>
> >> I think I was the one who mentioned using functional decomposition
> >> instead of goto.  I can assure you, that doesn't end up as "dozens of
> >> extra functions", "complicated interfaces" or shared common data.
> >
> > Could you take any function, and turn any block of block within it into 
> > a second function? Could you do that several times to create several new 
> > functions?
> 
> Yes, but that would suck.  You sometimes see people do that: take a
> big function, aim roughly and split it into two or three equal-sizes
> parts.  The result: smaller functions, but even worse code!
> 
> So that's not at all what I'm arguing for.  I probably should explain
> what I /really/ mean, but I find it difficult.
> 
> An example will have to do: a hexdump routine I use in many places. I
> split it three times ... dump() being split out mostly to keep the
> user interface types (void*) away from the logic. You could say that
> the interface to hexdump() is a fourth split -- I expect the caller to
> loop until the input is consumed.
> 
> Note that I only document the public function in this case -- there's
> not a lot more that needs to be said.
> 
> 
>     static char nybble(uint8_t n)
>     {
>         if(n<10) return '0' + n;
>         return 'a' + (n-10);
>     }
> 
>     static const uint8_t* dump(char* buf, size_t size,
>                                const uint8_t* const begin,
>                                const uint8_t* const end)
>     {
>         const uint8_t* p = begin;
> 
>         if(size>2 && p!=end) {
>             *buf++ = nybble((*p) >> 4);
>             *buf++ = nybble((*p) & 0x0f);
>             size -= 2;
>             p++;
>         }
>         while(size>3 && p!=end) {
>             *buf++ = ' ';
>             *buf++ = nybble((*p) >> 4);
>             *buf++ = nybble((*p) & 0x0f);
>             size -= 3;
>             p++;
>         }
>         *buf++ = '\0';
>         return p;
>     }
> 
>     /**
>      * Format [begin .. end) as hex octets, as much of it
>      * which fits in 'buf', of size 'size'.  'size' must
>      * allow room for the '\0' terminator (for example, be
>      * non-zero).
>      *
>      * Returns the first octet not formatted.
>      *
>      * This way, you can e.g. specify a buffer of 3*8 characters,
>      * loop until hexdump() returns 'end', printing the buffer + '\n'
>      * at every iteration -- and you have a hexdump with 8 octets
>      * per line.
>      *
>      */
>     const void* hexdump(char* buf, size_t size,
>                         const void* begin, const void* end)
>     {
>         return dump(buf, size, begin, end);
>     }
> 
> 
> I have better examples -- where pages of messy code boiled down to
> almost nothing, and boring bookkeeping became separated from the
> "business logic" -- but that's code I wrote at work.
> 

Here's a one-function version.

It's partly a matter of taste. But the one function version can be 
snipped and pasted into a file being debugged more easily. 

const void *hexdump2(char *buf, int size, const void *begin, const void *end)
{
	const unsigned char *ptr = begin;
	static char *hexdigits = "0123456789abcdef";
	int N = 0;

	if (size <= 0 || ptr == end)
		return ptr;

	while (1)
	{
		if (N >= size - 2)
			break;
		buf[N++] = hexdigits[(*ptr >> 4) & 0x0F];
		if (N >= size - 1)
			break;
		buf[N++] = hexdigits[*ptr & 0x0F];
		ptr++;
		if (ptr == end)
			break;
		if (N >= size - 3)
			break;
		buf[N++] = ' ';
	}
	buf[N++] = 0;
	return ptr;
}

[toc] | [prev] | [next] | [standalone]


#56040

Fromglen herrmannsfeldt <gah@ugcs.caltech.edu>
Date2015-01-08 08:29 +0000
Message-ID<m8lf5m$a9c$2@speranza.aioe.org>
In reply to#56032
(snip, someone wrote)
>> I have better examples -- where pages of messy code boiled down to
>> almost nothing, and boring bookkeeping became separated from the
>> "business logic" -- but that's code I wrote at work.

I have seen some strange code when people were paid by the lines
of code written. 

-- glen

[toc] | [prev] | [next] | [standalone]


#56185

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2015-01-12 22:08 +0000
Message-ID<slrnmb8hf6.4et.grahn+nntp@frailea.sa.invalid>
In reply to#56032
On Thu, 2015-01-08, Malcolm McLean wrote:
> On Wednesday, January 7, 2015 8:19:58 PM UTC, Jorgen Grahn wrote:
...
>> An example will have to do: a hexdump routine I use in many places. I
>> split it three times ... dump() being split out mostly to keep the
>> user interface types (void*) away from the logic. You could say that
>> the interface to hexdump() is a fourth split -- I expect the caller to
>> loop until the input is consumed.
>> 
>> Note that I only document the public function in this case -- there's
>> not a lot more that needs to be said.

...

> Here's a one-function version.
>
> It's partly a matter of taste. But the one function version can be 
> snipped and pasted into a file being debugged more easily. 
>
> const void *hexdump2(char *buf, int size, const void *begin, const void *end)
> {
> 	const unsigned char *ptr = begin;
> 	static char *hexdigits = "0123456789abcdef";
> 	int N = 0;
>
> 	if (size <= 0 || ptr == end)
> 		return ptr;
>
> 	while (1)
> 	{
> 		if (N >= size - 2)
> 			break;
> 		buf[N++] = hexdigits[(*ptr >> 4) & 0x0F];
> 		if (N >= size - 1)
> 			break;
> 		buf[N++] = hexdigits[*ptr & 0x0F];
> 		ptr++;
> 		if (ptr == end)
> 			break;
> 		if (N >= size - 3)
> 			break;
> 		buf[N++] = ' ';
> 	}
> 	buf[N++] = 0;
> 	return ptr;
> }

I haven't read it in detail, but it seems like an improvement on my
version. I'm pleasantly surprised that void* versus const unsigned
char* wasn't a real problem (didn't affect readability) after all.

The fact that it's only one self-contained function isn't a plus from
my point of view, though: if I want such a function, I'll copy
hexdump.c and hexdump.h and link with them.  Less of a hassle than
copy & pasting just a function IMO.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [next] | [standalone]


#58890

FromTim Rentsch <txr@alumni.caltech.edu>
Date2015-03-24 20:35 -0700
Message-ID<kfnmw31eolx.fsf@x-alumni2.alumni.caltech.edu>
In reply to#56185
Jorgen Grahn <grahn+nntp@snipabacken.se> writes:

> On Thu, 2015-01-08, Malcolm McLean wrote:
>> On Wednesday, January 7, 2015 8:19:58 PM UTC, Jorgen Grahn wrote:
>
> ...
>
>>> An example will have to do:  a hexdump routine I use in many
>>> places.  I split it three times ... dump() being split out mostly
>>> to keep the user interface types (void*) away from the logic.  You
>>> could say that the interface to hexdump() is a fourth split -- I
>>> expect the caller to loop until the input is consumed.
>>>
>>> Note that I only document the public function in this case --
>>> there's not a lot more that needs to be said.
>
> ...
>
>> Here's a one-function version.  [snip]
>>
>> const void *hexdump2(char *buf, int size, const void *begin, const void *end)
>> {
>> 	const unsigned char *ptr = begin;
>> 	static char *hexdigits = "0123456789abcdef";
>> 	int N = 0;
>>
>> 	if (size <= 0 || ptr == end)
>> 		return ptr;
>>
>> 	while (1)
>> 	{
>> 		if (N >= size - 2)
>> 			break;
>> 		buf[N++] = hexdigits[(*ptr >> 4) & 0x0F];
>> 		if (N >= size - 1)
>> 			break;
>> 		buf[N++] = hexdigits[*ptr & 0x0F];
>> 		ptr++;
>> 		if (ptr == end)
>> 			break;
>> 		if (N >= size - 3)
>> 			break;
>> 		buf[N++] = ' ';
>> 	}
>> 	buf[N++] = 0;
>> 	return ptr;
>> }
>
> I haven't read it in detail, but it seems like an improvement on
> my version.  I'm pleasantly surprised that void* versus const
> unsigned char* wasn't a real problem (didn't affect readability)
> after all.  [snip unrelated]

For what you want to do I think having a single function produces
a better result (I tried several other formulations).  The
definition above though is longer and more complicated than it
needs to be.  The example below retains the interface and semantics
of the original, not counting the reliance there on alphabetic
characters being contiguous, the unstated reliance here on
CHAR_BIT == 8, and the added 'restrict' qualifiers which
presumably also could apply to the original:

const void*
hexdump( char *restrict p, size_t size, const void* begin, const void* end ){
    static char hex[] = "0123456789abcdef";
    const unsigned char *restrict q = begin;
    size_t k = ((char*)end - (char*)begin) * 3;

    for(   size_t n = k < size ? k : size;   n > 2;   q++,  n -= 3   ){
        *p++ = hex[ *q / 16 ];
        *p++ = hex[ *q % 16 ];
        if(  n > 5  )  *p++ = ' ';
    }

    *p = 0;
    return  q;
}

[toc] | [prev] | [next] | [standalone]


#58918

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2015-03-25 08:43 -0700
Message-ID<69396a4c-1349-45ac-a040-02565e9c9c93@googlegroups.com>
In reply to#58890
On Wednesday, March 25, 2015 at 3:35:19 AM UTC, Tim Rentsch wrote:
> 
> For what you want to do I think having a single function produces
> a better result (I tried several other formulations).  The
> definition above though is longer and more complicated than it
> needs to be.  The example below retains the interface and semantics
> of the original, not counting the reliance there on alphabetic
> characters being contiguous, the unstated reliance here on
> CHAR_BIT == 8, and the added 'restrict' qualifiers which
> presumably also could apply to the original:
> 
> const void*
> hexdump( char *restrict p, size_t size, const void* begin, const void* end ){
>     static char hex[] = "0123456789abcdef";
>     const unsigned char *restrict q = begin;
>     size_t k = ((char*)end - (char*)begin) * 3;
> 
>     for(   size_t n = k < size ? k : size;   n > 2;   q++,  n -= 3   ){
>         *p++ = hex[ *q / 16 ];
>         *p++ = hex[ *q % 16 ];
>         if(  n > 5  )  *p++ = ' ';
>     }
> 
>     *p = 0;
>     return  q;
> }
>
>
I believe the restrict is superfluous, as a const-qualified pointer can be assumed not to
alias a non-const pointer.
It is a bit tighter-coded than mine, however.

[toc] | [prev] | [next] | [standalone]


#58987

FromTim Rentsch <txr@alumni.caltech.edu>
Date2015-03-25 16:38 -0700
Message-ID<kfnsics8x6m.fsf@x-alumni2.alumni.caltech.edu>
In reply to#58918
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:

> On Wednesday, March 25, 2015 at 3:35:19 AM UTC, Tim Rentsch wrote:
>
>> For what you want to do I think having a single function produces
>> a better result (I tried several other formulations).  The
>> definition above though is longer and more complicated than it
>> needs to be.  The example below retains the interface and semantics
>> of the original, not counting the reliance there on alphabetic
>> characters being contiguous, the unstated reliance here on
>> CHAR_BIT == 8, and the added 'restrict' qualifiers which
>> presumably also could apply to the original:
>>
>> const void*
>> hexdump( char *restrict p, size_t size, const void* begin, const void* end ){
>>     static char hex[] = "0123456789abcdef";
>>     const unsigned char *restrict q = begin;
>>     size_t k = ((char*)end - (char*)begin) * 3;
>>
>>     for(   size_t n = k < size ? k : size;   n > 2;   q++,  n -= 3   ){
>>         *p++ = hex[ *q / 16 ];
>>         *p++ = hex[ *q % 16 ];
>>         if(  n > 5  )  *p++ = ' ';
>>     }
>>
>>     *p = 0;
>>     return  q;
>> }
>
> I believe the restrict is superfluous, as a const-qualified
> pointer can be assumed not to alias a non-const pointer.

No, that assumption is not safe in general.  In fact I ran into a
case the other day very much like this one, where one pointed-to
type was const and the other wasn't, where adding 'restrict's
improved the code that was generated.

[toc] | [prev] | [next] | [standalone]


#59041

FromStephen Sprunk <stephen@sprunk.org>
Date2015-03-26 15:43 -0500
Message-ID<mf1r22$gc7$1@dont-email.me>
In reply to#58918
On 25-Mar-15 10:43, Malcolm McLean wrote:
> I believe the restrict is superfluous, as a const-qualified pointer
> can be assumed not to alias a non-const pointer. It is a bit
> tighter-coded than mine, however.

Why would one assume that?

char *p = malloc(1);
const char *q = p;
foo(p,q);

AIUI, the usual assumption is that pointers to different base types
won't alias, but qualifiers such as const aren't considered.

S

-- 
Stephen Sprunk         "God does not play dice."  --Albert Einstein
CCIE #3723         "God is an inveterate gambler, and He throws the
K5SSS        dice at every possible opportunity." --Stephen Hawking

[toc] | [prev] | [next] | [standalone]


#59144

FromTim Rentsch <txr@alumni.caltech.edu>
Date2015-03-28 08:57 -0700
Message-ID<kfnk2y1867u.fsf@x-alumni2.alumni.caltech.edu>
In reply to#59041
Stephen Sprunk <stephen@sprunk.org> writes:

> On 25-Mar-15 10:43, Malcolm McLean wrote:
>> I believe the restrict is superfluous, as a const-qualified pointer
>> can be assumed not to alias a non-const pointer.  It is a bit
>> tighter-coded than mine, however.
>
> Why would one assume that?
>
> char *p = malloc(1);
> const char *q = p;
> foo(p,q);
>
> AIUI, the usual assumption is that pointers to different base
> types won't alias,

That's mostly right (not counting character types), but there is an
exception for the unsigned version of a signed integer type, and
vice versa.

> but qualifiers such as const aren't considered.

Right;  generally the presence of absence of qualifiers
makes no difference.  A note of caution, however - gcc
has an option for 'strict aliasing', which AIUI actually
is more restrictive about what types can alias than the
C standard allows.  That would mean that (some) programs
that are legal and defined ISO C may exhibit non-conforming
behavior if compiled using gcc's strict aliasing option.

[toc] | [prev] | [next] | [standalone]


#56037

FromIan Collins <ian-news@hotmail.com>
Date2015-01-08 13:23 +1300
Message-ID<ch6108FpgmlU10@mid.individual.net>
In reply to#56018
Jorgen Grahn wrote:
>
> I have better examples -- where pages of messy code boiled down to
> almost nothing, and boring bookkeeping became separated from the
> "business logic" -- but that's code I wrote at work.

Some of the more common instances I see are switch statements with way 
too much code for each case which cries out to be extracted.  What's 
easier to follow:

switch( bla )
{
   case one:

    // 30 lines of code
    break;

   case two:

    // 40 lines of code
    break;

   case three:

    // 30 lines of code
    break;

   etc
}

or

switch( bla )
{
   case one:

    process_one();
    break;

   case two:

    process_two();
    break;

   case three:

    process_three();
    break;

   etc
}


The second is often how I design a function, effectively turning 
psudo-code (the functions calls) into real code.

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#56038

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2015-01-07 16:53 -0800
Message-ID<1660e7ed-5e31-423f-87b0-d3020c098863@googlegroups.com>
In reply to#56037
On Thursday, January 8, 2015 12:23:46 AM UTC, Ian Collins wrote:
> 
> Some of the more common instances I see are switch statements with way 
> too much code for each case which cries out to be extracted.  What's 
> easier to follow:
> 
A Windows callback is often like that.

I usually break it up, so a paint message will call "paint me",
a create will call a private constructor and set the user pointer to 
it, and so on.
However a Windows callback isn't really a single function, it's lots
of functions with different parameters artificially packaged into one.

[toc] | [prev] | [next] | [standalone]


#56039

FromIan Collins <ian-news@hotmail.com>
Date2015-01-08 14:11 +1300
Message-ID<ch63prFpgmjU1@mid.individual.net>
In reply to#56038
Malcolm McLean wrote:
> On Thursday, January 8, 2015 12:23:46 AM UTC, Ian Collins wrote:
>>
>> Some of the more common instances I see are switch statements with way
>> too much code for each case which cries out to be extracted.  What's
>> easier to follow:
>>
> A Windows callback is often like that.
>
> I usually break it up, so a paint message will call "paint me",
> a create will call a private constructor and set the user pointer to
> it, and so on.
> However a Windows callback isn't really a single function, it's lots
> of functions with different parameters artificially packaged into one.

Lots (maybe most?) functions with big switch statements fall into that 
category.  The usually handle some form of protocol where the message 
type is the first byte or two, the rest specific messages.  One I had to 
re-write recently was a single switch with over a thousand lines used to 
process Apple MFI protocol messages.

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#56029

FromIan Collins <ian-news@hotmail.com>
Date2015-01-08 11:10 +1300
Message-ID<ch5p66FpgmlU8@mid.individual.net>
In reply to#55981
BartC wrote:
> On 05/01/2015 22:02, Jorgen Grahn wrote:
>> On Mon, 2015-01-05, BartC wrote:
>>> On 05/01/2015 00:35, Richard Heathfield wrote:
>> ...
>>>> Whilst it is certainly true that I've heard that goto isn't good, I actually
>>>> have personal experience of its not being good.
>>>
>>> You might, nevertheless, prefer reading other people's code that uses
>>> gotos sensibly and consistently, to alternates that involve tortuous
>>> logic or where the action keep shifting to dozens of extra functions
>>> (with the complicated interfaces required to allow them to share common
>>> data).
>>
>> I think I was the one who mentioned using functional decomposition
>> instead of goto.  I can assure you, that doesn't end up as "dozens of
>> extra functions", "complicated interfaces" or shared common data.
>
> Could you take any function, and turn any block of block within it into
> a second function? Could you do that several times to create several new
> functions?

Yes, it is a common refactoring known as "extract method".

> I would have these problems:
>
> (1) I would need to think of meaningful names for these function, ones
> that are unique because now I'm in the global name-space.

Not if they are static.  If the block of code is doing something 
specific (which is why you are extracting it), choosing a name should be 
straightforward.

> (2) I would need to decide where to place those functions. Perhaps the
> original function was part of nice, tidy cluster with related
> functionality, but it's not going to be tidy much longer!

Indeed, it will be tidier once all the clutter is removed...

> (3) I'd need to document the functions and explain why each is only
> called from one place. Also what it actually does which is harder
> because it might not be meaningful outside its original context

Very often the cue to extract a function is the need to comment a block 
of code, so (if it is still required) the comment can go with it. 
Choosing a meaningful name for the function will often remove the need 
for the comment.  Why would you have to explain why it is called from 
one place?

> (4) I'd have to document the new calls in the original function.

Why?  Isn't calling them clear enough?

> (5) If the blocks of code made use of local variables (using or setting
> their values) then this is where the interface between the original
> function and the new splinter functions can get awkward (not to mention
> inefficient unless inlined. But relying on inlining a function you went
> to all that trouble to 'out-line' seems perverse!).

In that case there are a couple of options:

1) do nothing
2) if there aren't too many, pass the local variables as parameters
3) if there will be several functions working on the same data, consider 
using a struct.

> (6) If the code is under development, this gets more hairy if it now
> involves statements being moved across functions. (Imagine trying to
> reorganise your bookshelves but half your books are now with your
> neighbours.)

Eh?

If the code is under development, isn't it easier to test smaller chunks?

> (7) If I want to move the entire function, I now have all these extra
> dependencies. And any tools that lists all the functions is going to
> cluttered with all these new functions with ill-defined roles.

Surely the roles are well defined, which is why you extracted the 
functions in the first place.

> In short, creating all this mess for the sake of a avoiding gotos is
> crazy. (There are ways of encapsulating blocks of code but keeping them
> within the function, such as local functions or something like the 'flow
> blocks' that Rick was on about, but those are not standard.)

More often than not functions are extracted to simplify and improve the 
code, not to avoid a goto.  If you work with small functions from the 
beginning, you are far less likely to end up with a function the uses goto.

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#55970

FromJohn Bode <jfbode1029@gmail.com>
Date2015-01-05 15:54 -0800
Message-ID<3702c3f1-6594-47ee-b30c-42604139d92d@googlegroups.com>
In reply to#55859
On Sunday, January 4, 2015 6:35:13 PM UTC-6, Richard Heathfield wrote:
> Malcolm McLean wrote:
> 
> > On Sunday, January 4, 2015 3:30:58 PM UTC, Clark Smith wrote:
> >> 
> >> The idea is that some actions must be taken that must be correctly undone
> >> in case of error. With this approach, everything is centralized at the
> >> end of the loop.
> >>
> > That's basically sensible.
> > 
> > Typically a function is constructing some complex data structure. If
> > it encounters an error, the structure is in a half-completed state,
> > so needs deconstructing with care to avoid memory leaks. So you want
> > the code in one place.
> > 
> > However just use goto error_exit. Some people have heard that goto
> > isn't good. Whilst the person who made that statement (Dijkstra)
> > was no idiot, repeating him like a mantra isn't very helpful,
> > and requires no thought.
> 
> Whilst it is certainly true that I've heard that goto isn't good, I actually 
> have personal experience of its not being good. My first programming 
> language was VS/BASIC. In 1982-83, I wrote several programs that, to my 
> inexperienced eye, seemed to me to be very large - hundreds and hundreds of 
> lines of code! These programs were littered with GOTO statements. (It didn't 
> help that I didn't know about GOSUB.) On far too many occasions, I wanted to 
> change the behaviour of a particular program in a particular way, only to 
> find that I simply couldn't do it - the flow of control was just too... 
> well, not to put too fine a point on it, too screwed-up.
> 

BTDTGTFTS.  I've told that story a couple of dozen times on this forum:
huge, monolithic main() function, gotos out the wazoo branching either 
direction, literally several hundred distinct variables, etc.  Slow, brittle
as hell.  Told the customer we could either rewrite it from scratch or they
could buy faster hardware.  They bought faster hardware.

This isn't that situation.  He wants to bail out of a multi-step operation
that may fail at any point, and needs to back out anything that was done up
to that point.  Lacking a structured exception handling mechanism, using a 
goto is probably the least-painful option available.

  if ( condition_a_fails )
    goto error_handler;

  if ( condition_b_fails )
    goto error_handler;

  ...

error_handler:
  if ( operation_a_run )
    backout_operation_a;

  if ( operation_b_run )
    backout_operation_a;
    backout_operation_b;  
    
etc.

To the OP: if you feel like repeatedly stabbing yourself with a spork might
be fun, you could try C's excuse for an exception handling mechanism,
setjmp and longjmp.  I took your toy example and rewrote it as follows:

#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>

int f( const char *x, int l )
{
  char *a = NULL;
  char *b = NULL;

  enum error_type {OKAY, SOURCE_NULL, BAD_LENGTH, MALLOC_FAILED_A, MALLOC_FAILED_B} ;
  jmp_buf TryBlock;
  int diagnostic = 0;
  enum error_type status = setjmp( TryBlock ); // "try:
  if ( status != OKAY )
  {
    diagnostic = 1;

    switch( status )
    {
      case SOURCE_NULL:
        printf( "x is NULL!\n" );
        break;

      case BAD_LENGTH:
        printf( "l is > 255!\n" );
        break;

      case MALLOC_FAILED_A:
        printf( "Could not allocate memory for a!\n" );
        break;

      case MALLOC_FAILED_B:
        printf( "Could not allocate memory for b!\n" );
        printf( "Freeing allocated memory for a\n" );
        free( a );
        break;

      default:
        break;
    }
  }
  else
  {
    if ( !x )
      longjmp( TryBlock, SOURCE_NULL );

    if ( l > 255 )
      longjmp( TryBlock, BAD_LENGTH );

    a = malloc( 256 );
    if ( !a )
      longjmp( TryBlock, MALLOC_FAILED_A );

    b = malloc( 2*256 );
    if ( !b )
      longjmp( TryBlock, MALLOC_FAILED_B );

    strcpy( a, x );
    strcpy( b, x );

    printf( "a = %s\n", a );
    printf( "b = %s\n", b );

    free( a );
    free( b );
  }

  return diagnostic;
}

int main( void )
{
  if ( f( "test", 5 ) )
    printf( "test(\"test\", 5) failed with an exception\n" );
  if ( f( NULL, 0 ) )
    printf( "test(NULL, 0) failed with an exception\n" );
  if ( f( "test", 257 ) )
    printf( "test(\"test\", 257) failed with an exception\n" );
  exit( 0 );
}

When run, it gives the output

a = test
b = test
x is NULL!
test(NULL, 0) failed with an exception
l is > 255!
test("test", 257) failed with an exception

If a condition fails, it bails immediately and executes the relevant
"exception" path.  Not a goto in sight.

It's also pretty gagariffic code. I tossed this off in about 20 minutes, so
no doubt it could be structured a bit better, but it'd still be a mess.  
Having to handle the exception cases *before* the "try" block is just
a bit counterintuitive.   

[toc] | [prev] | [next] | [standalone]


#55978

FromRosario193 <Rosario@invalid.invalid>
Date2015-01-06 11:03 +0100
Message-ID<ifanaahc4ec5vn7lm7t12antr1ja0l7g9j@4ax.com>
In reply to#55970
On Mon, 5 Jan 2015 15:54:34 -0800 (PST), John Bode wrote:
>On Sunday, January 4, 2015 6:35:13 PM UTC-6, Richard Heathfield wrote:
>> Malcolm McLean wrote:
>> 
>> > On Sunday, January 4, 2015 3:30:58 PM UTC, Clark Smith wrote:
>> >> 
>> >> The idea is that some actions must be taken that must be correctly undone
>> >> in case of error. With this approach, everything is centralized at the
>> >> end of the loop.
>> >>
>> > That's basically sensible.
>> > 
>> > Typically a function is constructing some complex data structure. If
>> > it encounters an error, the structure is in a half-completed state,
>> > so needs deconstructing with care to avoid memory leaks. So you want
>> > the code in one place.
>> > 
>> > However just use goto error_exit. Some people have heard that goto
>> > isn't good. Whilst the person who made that statement (Dijkstra)
>> > was no idiot, repeating him like a mantra isn't very helpful,
>> > and requires no thought.
>> 
>> Whilst it is certainly true that I've heard that goto isn't good, I actually 
>> have personal experience of its not being good. My first programming 
>> language was VS/BASIC. In 1982-83, I wrote several programs that, to my 
>> inexperienced eye, seemed to me to be very large - hundreds and hundreds of 
>> lines of code! These programs were littered with GOTO statements. (It didn't 
>> help that I didn't know about GOSUB.) On far too many occasions, I wanted to 
>> change the behaviour of a particular program in a particular way, only to 
>> find that I simply couldn't do it - the flow of control was just too... 
>> well, not to put too fine a point on it, too screwed-up.
>> 
>
>BTDTGTFTS.  I've told that story a couple of dozen times on this forum:
>huge, monolithic main() function, gotos out the wazoo branching either 
>direction, literally several hundred distinct variables, etc.  Slow, brittle
>as hell.  Told the customer we could either rewrite it from scratch or they
>could buy faster hardware.  They bought faster hardware.
>
>This isn't that situation.  He wants to bail out of a multi-step operation
>that may fail at any point, and needs to back out anything that was done up
>to that point.  Lacking a structured exception handling mechanism, using a 
>goto is probably the least-painful option available.
>
>  if ( condition_a_fails )
>    goto error_handler;
>
>  if ( condition_b_fails )
>    goto error_handler;
>
>  ...
>
>error_handler:
>  if ( operation_a_run )
>    backout_operation_a;
>
>  if ( operation_b_run )
>    backout_operation_a;
>    backout_operation_b;  
>    
>etc.
>
>To the OP: if you feel like repeatedly stabbing yourself with a spork might
>be fun, you could try C's excuse for an exception handling mechanism,
>setjmp and longjmp.  I took your toy example and rewrote it as follows:
>
>#include <stdio.h>
>#include <setjmp.h>
>#include <stdlib.h>
>#include <string.h>
>
>int f( const char *x, int l )
>{
>  char *a = NULL;
>  char *b = NULL;
>
>  enum error_type {OKAY, SOURCE_NULL, BAD_LENGTH, MALLOC_FAILED_A, MALLOC_FAILED_B} ;
>  jmp_buf TryBlock;
>  int diagnostic = 0;
>  enum error_type status = setjmp( TryBlock ); // "try:
>  if ( status != OKAY )
>  {
>    diagnostic = 1;
>
>    switch( status )
>    {
>      case SOURCE_NULL:
>        printf( "x is NULL!\n" );
>        break;
>
>      case BAD_LENGTH:
>        printf( "l is > 255!\n" );
>        break;
>
>      case MALLOC_FAILED_A:
>        printf( "Could not allocate memory for a!\n" );
>        break;
>
>      case MALLOC_FAILED_B:
>        printf( "Could not allocate memory for b!\n" );
>        printf( "Freeing allocated memory for a\n" );
>        free( a );
>        break;
>
>      default:
>        break;
>    }
>  }
>  else
>  {
>    if ( !x )
>      longjmp( TryBlock, SOURCE_NULL );
>
>    if ( l > 255 )
>      longjmp( TryBlock, BAD_LENGTH );
>
>    a = malloc( 256 );
>    if ( !a )
>      longjmp( TryBlock, MALLOC_FAILED_A );
>
>    b = malloc( 2*256 );
>    if ( !b )
>      longjmp( TryBlock, MALLOC_FAILED_B );
>
>    strcpy( a, x );
>    strcpy( b, x );
>
>    printf( "a = %s\n", a );
>    printf( "b = %s\n", b );
>
>    free( a );
>    free( b );
>  }
>
>  return diagnostic;
>}
>
>int main( void )
>{
>  if ( f( "test", 5 ) )
>    printf( "test(\"test\", 5) failed with an exception\n" );
>  if ( f( NULL, 0 ) )
>    printf( "test(NULL, 0) failed with an exception\n" );
>  if ( f( "test", 257 ) )
>    printf( "test(\"test\", 257) failed with an exception\n" );
>  exit( 0 );
>}
>
>When run, it gives the output
>
>a = test
>b = test
>x is NULL!
>test(NULL, 0) failed with an exception
>l is > 255!
>test("test", 257) failed with an exception
>
>If a condition fails, it bails immediately and executes the relevant
>"exception" path.  Not a goto in sight.
>
>It's also pretty gagariffic code. I tossed this off in about 20 minutes, so
>no doubt it could be structured a bit better, but it'd still be a mess.  
>Having to handle the exception cases *before* the "try" block is just
>a bit counterintuitive.   

if i would write one function as f() that does something...

#include <stdlib.h>
#include <string.h>
#include  <stdio.h>
#define  G     goto
#define  P     printf
#define  u8    unsigned char
#define  u32   unsigned int

/* 
  This function double cat the arg string x in res
  return  0 for all ok
           in the other case [error] 
           a pointer to a string that say what goes wrong
  in the case all ok the caller has free what res point...

  2 exit point one for error one for all ok
  no exceptions use 
*/
u8*  catcat(u8**  res, u8* x)
{u8   *a, *b, *c, *r;
 u32             len;
 
 r="Error Arg"; 
 if(res==0) {ex: return r; }
 *res=0;
 if(x==0) G  ex; 
      
 r="Error strlen";
 len=strlen(x);

/* all has its limit all routine that not take in count limits are
wrong by design... */
      // if(len>0x7FFFFF00)       G ex;
 if(len>0x7F)             G ex;

 r="Error malloc";
 if((a=malloc(len+1))==0) G ex;
 if((b=malloc(2*len+2))==0)
          {ex1: free(a);  G ex;}

 strcpy(a, x);
 strcpy(b, x);
 strcpy(b+len, a);
  
 free(a);

 *res=b;
 return  0;
}


int main(void)
{u8  *r, *res,  big[1024];
 u32                    i;

 if(r=catcat(&res, "test"))
        P("catcat(\"test\") failed with error %s\n", r);
 else   P("Result catcat(\"test\")=%s\n",  res);
 if(res) free(res);

 if(r=catcat(0, "test"))
        P("catcat(0, \"test\") failed with error %s\n", r);
 else   P("Result catcat(\"test\")=%s\n",  res);
 if(res) free(res);

 if(r=catcat(&res, 0 ))
        P("catcat(0) failed with error %s\n", r);
 else   P("Result catcat(\"test\")=%s\n",  res);
 if(res) free(res);

 for(i=0;i<1000; ++i)
        big[i]='c';
 big[i]=0;

 if(r=catcat(&res, big ))
        P("catcat(\"big\") failed with error %s\n", r);
 else   P("Result catcat(\"test\")=%s\n",  res);
 if(res) free(res);

 return  0;
}

[toc] | [prev] | [next] | [standalone]


#55979

FromRosario193 <Rosario@invalid.invalid>
Date2015-01-06 11:10 +0100
Message-ID<72dnaa9g01gvu94n6j6bvp0qsm770j1vra@4ax.com>
In reply to#55978
On Tue, 06 Jan 2015 11:03:34 +0100, Rosario193
<Rosario@invalid.invalid> wrote:



>#include <stdlib.h>
>#include <string.h>
>#include  <stdio.h>
>#define  G     goto
>#define  P     printf
>#define  u8    unsigned char
>#define  u32   unsigned int
>
>/* 
>  This function double cat the arg string x in res
>  return  0 for all ok
>           in the other case [error] 
>           a pointer to a string that say what goes wrong
>  in the case all ok the caller has free what res point...
>
>  2 exit point one for error one for all ok
>  no exceptions use 
>*/
>u8*  catcat(u8**  res, u8* x)
>{u8   *a, *b, *c, *r;
> u32             len;
> 
> r="Error Arg"; 
> if(res==0) {ex: return r; }
> *res=0;
> if(x==0) G  ex; 
>      
> r="Error strlen";
> len=strlen(x);
>
>/* all has its limit all routine that not take in count limits are
>wrong by design... */
>      // if(len>0x7FFFFF00)       G ex;
> if(len>0x7F)             G ex;
>
> r="Error malloc";
> if((a=malloc(len+1))==0) G ex;
> if((b=malloc(2*len+2))==0)
>          {ex1: free(a);  G ex;}
>
> strcpy(a, x);
> strcpy(b, x);
> strcpy(b+len, a);
>  
> free(a);
>
> *res=b;
> return  0;
>}
>
>
>int main(void)
>{u8  *r, *res,  big[1024];
> u32                    i;
>
> if(r=catcat(&res, "test"))
>        P("catcat(\"test\") failed with error %s\n", r);
> else   P("Result catcat(\"test\")=%s\n",  res);
> if(res) free(res);
>
> if(r=catcat(0, "test"))
>        P("catcat(0, \"test\") failed with error %s\n", r);
> else   P("Result catcat(\"test\")=%s\n",  res);
> if(res) free(res);
>
> if(r=catcat(&res, 0 ))
>        P("catcat(0) failed with error %s\n", r);
> else   P("Result catcat(\"test\")=%s\n",  res);
> if(res) free(res);
>
> for(i=0;i<1000; ++i)
>        big[i]='c';
> big[i]=0;
>
> if(r=catcat(&res, big ))
>        P("catcat(\"big\") failed with error %s\n", r);
> else   P("Result catcat(\"test\")=%s\n",  res);
> if(res) free(res);
>
> return  0;
>}


Result catcat("test")=testtest
catcat(0, "test") failed with error Error Arg
catcat(0) failed with error Error Arg
catcat("big") failed with error Error strlen

[toc] | [prev] | [next] | [standalone]


#55983

FromJohn Bode <jfbode1029@gmail.com>
Date2015-01-06 06:03 -0800
Message-ID<603b5f3b-908f-4b3b-99ca-511716c368ec@googlegroups.com>
In reply to#55978
On Tuesday, January 6, 2015 4:03:46 AM UTC-6, Rosario193 wrote:
[snip]
> 
> #include <stdlib.h>
> #include <string.h>
> #include  <stdio.h>
> #define  G     goto
> #define  P     printf
> #define  u8    unsigned char
> #define  u32   unsigned int
> 

*Please* don't do this.  Please don't suggest anyone else do it either.  Using
the preprocessor to redefine/create synonyms for keywords and types is bad 
juju.

It does *not* improve readability, and in my experience creates maintenance
headaches down the line.  

If you insist on aliases for type names, use typedef instead of the 
preprocessor:

    typedef unsigned char u8
    typedef unsigned int u32

but before you do that, check to make sure that uint8_t and uint32_t aren't
already defined in stdint.h

[toc] | [prev] | [next] | [standalone]


#55986

FromRichard Heathfield <invalid@see.sig.invalid>
Date2015-01-06 14:44 +0000
Message-ID<YwSqw.328173$Kl4.155860@fx01.am4>
In reply to#55983
John Bode wrote:

> On Tuesday, January 6, 2015 4:03:46 AM UTC-6, Rosario193 wrote:
> [snip]
>> 
>> #include <stdlib.h>
>> #include <string.h>
>> #include  <stdio.h>
>> #define  G     goto
>> #define  P     printf
>> #define  u8    unsigned char
>> #define  u32   unsigned int
>> 
> 
> *Please* don't do this.

He's been doing this for about 15 years, despite closely reasoned arguments 
against it, repeated frequently. Realistically, do you really think he's 
suddenly going to develop some kind of cluefulness at this point?

<snip>

-- 
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within

[toc] | [prev] | [next] | [standalone]


Page 3 of 6 — ← Prev page 1 2 [3] 4 5 6  Next page →

Back to top | Article view | comp.lang.c


csiph-web