P
Petr Pavlu
Hello,
I have two questions how the functions should be written. I read the FAQ
but didn't find any answer. If there is any please point me out.
I. Cleanup code
Consider I have to open file1, then make some malloc and then open file2.
What is the best solution how to write the cleanup code? See my pseudo-
code ideas.
1. Write appropriate calls before every return
my_function()
{
if (fopen1 failed)
return 1
if (malloc failed) {
fclose1
return 2
}
if (fopen2 failed) {
free
fclose1
return 3
}
...
if (something failed) {
fclose2
free
fclose1
return 4
}
/* everything ok */
fclose2
free
fclose1
return 0
}
2. Use goto statement and an extra variable
my_function()
{
int rv = 0;
if (fopen1 failed) {
rv = 1
goto end
}
if (malloc failed) {
rv = 2
goto end
}
if (fopen2 failed) {
rv = 3
goto end
}
...
if (something failed) {
rv = 4
goto end
}
end:
if (filepointer2 != NULL)
fclose2
if (memorypointer != NULL)
free
if (filepointer1 != NULL)
fclose1
return rv
}
3. Use a similar mechanism as atexit()
my_function()
{
if (fopen1 failed) {
execute_all(stack)
return 1
}
push(stack, fclose, filepointer1)
if (malloc failed) {
execute_all(stack)
return 2
}
push(stack, free, memorypointer)
if (fopen2 failed) {
execute_all(stack)
return 3
}
push(stack, fclose, filepointer2)
...
if (something failed) {
execute_all(stack)
return 4
}
execute_all(stack)
return 0
}
void push(STACK *stack, void (*func)(void *), void *data) ?
Pushing on the stack should be always successful, the stack should have a
static array for functions, there would be assert() to disallow saving
more than an allowed amount of functions...
execute_all(stack) and return X could be defined as
#define RETURN(stack, rv) \
execute_all(stack) \
return rv
4. Define a cleanup macro for every function
my_function()
{
#define RETURN(rv) \
if (filepointer2 != NULL) \
fclose2 \
if (memorypoiter != NULL) \
free \
if (filepointer1 != NULL) \
fclose1 \
return rv
if (fopen1 failed)
RETURN(1)
if (malloc failed)
RETURN(2)
if (fopen2 failed)
RETURN(3)
...
if (something failed)
RETURN(4)
RETURN(0)
#undef RETURN
}
I think the final output will be very large in this case.
5. Another solution
Do you know about some different elegant solution? Smart pointers in C?
Or am I doing everything wrongly if my function needs three or more
cleanup functions?
II. My malloc with exit()
Is it good idea using my malloc wrapper like this one?
void *xmalloc(size_t size)
{
void *p;
if ((p = malloc(size)) == NULL) {
fprintf(stderr, "Malloc error.\n");
exit(1);
}
return p;
}
Is it ok to call exit() inside it? Some people says, you should have
free() to every malloc() call (other pair is fopen() and fclose() etc.).
However if a program (and probably also an operation system) doesn't have
enough memory should program give up as soon as possible?
Thanks in advance
I have two questions how the functions should be written. I read the FAQ
but didn't find any answer. If there is any please point me out.
I. Cleanup code
Consider I have to open file1, then make some malloc and then open file2.
What is the best solution how to write the cleanup code? See my pseudo-
code ideas.
1. Write appropriate calls before every return
my_function()
{
if (fopen1 failed)
return 1
if (malloc failed) {
fclose1
return 2
}
if (fopen2 failed) {
free
fclose1
return 3
}
...
if (something failed) {
fclose2
free
fclose1
return 4
}
/* everything ok */
fclose2
free
fclose1
return 0
}
2. Use goto statement and an extra variable
my_function()
{
int rv = 0;
if (fopen1 failed) {
rv = 1
goto end
}
if (malloc failed) {
rv = 2
goto end
}
if (fopen2 failed) {
rv = 3
goto end
}
...
if (something failed) {
rv = 4
goto end
}
end:
if (filepointer2 != NULL)
fclose2
if (memorypointer != NULL)
free
if (filepointer1 != NULL)
fclose1
return rv
}
3. Use a similar mechanism as atexit()
my_function()
{
if (fopen1 failed) {
execute_all(stack)
return 1
}
push(stack, fclose, filepointer1)
if (malloc failed) {
execute_all(stack)
return 2
}
push(stack, free, memorypointer)
if (fopen2 failed) {
execute_all(stack)
return 3
}
push(stack, fclose, filepointer2)
...
if (something failed) {
execute_all(stack)
return 4
}
execute_all(stack)
return 0
}
void push(STACK *stack, void (*func)(void *), void *data) ?
Pushing on the stack should be always successful, the stack should have a
static array for functions, there would be assert() to disallow saving
more than an allowed amount of functions...
execute_all(stack) and return X could be defined as
#define RETURN(stack, rv) \
execute_all(stack) \
return rv
4. Define a cleanup macro for every function
my_function()
{
#define RETURN(rv) \
if (filepointer2 != NULL) \
fclose2 \
if (memorypoiter != NULL) \
free \
if (filepointer1 != NULL) \
fclose1 \
return rv
if (fopen1 failed)
RETURN(1)
if (malloc failed)
RETURN(2)
if (fopen2 failed)
RETURN(3)
...
if (something failed)
RETURN(4)
RETURN(0)
#undef RETURN
}
I think the final output will be very large in this case.
5. Another solution
Do you know about some different elegant solution? Smart pointers in C?
Or am I doing everything wrongly if my function needs three or more
cleanup functions?
II. My malloc with exit()
Is it good idea using my malloc wrapper like this one?
void *xmalloc(size_t size)
{
void *p;
if ((p = malloc(size)) == NULL) {
fprintf(stderr, "Malloc error.\n");
exit(1);
}
return p;
}
Is it ok to call exit() inside it? Some people says, you should have
free() to every malloc() call (other pair is fopen() and fclose() etc.).
However if a program (and probably also an operation system) doesn't have
enough memory should program give up as soon as possible?
Thanks in advance