Re: possible useful macros...

Discussion in 'C Programming' started by Dirk Zabel, May 24, 2012.

  1. Dirk Zabel

    Dirk Zabel Guest

    Am 24.05.2012 08:16, schrieb Robert Wessel:
    > On Wed, 23 May 2012 16:18:30 -0400, David T. Ashley
    > <> wrote:
    > [...]
    > Of course these categories overlap. Consider the advantages of a
    > common definition in case the value of pi changes...

    YMMD. But do you really prefer looking up the value of pi everytime to a
    simple

    #include <math.h>
    ...
    area = M_PI * r * r;

    ;-)

    What I really like is:

    #define CNEW(type) (type *)malloc(sizeof(type))

    so i get a compiler message if i try

    pear * pp = CNEW(apple);

    while
    pear * pp = malloc(sizeof(apple));

    will go undetected.

    -- Dirk
     
    Dirk Zabel, May 24, 2012
    #1
    1. Advertising

  2. Dirk Zabel

    Ian Collins Guest

    On 05/24/12 10:15 PM, Dirk Zabel wrote:
    > Am 24.05.2012 08:16, schrieb Robert Wessel:
    >> On Wed, 23 May 2012 16:18:30 -0400, David T. Ashley
    >> <> wrote:
    > > [...]
    >> Of course these categories overlap. Consider the advantages of a
    >> common definition in case the value of pi changes...

    > YMMD. But do you really prefer looking up the value of pi everytime to a
    > simple
    >
    > #include<math.h>
    > ...
    > area = M_PI * r * r;
    >
    > ;-)
    >
    > What I really like is:
    >
    > #define CNEW(type) (type *)malloc(sizeof(type))
    >
    > so i get a compiler message if i try
    >
    > pear * pp = CNEW(apple);
    >
    > while
    > pear * pp = malloc(sizeof(apple));
    >
    > will go undetected.


    Which is why people write

    pear* pp = malloc(sizeof *pp);

    --
    Ian Collins
     
    Ian Collins, May 24, 2012
    #2
    1. Advertising

  3. Dirk Zabel

    James Kuyper Guest

    On 05/24/2012 12:31 PM, Robert Wessel wrote:
    > On Thu, 24 May 2012 12:15:16 +0200, Dirk Zabel <>
    > wrote:
    >
    >> Am 24.05.2012 08:16, schrieb Robert Wessel:
    >>> On Wed, 23 May 2012 16:18:30 -0400, David T. Ashley
    >>> <> wrote:
    >>> [...]
    >>> Of course these categories overlap. Consider the advantages of a
    >>> common definition in case the value of pi changes...

    >> YMMD. But do you really prefer looking up the value of pi everytime to a
    >> simple
    >>
    >> #include <math.h>
    >> ...
    >> area = M_PI * r * r;
    >>
    >> ;-)
    >>
    >> What I really like is:
    >>
    >> #define CNEW(type) (type *)malloc(sizeof(type))
    >>
    >> so i get a compiler message if i try
    >>
    >> pear * pp = CNEW(apple);
    >>
    >> while
    >> pear * pp = malloc(sizeof(apple));
    >>
    >> will go undetected.

    >
    >
    > I've actually made that suggestion in c.l.c.:
    >
    > http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-11/msg02546.html
    >
    > Got the usual knee-jerk (and completely missing the point) "don't cast
    > malloc!" reply...


    It's not missing the point, it's seeing the point quite clearly, and
    rejecting it. There are only two possible errors when making a call to
    malloc(). The first is that the size is wrong; the type is irrelevant.
    The second is failing to check for the possibility of a null return
    value. The clc idiom

    pp = malloc(count * sizeof *pp);

    (or malloc(sizeof *pp) if count is guaranteed to be 1) is sufficient in
    itself to avoid the size error. Your alternative

    pp = CNEW(pear);

    has no advantage over that idiom. If pp is indeed a pear*, it will do
    the same thing as the clc idiom. If pp is not a pear*, the clc idiom
    will still produce the correct size, without requiring any changes,
    while CNEW(pear) will have to be changed.

    Can you identify a context in which the clc idiom will tolerate an
    incorrect use of malloc(), while CNEW() will not?
     
    James Kuyper, May 24, 2012
    #3
  4. Dirk Zabel

    Ben Pfaff Guest

    Dirk Zabel <> writes:

    > What I really like is:
    >
    > #define CNEW(type) (type *)malloc(sizeof(type))
    >
    > so i get a compiler message if i try
    >
    > pear * pp = CNEW(apple);
    >
    > while
    > pear * pp = malloc(sizeof(apple));
    >
    > will go undetected.


    I don't have a problem with your macro (although it only works
    with certain types), but:
    pear *pp = malloc(sizeof *pp);
    works just as well without a need for a special macro.
     
    Ben Pfaff, May 24, 2012
    #4
  5. Dirk Zabel

    Willem Guest

    Ben Pfaff wrote:
    ) Dirk Zabel <> writes:
    )
    )> What I really like is:
    )>
    )> #define CNEW(type) (type *)malloc(sizeof(type))
    )>
    )> so i get a compiler message if i try
    )>
    )> pear * pp = CNEW(apple);
    )>
    )> while
    )> pear * pp = malloc(sizeof(apple));
    )>
    )> will go undetected.
    )
    ) I don't have a problem with your macro (although it only works
    ) with certain types), but:
    ) pear *pp = malloc(sizeof *pp);
    ) works just as well without a need for a special macro.

    #define NEW(pp) (pp)=malloc(sizeof *(pp))

    pear *pp;
    NEW(pp);


    SaSW, Willem
    --
    Disclaimer: I am in no way responsible for any of the statements
    made in the above text. For all I know I might be
    drugged or something..
    No I'm not paranoid. You all think I'm paranoid, don't you !
    #EOT
     
    Willem, May 24, 2012
    #5
  6. Dirk Zabel

    Ben Pfaff Guest

    Willem <> writes:

    > Ben Pfaff wrote:
    > ) I don't have a problem with your macro (although it only works
    > ) with certain types), but:
    > ) pear *pp = malloc(sizeof *pp);
    > ) works just as well without a need for a special macro.
    >
    > #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >
    > pear *pp;
    > NEW(pp);


    If the goal is "write a macro to allocate memory and assign it to
    a variable", you have accomplished it. But I think that this
    makes code harder to reduce, without a clear benefit.
     
    Ben Pfaff, May 24, 2012
    #6
  7. On 2012-05-24 22:27, Ben Pfaff wrote:
    > Willem<> writes:
    >> #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >>
    >> pear *pp;
    >> NEW(pp);

    >
    > If the goal is "write a macro to allocate memory and assign it to
    > a variable", you have accomplished it. But I think that this
    > makes code harder to reduce, without a clear benefit.


    The clear benefit is that it makes memory allocation simpler and less
    error prone. Compare these two statements for instance:

    NEW(foo->bar->baz);

    foo->bar->baz = malloc(sizeof *(foo->bar->boz));


    /August
     
    August Karlstrom, May 24, 2012
    #7
  8. Dirk Zabel

    Ben Pfaff Guest

    August Karlstrom <> writes:

    > On 2012-05-24 22:27, Ben Pfaff wrote:
    >> Willem<> writes:
    >>> #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >>>
    >>> pear *pp;
    >>> NEW(pp);

    >>
    >> If the goal is "write a macro to allocate memory and assign it to
    >> a variable", you have accomplished it. But I think that this
    >> makes code harder to reduce, without a clear benefit.


    "to read", I meant, of course.

    > The clear benefit is that it makes memory allocation simpler and less
    > error prone. Compare these two statements for instance:
    >
    > NEW(foo->bar->baz);
    >
    > foo->bar->baz = malloc(sizeof *(foo->bar->boz));


    It would seem so at first glance, but I don't actually see that
    sort of error in practice, so I'm suspicious that this really
    overcomes the benefit of having code that is easy to read.
     
    Ben Pfaff, May 24, 2012
    #8
  9. Dirk Zabel

    Martin Shobe Guest

    Eric Sosman wrote:

    > On 5/24/2012 4:46 PM, August Karlstrom wrote:
    >> On 2012-05-24 22:27, Ben Pfaff wrote:
    >>> Willem<> writes:
    >>>> #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >>>>
    >>>> pear *pp;
    >>>> NEW(pp);
    >>>
    >>> If the goal is "write a macro to allocate memory and assign it to
    >>> a variable", you have accomplished it. But I think that this
    >>> makes code harder to reduce, without a clear benefit.

    >>
    >> The clear benefit is that it makes memory allocation simpler and less
    >> error prone. Compare these two statements for instance:
    >>
    >> NEW(foo->bar->baz);
    >>
    >> foo->bar->baz = malloc(sizeof *(foo->bar->boz));

    >
    > Aren't you overlooking what comes next?
    >
    > if (foo->bar->bas == NULL) ...
    > foo->bor->baz->answer = 42;
    > strcpy(fou->bar->baz->question,
    > "Life, the Universe -- everything");
    >
    > In other words, the code will refer to the new pointer at least
    > three times, usually more. That is, the suggested macro reduces
    > the number of uses of the pointer from N to N-1, but N >= 3
    > always, N >= 4 nearly always.
    >
    > If the pointer is cumbersome to spell, the programmer is very
    > likely to introduce a new variable anyhow:
    >
    > struct baz_s *newbaz = foo->bar->baz = malloc(sizeof *newbaz);
    > if (newbaz == NULL) ...
    > newbaz->answer = 42;
    > strcpy(newbaz->question, "Life, the Universe -- everything");
    >
    > In this pattern, the naive use of the macro would actually *increase*
    > the number of times the pointer is spelled out:
    >
    > NEW(foo->bar->baz);
    > struct baz_s *newbaz = foo->bar->baz;
    > ...


    Really? Why wouldn't the programmer use

    struct baz_s * newbaz = NEW(foo->bar->baz);

    (Not that I approve of the macro, but lets at least keep the criticisms
    legitimate.)

    [snip]

    Martin Shobe
     
    Martin Shobe, May 24, 2012
    #9
  10. Dirk Zabel

    Eric Sosman Guest

    On 5/24/2012 5:42 PM, Martin Shobe wrote:
    > Eric Sosman wrote:
    >
    >> On 5/24/2012 4:46 PM, August Karlstrom wrote:
    >>> On 2012-05-24 22:27, Ben Pfaff wrote:
    >>>> Willem<> writes:
    >>>>> #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >>>>>
    >>>>> pear *pp;
    >>>>> NEW(pp);
    >>>>
    >>>> If the goal is "write a macro to allocate memory and assign it to
    >>>> a variable", you have accomplished it. But I think that this
    >>>> makes code harder to reduce, without a clear benefit.
    >>>
    >>> The clear benefit is that it makes memory allocation simpler and less
    >>> error prone. Compare these two statements for instance:
    >>>
    >>> NEW(foo->bar->baz);
    >>>
    >>> foo->bar->baz = malloc(sizeof *(foo->bar->boz));

    >>
    >> Aren't you overlooking what comes next?
    >>
    >> if (foo->bar->bas == NULL) ...
    >> foo->bor->baz->answer = 42;
    >> strcpy(fou->bar->baz->question,
    >> "Life, the Universe -- everything");
    >>
    >> In other words, the code will refer to the new pointer at least
    >> three times, usually more. That is, the suggested macro reduces
    >> the number of uses of the pointer from N to N-1, but N>= 3
    >> always, N>= 4 nearly always.
    >>
    >> If the pointer is cumbersome to spell, the programmer is very
    >> likely to introduce a new variable anyhow:
    >>
    >> struct baz_s *newbaz = foo->bar->baz = malloc(sizeof *newbaz);
    >> if (newbaz == NULL) ...
    >> newbaz->answer = 42;
    >> strcpy(newbaz->question, "Life, the Universe -- everything");
    >>
    >> In this pattern, the naive use of the macro would actually *increase*
    >> the number of times the pointer is spelled out:
    >>
    >> NEW(foo->bar->baz);
    >> struct baz_s *newbaz = foo->bar->baz;
    >> ...

    >
    > Really? Why wouldn't the programmer use
    >
    > struct baz_s * newbaz = NEW(foo->bar->baz);
    >
    > (Not that I approve of the macro, but lets at least keep the criticisms
    > legitimate.)


    Kind of rends the veil of opacity, don't you think? The
    programmer now needs to know that NEW expands not just to any
    old code sequence that allocates memory and assigns to its
    argument, but specifically to an expression -- in particular,
    to an expression that yields a pointer to the new memory.

    Besides, in what you snipped I described how the programmer
    would (IMHO) quite likely avoid the naive bloat.


    --
    Eric Sosman
    d
     
    Eric Sosman, May 24, 2012
    #10
  11. Dirk Zabel

    James Kuyper Guest

    On 05/24/2012 06:05 PM, Robert Wessel wrote:
    > On Thu, 24 May 2012 13:05:52 -0400, James Kuyper
    > <> wrote:
    >
    >> On 05/24/2012 12:31 PM, Robert Wessel wrote:
    >>> On Thu, 24 May 2012 12:15:16 +0200, Dirk Zabel <>
    >>> wrote:

    ....
    >>>> What I really like is:
    >>>>
    >>>> #define CNEW(type) (type *)malloc(sizeof(type))
    >>>>
    >>>> so i get a compiler message if i try
    >>>>
    >>>> pear * pp = CNEW(apple);
    >>>>
    >>>> while
    >>>> pear * pp = malloc(sizeof(apple));
    >>>>
    >>>> will go undetected.
    >>>
    >>>
    >>> I've actually made that suggestion in c.l.c.:
    >>>
    >>> http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-11/msg02546.html
    >>>
    >>> Got the usual knee-jerk (and completely missing the point) "don't cast
    >>> malloc!" reply...

    >>
    >> It's not missing the point, it's seeing the point quite clearly, and
    >> rejecting it. There are only two possible errors when making a call to
    >> malloc(). The first is that the size is wrong; the type is irrelevant.
    >> The second is failing to check for the possibility of a null return
    >> value. The clc idiom
    >>
    >> pp = malloc(count * sizeof *pp);
    >>
    >> (or malloc(sizeof *pp) if count is guaranteed to be 1) is sufficient in
    >> itself to avoid the size error. Your alternative
    >>
    >> pp = CNEW(pear);
    >>
    >> has no advantage over that idiom. If pp is indeed a pear*, it will do
    >> the same thing as the clc idiom. If pp is not a pear*, the clc idiom
    >> will still produce the correct size, without requiring any changes,
    >> while CNEW(pear) will have to be changed.
    >>
    >> Can you identify a context in which the clc idiom will tolerate an
    >> incorrect use of malloc(), while CNEW() will not?

    >
    >
    > Trivially:
    >
    > pp = malloc(count * sizeof *qq); /* allocates kumquats */


    It's equally trivial to notice that pp and qq don't match; they're both
    part of the same assignment expression. When used in the initialization
    of a pointer, CNEW() is equally easy to check. However, in the common
    case where a pointer is being set somewhere other than during
    initialization, if an error message is triggered, figuring out the right
    type requires searching elsewhere:

    pp = CNEW(pear); // Is pp a pear*, or a kumquat*?

    When the object whose value is being set is foo->bar->baz, finding out
    the type pointed at could require searching through a string of header
    files; with the clc idiom, getting it right can be done just by looking
    at a single assignment expression.

    > The standard idiom also does not help usages which do not involve
    > immediate assignments to a pointer. Consider:
    >
    > int f(pear *pp);
    > ...
    > f(CNEW(pear)); /* safe */
    > f(malloc(...)); /* not so much */


    The clc idiom is inapplicable to that case - it requires a pointer
    expression as an argument of sizeof. CNEW() is not particularly
    objectionable in that case, but I would consider it a rare one. I
    generally want to do something more like the following:

    pp = malloc(sizeof *pp);
    if(pp)
    f(pp);
    else
    {
    // Error handling
    }

    Actually, normally, what would be the if-branch in the above code
    usually contains a lot more than single function call. I prefer to keep
    error handling close to the function call whose failure triggered the
    handling, so I would usually reverse the test:

    pp = malloc(sizeof *pp);
    if(pp == NULL)
    {
    // Error handling
    }
    else
    {
    if(f(pp)!=SUCCESS)
    {
    // Error handling
    }
    else
    {
    // Other stuff
    }
    }


    > IMO, as a practice, attaching a type to an object as soon as it's
    > created is just good form,


    I agree. With either the clc idiom, or CNEW(), the compiler must
    generate code implementing the following sequence of events:

    1. call malloc()
    2. convert the result of that call to pear*
    3. store the result of that conversion in pp

    There's not a single event separating step 1 from step 2, whether you
    use the clc idiom or CNEW(). That's ASAP as far as I'm concerned.

    > ... and requiring something to be duplicated
    > (correctly) is just asking for trouble, as sooner or later someone is
    > going to duplicate and modify a chunk of code and miss one of the
    > references.


    That's an argument for

    #define NEW(pp) (pp) = malloc(sizeof *(pp))

    which has already been discussed. The argument of NEW() only needs to be
    changed if the variable name changes. The argument of CNEW() needs to be
    changed any time the type changes. I might consider using something like
    NEW(), but only if I needed to use it very frequently. However, I'd
    probably give it a different name, since it's not as closely analogous
    to the 'new' operator in C++ as CNEW() is.
     
    James Kuyper, May 24, 2012
    #11
  12. Dirk Zabel

    Martin Shobe Guest

    Eric Sosman wrote:

    > On 5/24/2012 5:42 PM, Martin Shobe wrote:
    >> Eric Sosman wrote:
    >>
    >>> On 5/24/2012 4:46 PM, August Karlstrom wrote:
    >>>> On 2012-05-24 22:27, Ben Pfaff wrote:
    >>>>> Willem<> writes:
    >>>>>> #define NEW(pp) (pp)=malloc(sizeof *(pp))
    >>>>>>
    >>>>>> pear *pp;
    >>>>>> NEW(pp);
    >>>>>
    >>>>> If the goal is "write a macro to allocate memory and assign it to
    >>>>> a variable", you have accomplished it. But I think that this
    >>>>> makes code harder to reduce, without a clear benefit.
    >>>>
    >>>> The clear benefit is that it makes memory allocation simpler and less
    >>>> error prone. Compare these two statements for instance:
    >>>>
    >>>> NEW(foo->bar->baz);
    >>>>
    >>>> foo->bar->baz = malloc(sizeof *(foo->bar->boz));
    >>>
    >>> Aren't you overlooking what comes next?
    >>>
    >>> if (foo->bar->bas == NULL) ...
    >>> foo->bor->baz->answer = 42;
    >>> strcpy(fou->bar->baz->question,
    >>> "Life, the Universe -- everything");
    >>>
    >>> In other words, the code will refer to the new pointer at least
    >>> three times, usually more. That is, the suggested macro reduces
    >>> the number of uses of the pointer from N to N-1, but N>= 3
    >>> always, N>= 4 nearly always.
    >>>
    >>> If the pointer is cumbersome to spell, the programmer is very
    >>> likely to introduce a new variable anyhow:
    >>>
    >>> struct baz_s *newbaz = foo->bar->baz = malloc(sizeof *newbaz);
    >>> if (newbaz == NULL) ...
    >>> newbaz->answer = 42;
    >>> strcpy(newbaz->question, "Life, the Universe -- everything");
    >>>
    >>> In this pattern, the naive use of the macro would actually *increase*
    >>> the number of times the pointer is spelled out:
    >>>
    >>> NEW(foo->bar->baz);
    >>> struct baz_s *newbaz = foo->bar->baz;
    >>> ...

    >>
    >> Really? Why wouldn't the programmer use
    >>
    >> struct baz_s * newbaz = NEW(foo->bar->baz);
    >>
    >> (Not that I approve of the macro, but lets at least keep the criticisms
    >> legitimate.)

    >
    > Kind of rends the veil of opacity, don't you think? The
    > programmer now needs to know that NEW expands not just to any
    > old code sequence that allocates memory and assigns to its
    > argument, but specifically to an expression -- in particular,
    > to an expression that yields a pointer to the new memory.


    No, I don't think that. I see absolutely no problem specifying that NEW
    return the pointer value assigned to its argument.

    >
    > Besides, in what you snipped I described how the programmer
    > would (IMHO) quite likely avoid the naive bloat.


    Would that still hold for those programmers who are using that macro
    regularly?

    Martin Shobe
     
    Martin Shobe, May 25, 2012
    #12
  13. Dirk Zabel

    James Kuyper Guest

    I'm sending a second reply, largely redundant with my first, just
    because I've noticed a better way to tie it in with your own remarks.

    On 05/24/2012 06:05 PM, Robert Wessel wrote:
    ....
    > ... requiring something to be duplicated
    > (correctly) is just asking for trouble, as sooner or later someone is
    > going to duplicate and modify a chunk of code and miss one of the
    > references.


    Yes, and CNEW() requires that you duplicate a type specified elsewhere,
    possibly very far from the point where CNEW() is used. The clc idiom
    requires duplication of a pointer expression that is right nearby, and
    that duplication can easily be automated, as has already been discussed
    with the NEW() macro.

    --
    James Kuyper
     
    James Kuyper, May 25, 2012
    #13
  14. Dirk Zabel

    James Kuyper Guest

    On 05/25/2012 10:50 AM, Robert Wessel wrote:
    > On Fri, 25 May 2012 06:47:47 -0400, James Kuyper
    > <> wrote:
    >
    >> I'm sending a second reply, largely redundant with my first, just
    >> because I've noticed a better way to tie it in with your own remarks.
    >>
    >> On 05/24/2012 06:05 PM, Robert Wessel wrote:
    >> ...
    >>> ... requiring something to be duplicated
    >>> (correctly) is just asking for trouble, as sooner or later someone is
    >>> going to duplicate and modify a chunk of code and miss one of the
    >>> references.

    >>
    >> Yes, and CNEW() requires that you duplicate a type specified elsewhere,
    >> possibly very far from the point where CNEW() is used. The clc idiom
    >> requires duplication of a pointer expression that is right nearby, and
    >> that duplication can easily be automated, as has already been discussed
    >> with the NEW() macro.

    >
    >
    > Basically true, *but* CNEW() checks that you did it correctly. The
    > common idiom does *not*.


    There's only one rather unlikely kind of mistake that you can make with
    the clc idiom that can be caught by the CNEW() macro. If you're really
    worried about making that kind of error, the NEW() macro removes the
    possibility of making it, and therefore the need to check for it.
     
    James Kuyper, May 25, 2012
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben Pfaff

    Re: possible useful macros...

    Ben Pfaff, May 23, 2012, in forum: C Programming
    Replies:
    0
    Views:
    305
    Ben Pfaff
    May 23, 2012
  2. John Gordon

    Re: possible useful macros...

    John Gordon, May 23, 2012, in forum: C Programming
    Replies:
    3
    Views:
    302
    Phil Carmody
    Jun 11, 2012
  3. Kaz Kylheku

    Re: possible useful macros...

    Kaz Kylheku, May 23, 2012, in forum: C Programming
    Replies:
    0
    Views:
    242
    Kaz Kylheku
    May 23, 2012
  4. Lanarcam

    Re: possible useful macros...

    Lanarcam, May 23, 2012, in forum: C Programming
    Replies:
    1
    Views:
    232
    Bartc
    May 23, 2012
  5. Ike Naar

    Re: possible useful macros...

    Ike Naar, May 23, 2012, in forum: C Programming
    Replies:
    0
    Views:
    227
    Ike Naar
    May 23, 2012
Loading...

Share This Page