debugcMinor
Error-checking macro to jump to specified label given a failing statement
Viewed 0 times
errorjumpfailingstatementcheckingmacrolabelgivenspecified
Problem
I'm starting a new C projet and I want to have the cleanest and shortest error checking code possible.
To that aim, I often use two things :
So I thought about using a macro to simplify the code even more. If I choose to use it, this pattern will appear in almost every function so I expect that people reading the code will become accustomed to it very quickly.
So, is it a viable solution ? Here is an example snippet :
EDIT: Here is an alternative without the
To that aim, I often use two things :
- functions return
0,falseorNULLon error
- I try to use small functions with one cleanup label "fail" or "error"
So I thought about using a macro to simplify the code even more. If I choose to use it, this pattern will appear in almost every function so I expect that people reading the code will become accustomed to it very quickly.
So, is it a viable solution ? Here is an example snippet :
#define try( stmt , error_label ) if (!(stmt)) { goto error_label; }
// this function creates a new image database
ImageDatabase*
db_create(uint32_t max_images,
struct resolution resolutions[NB_RESOLUTIONS])
{
ImageDatabase* db;
try( db = allocate_empty_db(), error);
memcpy(db->resolutions, resolutions, NB_RESOLUTIONS * sizeof(struct resolution));
db->max_nb_images = max_images;
try( db->metadatas = calloc(max_images, sizeof(struct metadata)) , error);
try( finalize_creation(db) , error);
return db;
error:
free(db->metadatas);
free(db);
return NULL;
}EDIT: Here is an alternative without the
try macro... but it's not as pretty. Also note that in this example the code is simple enough to avoid a goto, but it's not always the case.ImageDatabase*
db_create(uint32_t max_images,
struct resolution resolutions[NB_RESOLUTIONS])
{
ImageDatabase* db = allocate_empty_db();
if (db) {
memcpy(db->resolutions, resolutions, NB_RESOLUTIONS * sizeof(struct resolution));
db->max_nb_images = max_images;
db->metadatas = calloc(max_images, sizeof(struct metadata)) , error);
if (db->metadata == NULL
|| !finalize_creation(db))
{
free(db->metadatas);
free(db);
db = NULL;
}
}
return db;
}Solution
NULL dereference
If
The macro
I'm not exactly a fan of the macro. First, calling it
Also, the macro should be a
Here, your macro will cause the above to expand to:
Which is actually a syntax error because of the extra semicolon but is also close to making the
This
If
allocate_empty_db() returns NULL, it will jump to the error handling code, which will cause a NULL dereference when it tries to free(db->metadatas). You will need to guard that line like this:if (db)
free(db->metadatas);The macro
I'm not exactly a fan of the macro. First, calling it
try makes me think your code is java, and then when I realize it isn't, I'm more confused. Second, if your label is always going to be the same in every function, then you can omit the second argument of the macro and just hardcode it to goto error. That way the macro can look smaller and the reader doesn't have to parse the extra comma and argument (which look like part of the body you are try-ing).Also, the macro should be a
do while instead of an if. You could get in trouble with code like this:if (condition)
try(foo(), error);
else
bar();Here, your macro will cause the above to expand to:
if (condition)
if (!foo()) { goto error; };
else
bar();Which is actually a syntax error because of the extra semicolon but is also close to making the
else pair with the wrong if. The macro should look like this instead:#define try( stmt , error_label ) \
do { \
if (!(stmt)) { goto error_label; } \
} while(0)This
do while(0) construction is a common trick when writing C macros. It allows a multi-statement macro to be used in an if statement like the one in my example. See this Stackoverflow question for more info.Code Snippets
if (db)
free(db->metadatas);if (condition)
try(foo(), error);
else
bar();if (condition)
if (!foo()) { goto error; };
else
bar();#define try( stmt , error_label ) \
do { \
if (!(stmt)) { goto error_label; } \
} while(0)Context
StackExchange Code Review Q#133505, answer score: 2
Revisions (0)
No revisions yet.