What is considered macro abuse?

K

Khookie

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);
}
 
D

Duncan Muirhead

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.
 
E

Eric Sosman

Khookie said:
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"
 
M

Mark Bluemel

Khookie said:
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.
 
P

pete

Khookie said:
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);
}
 
R

Richard Bos

Marc Boyer said:
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
 
M

Marc Boyer

Le 12-12-2007 said:
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
 
M

Marc Boyer

Le 12-12-2007 said:
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.
 
D

dj3vande

Khookie said:
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
 
K

Khookie

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top