S
Shao Miller
Where one uses macros instead of jump statements (though the actual
statements _could_ be overridden, theoretically) and where one uses the
'HAS_CLEANUP' macro within whatever scopes require resource cleanup.
Also available (with nice colours) at:
http://codepad.org/YLV13z1K
Can be compiled all as one file via copy and paste, or split into
component files.
Feedback welcome and appreciated, as always.
-----
/*
* To split into separate files, remove all
* lines containing 'ALL_IN_ONE'
*/
#define ALL_IN_ONE 1
/****
* cleanup.h
* 2011, Shao Miller
*/
#ifndef CLEANUP_H_
#include <stddef.h>
/*** Macros */
#define CLEANUP_H_
/**
* Block cleanup details
*/
#define CLEANUP_BLOCK_ cleanup_do_(cleanup_);
/**
* Function cleanup details
*/
#define CLEANUP_FUNC_ cleanup_chain_(cleanup_);
/**
* Perform block cleanup and jump to a label.
* Only use for jumping from an inner to an
* outer scope.
* Note the trailing space after 'goto'.
*/
#define c_goto \
switch (1) \
while (1) \
if (0) { \
default: { \
CLEANUP_BLOCK_; \
} \
} else \
goto
/**
* Perform block cleanup and continue a loop
*/
#define c_continue \
if (1) { \
CLEANUP_BLOCK_; \
continue; \
} else do ; while (0)
/**
* Perform block cleanup and break out of a
* a loop or 'switch' statement
*/
#define c_break \
if (1) { \
CLEANUP_BLOCK_; \
break; \
} else do ; while (0)
/**
* Perform function cleanup and return from
* a function.
* Note the trailing space after 'goto'
*/
#define c_return \
switch (1) \
while (1) \
if (0) { \
default: { \
CLEANUP_FUNC_; \
} \
} else \
return
/**
* An integer constant expression which
* declares an enumeration value that is one
* greater than the same-name enumeration
* value in the containing scope. This
* is for internal use; don't use it
*/
#define CV_CLEANUP_ ((enum { \
cv_cleanup_ = cv_cleanup_ + 1 \
})1)
/**
* This macro establishes cleanup context for
* a function or compound statement. Use it
* only where declarations are allowed, such as
* at the beginning of a function or compound
* statement in C89.
*/
#define HAS_CLEANUP \
cleanup_t_ \
new_cleanup_[CV_CLEANUP_] = {{0}}, \
* p_new_cleanup_ = cleanup_init_( \
new_cleanup_, \
cleanup_, \
cv_cleanup_ \
), \
* cleanup_ = p_new_cleanup_
#ifndef ALL_IN_ONE
/* Wrap malloc */
#ifdef malloc
#undef malloc
#endif
#define malloc(size) c_malloc_(cleanup_, (size))
#endif
/*** Constants */
enum { cv_cleanup_ };
/*** Object types */
/* Internal use; don't use it */
struct s_cleanup_ {
struct s_cleanup_ * prev;
void * item;
};
typedef struct s_cleanup_ cleanup_t_;
/*** Objects */
/**
* Top-level cleanup context (empty).
* Internal use; don't use it
*/
extern cleanup_t_ cleanup_[1];
/*** Functions */
/* Internal use; don't use these */
extern cleanup_t_ * cleanup_init_(
cleanup_t_ *,
cleanup_t_ *,
int
);
extern void * c_malloc_(cleanup_t_ *, size_t);
extern void cleanup_do_(cleanup_t_ *);
extern void cleanup_chain_(cleanup_t_ *);
#endif /* CLEANUP_H_ */
/****
* cleanup.c
* 2011, Shao Miller
*/
#include <stdlib.h>
#include <stdio.h>
#ifndef ALL_IN_ONE
#include "cleanup.h"
#endif /* ALL_IN_ONE */
/*** Object types */
/* A cleanable item */
struct s_cleanup_item_ {
void * ref;
struct s_cleanup_item_ * next;
};
typedef struct s_cleanup_item_ s_cleanup_item;
/*** Objects */
cleanup_t_ cleanup_[1] = {{0, 0}};
/*** Functions */
cleanup_t_ * cleanup_init_(
cleanup_t_ * new,
cleanup_t_ * prev,
int level
) {
new->prev = prev;
return new;
}
void * c_malloc_(
cleanup_t_ * cleanup,
size_t size
) {
s_cleanup_item * item;
void * obj;
item = (malloc)(sizeof *item);
if (!item)
return item;
obj = (malloc)(size);
if (!obj) {
free(item);
return obj;
}
item->ref = obj;
/* Insert at beginning */
item->next = cleanup->item;
cleanup->item = item;
return obj;
}
static char * at_root(cleanup_t_ * cleanup) {
if (cleanup == cleanup_)
return " (root)";
return "";
}
void cleanup_do_(cleanup_t_ * cleanup) {
s_cleanup_item * item, * next;
printf(
"Cleanup @ %p%s\n",
(void *) cleanup,
at_root(cleanup)
);
item = cleanup->item;
cleanup->item = 0;
while (item) {
next = item->next;
printf("Freeing object @ %p\n", item->ref);
free(item->ref);
free(item);
item = next;
}
return;
}
void cleanup_chain_(cleanup_t_ * cleanup) {
printf(
"Cleaning chain @ %p%s\n",
(void *) cleanup,
at_root(cleanup)
);
while (cleanup) {
cleanup_t_ * next;
cleanup_do_(cleanup);
next = cleanup->prev;
cleanup->prev = 0;
cleanup = next;
}
return;
}
/**** Test program */
#ifndef ALL_IN_ONE
#include <stdio.h>
#include "cleanup.h"
#endif /* ALL_IN_ONE */
#ifndef malloc
/* Wrap malloc the same as cleanup.h does */
#ifdef malloc
#undef malloc
#endif
#define malloc(size) c_malloc_(cleanup_, (size))
#endif
/*** Functions */
/* Dummy */
int true_func(int x) { return x + 1; }
void test1_c_goto(void) {
puts("\ntest1_c_goto():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
/* Used in an 'if'-'else' */
if (true_func(0))
c_goto outer_scope;
else
puts("Failed!");
return;
}
outer_scope:
puts("Exiting");
return;
}
void test2_c_goto(void) {
puts("\ntest2_c_goto():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
c_goto outer_scope;
puts("Failed!");
return;
}
outer_scope:
puts("Exiting");
return;
}
void test3_c_continue(void) {
/* Expecting: 4 loops */
int x = 5;
puts("\ntest3_c_continue():");
while (--x) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
if (true_func(0))
c_continue;
else
puts("Oops!");
puts("Failed!");
return;
}
puts("Exiting");
return;
}
void test4_c_break(void) {
/* Expecting: 1 loop */
int x = 5;
puts("\ntest4_c_break():");
while (--x) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
if (true_func(0))
c_break;
else
puts("Oops!");
puts("Failed!");
return;
}
puts("Exiting");
return;
}
/** Function "returning 'void'" */
void test5_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest5_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
if (true_func(0)) {
puts("Attempting c_return");
c_return;
} else
puts("Oops!");
}
puts("Failed!");
return;
}
/** Function returning 'int' */
int test6_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest6_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
if (true_func(0)) {
puts("Attempting c_return");
c_return 1;
} else
puts("Oops!");
}
puts("Failed!");
return 0;
}
/** Function returning 'int' */
int test7_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest7_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
/* Another inner scope, below */
{
HAS_CLEANUP;
int * ip5 = malloc(sizeof *ip5);
int * ip6 = malloc(sizeof *ip6);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
printf("ip5: %p\n", (void *) ip5);
printf("ip6: %p\n", (void *) ip6);
if (true_func(0)) {
puts("Attempting c_return");
c_return 1;
} else
puts("Oops!");
}
}
puts("Failed!");
return 0;
}
/** Program entry-point */
int main(void) {
test1_c_goto();
test2_c_goto();
test3_c_continue();
test4_c_break();
test5_c_return();
if (!test6_c_return())
puts("Failed!");
if (!test7_c_return())
puts("Failed!");
return 0;
}
statements _could_ be overridden, theoretically) and where one uses the
'HAS_CLEANUP' macro within whatever scopes require resource cleanup.
Also available (with nice colours) at:
http://codepad.org/YLV13z1K
Can be compiled all as one file via copy and paste, or split into
component files.
Feedback welcome and appreciated, as always.
-----
/*
* To split into separate files, remove all
* lines containing 'ALL_IN_ONE'
*/
#define ALL_IN_ONE 1
/****
* cleanup.h
* 2011, Shao Miller
*/
#ifndef CLEANUP_H_
#include <stddef.h>
/*** Macros */
#define CLEANUP_H_
/**
* Block cleanup details
*/
#define CLEANUP_BLOCK_ cleanup_do_(cleanup_);
/**
* Function cleanup details
*/
#define CLEANUP_FUNC_ cleanup_chain_(cleanup_);
/**
* Perform block cleanup and jump to a label.
* Only use for jumping from an inner to an
* outer scope.
* Note the trailing space after 'goto'.
*/
#define c_goto \
switch (1) \
while (1) \
if (0) { \
default: { \
CLEANUP_BLOCK_; \
} \
} else \
goto
/**
* Perform block cleanup and continue a loop
*/
#define c_continue \
if (1) { \
CLEANUP_BLOCK_; \
continue; \
} else do ; while (0)
/**
* Perform block cleanup and break out of a
* a loop or 'switch' statement
*/
#define c_break \
if (1) { \
CLEANUP_BLOCK_; \
break; \
} else do ; while (0)
/**
* Perform function cleanup and return from
* a function.
* Note the trailing space after 'goto'
*/
#define c_return \
switch (1) \
while (1) \
if (0) { \
default: { \
CLEANUP_FUNC_; \
} \
} else \
return
/**
* An integer constant expression which
* declares an enumeration value that is one
* greater than the same-name enumeration
* value in the containing scope. This
* is for internal use; don't use it
*/
#define CV_CLEANUP_ ((enum { \
cv_cleanup_ = cv_cleanup_ + 1 \
})1)
/**
* This macro establishes cleanup context for
* a function or compound statement. Use it
* only where declarations are allowed, such as
* at the beginning of a function or compound
* statement in C89.
*/
#define HAS_CLEANUP \
cleanup_t_ \
new_cleanup_[CV_CLEANUP_] = {{0}}, \
* p_new_cleanup_ = cleanup_init_( \
new_cleanup_, \
cleanup_, \
cv_cleanup_ \
), \
* cleanup_ = p_new_cleanup_
#ifndef ALL_IN_ONE
/* Wrap malloc */
#ifdef malloc
#undef malloc
#endif
#define malloc(size) c_malloc_(cleanup_, (size))
#endif
/*** Constants */
enum { cv_cleanup_ };
/*** Object types */
/* Internal use; don't use it */
struct s_cleanup_ {
struct s_cleanup_ * prev;
void * item;
};
typedef struct s_cleanup_ cleanup_t_;
/*** Objects */
/**
* Top-level cleanup context (empty).
* Internal use; don't use it
*/
extern cleanup_t_ cleanup_[1];
/*** Functions */
/* Internal use; don't use these */
extern cleanup_t_ * cleanup_init_(
cleanup_t_ *,
cleanup_t_ *,
int
);
extern void * c_malloc_(cleanup_t_ *, size_t);
extern void cleanup_do_(cleanup_t_ *);
extern void cleanup_chain_(cleanup_t_ *);
#endif /* CLEANUP_H_ */
/****
* cleanup.c
* 2011, Shao Miller
*/
#include <stdlib.h>
#include <stdio.h>
#ifndef ALL_IN_ONE
#include "cleanup.h"
#endif /* ALL_IN_ONE */
/*** Object types */
/* A cleanable item */
struct s_cleanup_item_ {
void * ref;
struct s_cleanup_item_ * next;
};
typedef struct s_cleanup_item_ s_cleanup_item;
/*** Objects */
cleanup_t_ cleanup_[1] = {{0, 0}};
/*** Functions */
cleanup_t_ * cleanup_init_(
cleanup_t_ * new,
cleanup_t_ * prev,
int level
) {
new->prev = prev;
return new;
}
void * c_malloc_(
cleanup_t_ * cleanup,
size_t size
) {
s_cleanup_item * item;
void * obj;
item = (malloc)(sizeof *item);
if (!item)
return item;
obj = (malloc)(size);
if (!obj) {
free(item);
return obj;
}
item->ref = obj;
/* Insert at beginning */
item->next = cleanup->item;
cleanup->item = item;
return obj;
}
static char * at_root(cleanup_t_ * cleanup) {
if (cleanup == cleanup_)
return " (root)";
return "";
}
void cleanup_do_(cleanup_t_ * cleanup) {
s_cleanup_item * item, * next;
printf(
"Cleanup @ %p%s\n",
(void *) cleanup,
at_root(cleanup)
);
item = cleanup->item;
cleanup->item = 0;
while (item) {
next = item->next;
printf("Freeing object @ %p\n", item->ref);
free(item->ref);
free(item);
item = next;
}
return;
}
void cleanup_chain_(cleanup_t_ * cleanup) {
printf(
"Cleaning chain @ %p%s\n",
(void *) cleanup,
at_root(cleanup)
);
while (cleanup) {
cleanup_t_ * next;
cleanup_do_(cleanup);
next = cleanup->prev;
cleanup->prev = 0;
cleanup = next;
}
return;
}
/**** Test program */
#ifndef ALL_IN_ONE
#include <stdio.h>
#include "cleanup.h"
#endif /* ALL_IN_ONE */
#ifndef malloc
/* Wrap malloc the same as cleanup.h does */
#ifdef malloc
#undef malloc
#endif
#define malloc(size) c_malloc_(cleanup_, (size))
#endif
/*** Functions */
/* Dummy */
int true_func(int x) { return x + 1; }
void test1_c_goto(void) {
puts("\ntest1_c_goto():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
/* Used in an 'if'-'else' */
if (true_func(0))
c_goto outer_scope;
else
puts("Failed!");
return;
}
outer_scope:
puts("Exiting");
return;
}
void test2_c_goto(void) {
puts("\ntest2_c_goto():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
c_goto outer_scope;
puts("Failed!");
return;
}
outer_scope:
puts("Exiting");
return;
}
void test3_c_continue(void) {
/* Expecting: 4 loops */
int x = 5;
puts("\ntest3_c_continue():");
while (--x) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
if (true_func(0))
c_continue;
else
puts("Oops!");
puts("Failed!");
return;
}
puts("Exiting");
return;
}
void test4_c_break(void) {
/* Expecting: 1 loop */
int x = 5;
puts("\ntest4_c_break():");
while (--x) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
if (true_func(0))
c_break;
else
puts("Oops!");
puts("Failed!");
return;
}
puts("Exiting");
return;
}
/** Function "returning 'void'" */
void test5_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest5_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
if (true_func(0)) {
puts("Attempting c_return");
c_return;
} else
puts("Oops!");
}
puts("Failed!");
return;
}
/** Function returning 'int' */
int test6_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest6_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
if (true_func(0)) {
puts("Attempting c_return");
c_return 1;
} else
puts("Oops!");
}
puts("Failed!");
return 0;
}
/** Function returning 'int' */
int test7_c_return(void) {
HAS_CLEANUP;
int * ip1 = malloc(sizeof *ip1);
int * ip2 = malloc(sizeof *ip2);
puts("\ntest7_c_return():");
/* Inner scope, below */
{
HAS_CLEANUP;
int * ip3 = malloc(sizeof *ip3);
int * ip4 = malloc(sizeof *ip4);
/* Another inner scope, below */
{
HAS_CLEANUP;
int * ip5 = malloc(sizeof *ip5);
int * ip6 = malloc(sizeof *ip6);
printf("ip1: %p\n", (void *) ip1);
printf("ip2: %p\n", (void *) ip2);
printf("ip3: %p\n", (void *) ip3);
printf("ip4: %p\n", (void *) ip4);
printf("ip5: %p\n", (void *) ip5);
printf("ip6: %p\n", (void *) ip6);
if (true_func(0)) {
puts("Attempting c_return");
c_return 1;
} else
puts("Oops!");
}
}
puts("Failed!");
return 0;
}
/** Program entry-point */
int main(void) {
test1_c_goto();
test2_c_goto();
test3_c_continue();
test4_c_break();
test5_c_return();
if (!test6_c_return())
puts("Failed!");
if (!test7_c_return())
puts("Failed!");
return 0;
}