What is considered macro abuse?

Discussion in 'C Programming' started by Khookie, Dec 12, 2007.

  1. Khookie

    Khookie Guest

    Hi all,

    I was wondering what was considered macro abuse. Specifically I
    implemented a key-value array (or dictionary), as per below. Notice
    the use of macros - would that be considered alright?

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

    #define START_ITERATE_KVA(kva) \
    string_kva_node *node = kva->bol; \
    while (node != kva->eol) {

    #define END_ITERATE_KVA() \
    node = node->next; \
    }

    typedef struct string_kva_node_struct {
    char* key;
    char* value;
    struct string_kva_node_struct *next;
    } string_kva_node;

    typedef struct string_kva {
    string_kva_node* bol;
    string_kva_node* eol;
    } string_kva;

    string_kva* string_kva_init() {
    string_kva *kva = (string_kva*)malloc(sizeof(string_kva));
    kva->bol = (string_kva_node*)malloc(sizeof(string_kva_node));
    kva->eol = kva->bol;
    return kva;
    }

    void string_kva_add(string_kva *kva, char *key, char* value) {
    // It just assigns the key & value reference, so don't go changing it
    // afterwards - it's designed for speed here.
    // And it will also allow you to add duplicate keys so be careful.

    // If you need to set a key value that may or may not exist, use
    // string_kva_set. This will guarantee uniqueness of keys.

    kva->eol->key = key;
    kva->eol->value = value;

    kva->eol->next = (string_kva_node*)malloc(sizeof(string_kva_node));
    kva->eol = kva->eol->next;
    }

    void string_kva_set(string_kva *kva, char *key, char* value) {
    // find key and replace it
    START_ITERATE_KVA(kva)
    if (strcmp(node->key, key) == 0) {
    node->value = value;
    return;
    }
    END_ITERATE_KVA()

    // if cannot find, then add as per usual
    string_kva_add(kva, key, value);
    }

    char* string_kva_get(string_kva *kva, char* key) {
    START_ITERATE_KVA(kva)
    if (strcmp(node->key, key) == 0) {
    return node->value;
    }
    END_ITERATE_KVA()
    return NULL;
    }

    int string_kva_length(string_kva *kva) {
    int length = 0;
    START_ITERATE_KVA(kva)
    length++;
    END_ITERATE_KVA()
    return length;
    }

    void string_kva_free(string_kva *kva) {
    string_kva_node *node = kva->bol;
    while (node != kva->eol) {
    string_kva_node *next_node = node->next;
    free(node);
    node = next_node;
    }
    free(kva);
    }

    int main() {
    string_kva* kva = string_kva_init();
    printf("* Test first element\n");
    string_kva_add(kva, "Ross", "Stevenson");
    string_kva_add(kva, "Matthew", "Carrington");
    string_kva_add(kva, "Lisa", "McQueen");
    string_kva_add(kva, "Jerry", "Seinhauser");
    string_kva_add(kva, "Wong", "Fei Hong");

    printf("* Test first element\n");
    assert(string_kva_get(kva, "Ross") == "Stevenson");

    printf("* Test middle element\n");
    assert(string_kva_get(kva, "Lisa") == "McQueen");

    printf("* Test last element\n");
    assert(string_kva_get(kva, "Wong") == "Fei Hong");

    printf("* Test non-existent element\n");
    assert(string_kva_get(kva, "Tara") == NULL);

    printf("* Set duplicate key and test equality\n");
    string_kva_set(kva, "Jerry", "Lee");
    assert(string_kva_get(kva, "Jerry") == "Lee");

    printf("* Test length\n");
    assert(string_kva_length(kva) == 5);

    printf("* Add another element after all that\n");
    string_kva_add(kva, "Mini", "Me");

    printf("* Test length again\n");
    assert(string_kva_length(kva) == 6);

    string_kva_free(kva);
    }
    Khookie, Dec 12, 2007
    #1
    1. Advertising

  2. On Wed, 12 Dec 2007 02:57:12 -0800, Khookie wrote:

    > Hi all,
    >
    > I was wondering what was considered macro abuse. Specifically I
    > implemented a key-value array (or dictionary), as per below. Notice
    > the use of macros - would that be considered alright?
    >
    > #include <assert.h>
    > #include <stdio.h>
    > #include <stdlib.h>
    > #include <string.h>
    >
    > #define START_ITERATE_KVA(kva) \
    > string_kva_node *node = kva->bol; \
    > while (node != kva->eol) {
    >
    > #define END_ITERATE_KVA() \
    > node = node->next; \
    > }
    >

    <snip>
    Can't say I like it.
    In C90 at least, the caller would need to enclose START_ITERATE_KVA(),
    END_ITERATE_KVA() pairs in {} or they are going to have syntax errors
    (mixing declarations with code).
    With nested loops you are going to have the inner node variable shadowing
    the outer one and so there's no way to refer to the outer node in the
    inner loop.
    START_ITERATE_KVA(kva)
    if ( !cond(kva, node))
    { continue;
    }
    /* code */
    END_ITERATE_KVA()
    would take a long time if cond were ever false.
    These would be fixed by
    #define START_ITERATE_KVA(kva,node) \
    for((node)=(kva)->bol; (node)!=(kva)->eol; (node)=(node)->next)
    and not bothering with END_ITERATE_KVA
    but personally I'd prefer just to type the for.
    Duncan Muirhead, Dec 12, 2007
    #2
    1. Advertising

  3. Khookie

    Eric Sosman Guest

    Khookie wrote:
    > Hi all,
    >
    > I was wondering what was considered macro abuse. [...]


    The most abusive example I ever saw was an IOCCC entry
    from quite a few years ago. The program had a few minor
    portability issues, but was astonishingly flexible and
    powerful, especially considering its brevity:

    #include "/dev/tty"

    --
    Eric Sosman
    lid
    Eric Sosman, Dec 12, 2007
    #3
  4. Khookie

    Mark Bluemel Guest

    Khookie wrote:
    > Hi all,
    >
    > I was wondering what was considered macro abuse. Specifically I
    > implemented a key-value array (or dictionary), as per below. Notice
    > the use of macros - would that be considered alright?
    >
    > #include <assert.h>
    > #include <stdio.h>
    > #include <stdlib.h>
    > #include <string.h>
    >
    > #define START_ITERATE_KVA(kva) \
    > string_kva_node *node = kva->bol; \
    > while (node != kva->eol) {
    >
    > #define END_ITERATE_KVA() \
    > node = node->next; \
    > }


    I've worked on codebases with this sort of thing, though I remember it
    being more like this :-

    #define START_ITERATE_KVA(kva) \
    string_kva_node *node; \
    for (node = kva->bol; node != kva->eol; node = node->next;) {

    #define END_ITERATE_KVA() \
    }

    Of course for C90 compatibility, you'd go for :-

    #define START_ITERATE_KVA(kva) \
    { \
    string_kva_node *node; \
    for (node = kva->bol; node != kva->eol; node = node->next;) {

    #define END_ITERATE_KVA() \
    }\
    }

    In some cases we had macros which depended on being nested within this
    sort of structure, in much the same way as your string_kva_set function
    has code which refers to variables it didn't appear to define.

    I don't like it, as I feel it makes code less readable and less
    maintainable.

    I would be inclined to look for less ugly solutions, perhaps using a
    call-back approach.
    Mark Bluemel, Dec 12, 2007
    #4
  5. Khookie

    pete Guest

    Khookie wrote:
    >
    > Hi all,
    >
    > I was wondering what was considered macro abuse.


    http://groups.google.com/group/comp.lang.c/msg/3037543775aa8567

    I think there are 66 different functions defined here.
    This really compiles!

    /* pete-4.c */

    #define F(Q,R,P) Q(int x){int i=x;while(i--)x=R(x,x);return x;}\
    P(int L,int x){int i=x;if(L--)while(i--)x=P(L,x);return Q(x);}

    #define Y(A,z,B,C,D,E,G,H,I,J,K,M,N,O,S,T,U,V,W)\
    F(A,z,B)F(C,B,D)F(E,D,G)F(H,G,I)F(J,I,K)F(M,K,N)F(O,N,S)F(T,S,U)F(V,U,W)

    Z(int L,int x)
    {
    int i = x;

    if(L--)
    while(i--)
    x = Z(L,x);
    return x << x;
    }

    Y(a,Z,b,c,d,e,g,h,X,j,k,m,n,o,s,t,u,v,w)
    Y(Aa,w,Ba,Ca,Da,Ea,Ga,Ha,Ia,Ja,Ka,Ma,Na,Oa,Sa,Ta,Ua,Va,Wa)
    Y(Ab,Wa,Bb,Cb,Db,Eb,Gb,Hb,Ib,Jb,Kb,V,U,W,T,S,O,N,M)
    F(A,M,B)
    F(C,B,D)
    F(E,D,G)
    F(H,G,I)
    F(J,I,K)

    int main()
    {
    return K(99999,9);
    }


    --
    pete
    pete, Dec 12, 2007
    #5
  6. Khookie

    Richard Bos Guest

    Marc Boyer <> wrote:

    > Le 12-12-2007, Khookie <> a écrit :
    > > Hi all,
    > >
    > > I was wondering what was considered macro abuse. Specifically I
    > > implemented a key-value array (or dictionary), as per below. Notice
    > > the use of macros - would that be considered alright?

    >
    > Yes it is to me.
    > But, why don't you use the well-known iterator paradim ?
    >
    > kva_it it= kva_first( ... );
    > while( has_next(kva_it) ){
    > value= get_next(kva_it);
    > do_thing_on(value);
    > }


    Because
    - it's not grammatical - you're using kva_it as both type and object
    - the first kva doesn't get things done on it, which may make it jealous
    - the following is more idiomatic, and IMO clearer:

    for (kva_it kva=kva_first(); kva; kva=kva_next(kva) {
    do_thing_on(kva_value(kva));
    }

    Richard
    Richard Bos, Dec 12, 2007
    #6
  7. Khookie

    Marc Boyer Guest

    Le 12-12-2007, Khookie <> a écrit :
    > Hi all,
    >
    > I was wondering what was considered macro abuse. Specifically I
    > implemented a key-value array (or dictionary), as per below. Notice
    > the use of macros - would that be considered alright?


    Yes it is to me.
    But, why don't you use the well-known iterator paradim ?

    kva_it it= kva_first( ... );
    while( has_next(kva_it) ){
    value= get_next(kva_it);
    do_thing_on(value);
    }

    Marc Boyer
    Marc Boyer, Dec 12, 2007
    #7
  8. Khookie

    Marc Boyer Guest

    Le 12-12-2007, Richard Bos <> a écrit :
    > Marc Boyer <> wrote:
    >
    >> Le 12-12-2007, Khookie <> a écrit :
    >> > Hi all,
    >> >
    >> > I was wondering what was considered macro abuse. Specifically I
    >> > implemented a key-value array (or dictionary), as per below. Notice
    >> > the use of macros - would that be considered alright?

    >>
    >> Yes it is to me.
    >> But, why don't you use the well-known iterator paradim ?
    >>
    >> kva_it it= kva_first( ... );
    >> while( has_next(kva_it) ){
    >> value= get_next(kva_it);
    >> do_thing_on(value);
    >> }

    >
    > Because
    > - it's not grammatical - you're using kva_it as both type and object


    Yes:
    2,3s/kva_it/it/

    > - the first kva doesn't get things done on it, which may make it jealous


    It depend the semantics of 'first'. It can be 'one-before-start', or
    the real first element.

    > - the following is more idiomatic, and IMO clearer:
    >
    > for (kva_it kva=kva_first(); kva; kva=kva_next(kva) {
    > do_thing_on(kva_value(kva));
    > }


    Yes, this is another variation. I was using a Java-like concept,
    but I wont defend mine more than your.
    The only point I want to says is that the iterator concept
    is better than the OP stange macros.
    Marc Boyer, Dec 12, 2007
    #8
  9. Khookie

    Guest

    In article <>,
    Eric Sosman <> wrote:
    >Khookie wrote:
    >> Hi all,
    >>
    >> I was wondering what was considered macro abuse. [...]

    >
    > The most abusive example I ever saw was an IOCCC entry
    >from quite a few years ago. The program had a few minor
    >portability issues, but was astonishingly flexible and
    >powerful, especially considering its brevity:
    >
    > #include "/dev/tty"


    Is that macro abuse, or more generic preprocessor abuse?


    dave
    , Dec 12, 2007
    #9
  10. Khookie

    Khookie Guest

    On Dec 13, 1:57 am, (Richard Bos) wrote:
    > Marc Boyer <> wrote:
    > > Le 12-12-2007, Khookie <> a écrit :
    > > > Hi all,

    >
    > > > I was wondering what was considered macro abuse. Specifically I
    > > > implemented a key-value array (or dictionary), as per below. Notice
    > > > the use of macros - would that be considered alright?

    >
    > > Yes it is to me.
    > > But, why don't you use the well-known iterator paradim ?

    >
    > > kva_it it= kva_first( ... );
    > > while( has_next(kva_it) ){
    > > value= get_next(kva_it);
    > > do_thing_on(value);
    > > }

    >
    > Because
    > - it's not grammatical - you're using kva_it as both type and object
    > - the first kva doesn't get things done on it, which may make it jealous
    > - the following is more idiomatic, and IMO clearer:
    >
    > for (kva_it kva=kva_first(); kva; kva=kva_next(kva) {
    > do_thing_on(kva_value(kva));
    > }
    >
    > Richard


    Hi Richard

    Yeah I like that - thanks man.

    Chris
    Khookie, Dec 13, 2007
    #10
    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. rossz
    Replies:
    31
    Views:
    1,009
    Rincewind
    Sep 25, 2005
  2. Graeme

    STL abuse

    Graeme, Jan 7, 2004, in forum: C++
    Replies:
    5
    Views:
    539
    tom_usenet
    Jan 7, 2004
  3. Richard

    Complaints to

    Richard, Jul 22, 2004, in forum: C++
    Replies:
    0
    Views:
    1,758
    Richard
    Jul 22, 2004
  4. Kal Iyer
    Replies:
    10
    Views:
    644
    Victor Bazarov
    Dec 9, 2004
  5. Paul
    Replies:
    1
    Views:
    338
    bruce barker \(sqlwork.com\)
    Jun 29, 2006
Loading...

Share This Page