Skip to content

Instantly share code, notes, and snippets.

@wowczarek
Last active February 24, 2016 21:52
Show Gist options
  • Save wowczarek/7c8121696f7373e126d0 to your computer and use it in GitHub Desktop.
Save wowczarek/7c8121696f7373e126d0 to your computer and use it in GitHub Desktop.
Simple C (C99) re-entrant "foreach" loop macro to split a string into tokens separated by delimiter and loop over them
#include <string.h>
/* (c) 2015 Wojciech Owczarek - BSD Clause 2 licence - I'm too lazy to paste it */
#ifndef __FOREACH_TOKEN_H
#define __FOREACH_TOKEN_H
/* handy default delimiter - space, comma, semicolon or tab */
#define DEFAULT_TOKEN_DELIM ", ;\t"
/*
* foreach loop across substrings from @var, delimited by @delim, placing
* each token in @targetvar on iteration, using @id variable name prefix
* to allow nesting (each loop uses an individual set of variables).
* @id is only a string identifier, so the end macro can identify which
* helper pointer to free. We need to duplicate the string, because strtok
* functions alter it. We also need to free the duplicate. A counter variable
* (counter_@id) is defined and incremented from zero, so we know which token we
* are processing. The counter is incremented once again when ending the loop,
* so in the end contains the total number of tokens found.
*
* Note: because this is a macro, if you forget the end call, you're missing
* a closing bracket and compiler errors will not clearly show what happened.
*/
#define foreach_token_begin(id, var, targetvar, delim) \
int counter_##id = -1; /* iteration counter - start from -1 because we increment at the top of the loop */ \
char* stash_##id = NULL; /* temporary pointer for strtok_r() - in strtok() this is static */ \
char* text_##id; /* copy of our string */ \
char* text__##id; /* pointer to our copy, which has to be used once only */ \
char* targetvar; /* our target variable for the token */ \
text_##id=strdup(var); \
for(text__##id = text_##id;;text__##id = NULL) { /* why nullify? because strtoks require this */ \
targetvar = strtok_r(text__##id, delim, &stash_##id); \
if(targetvar == NULL) break; /* no (more) tokens */ \
counter_##id++;
#define foreach_token_end(id) } \
if(text_##id != NULL) { \
free(text_##id); /* thou shall free what thou strdupeth */ \
} \
counter_##id++; /* because the for loop does no incrementing here */
/* example usage (semicolons are not necessary when invoking begin_ and end_) */
/*
foreach_token_begin(names, "Bob, Jake Steve; Norman Lucy", name, DEFAULT_TOKEN_DELIM);
foreach_token_begin(animals, "frog:cougar:fruit bat:tarsier", animal, ":");
printf("%s\'s animal number %d is a %s.\n", name, counter_animals + 1, animal);
foreach_token_end(animals);
printf("this was a list of %s\'s %d animals.\n", name, counter_animals);
foreach_token_end(names);
printf("a total of %d people have animals\n.", counter_names);
*/
#endif /* __FOREACH_TOKEN_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment