Jump Statement Hooks

S

Shao Miller

A recent code example in the thread "\"Automatic\" Resource Cleanup?"
might have been a bit difficult to digest, so here's a smaller bite...

The point of this code to provide "special" replacements for the C jump
statements and allow for executing an arbitrary statement (including a
compound statement) wherever these "special jump statements" are reached.

Also possible (and demonstrated in the example) is to override the real
C jump statements with the extra behaviour of the "special" ones.

In order to use the code, one needs two have the two header files
"jumphook.h" and "jmphookp.h" available, and one needs to '#include'
just the "jumphook.h" file.

The program below can be copied, pasted and compiled, as is. It is also
available with nice colours and example output at:

http://codepad.org/tjhRii20

As always, feedback is welcome and appreciated.

-----

/*****
* This program can be copied and pasted, as is.
* To split the program into three separate files, remove every
* line containing the text "ALL_IN_ONE". Enjoy :)
*/
#define ALL_IN_ONE

/****
* jumphook.h
* 2011, Shao Miller
*
* Provide replacements for C jump statements and allow these
* to be "hooked" in order to execute specified code whenever
* the statements are reached.
*/
#ifndef JUMPHOOK_H__

/*** Macros */

/**
* Use 'c_goto' as a replacement for the 'goto' statement.
* 'c_goto' will invoke the 'GOTO_HOOK' macro as a statement,
* if it is #defined.
*/
#define c_goto goto

/**
* Use 'c_continue' as a replacement for the 'continue' statement.
* 'c_continue' will invoke the 'CONTINUE_HOOK' macro as a
* statement, if it is #defined.
*/
#define c_continue continue

/**
* Use 'c_break' as a replacement for the 'break' statement.
* 'c_break' will invoke the 'BREAK_HOOK' macro as a statement,
* if it is #defined.
*/
#define c_break break

/**
* Use 'c_return' as a replacement for the 'return' statement.
* 'c_return' will invoke the 'RETURN_HOOK' macro as a
* statement, if it is #defined.
*/
#define c_return return

/**
* #define the 'GOTO_HOOK' function-like macro in your code if
* you wish to provide a statement (including a compound
* statement) to be executed whenever the 'c_goto' statement is
* reached. Although it is a function-like macro, no argument
* will be passed.
*/
#define GOTO_HOOK() statement

/**
* #define the 'CONTINUE_HOOK' function-like macro in your code if
* you wish to provide a statement (including a compound
* statement) to be executed whenever the 'c_continue' statement is
* reached. Although it is a function-like macro, no argument
* will be passed.
*/
#define CONTINUE_HOOK() statement

/**
* #define the 'BREAK_HOOK' function-like macro in your code if
* you wish to provide a statement (including a compound
* statement) to be executed whenever the 'c_break' statement is
* reached. Although it is a function-like macro, no argument
* will be passed.
*/
#define BREAK_HOOK() statement

/**
* #define the 'RETURN_HOOK' function-like macro in your code if
* you wish to provide a statement (including a compound
* statement) to be executed whenever the 'c_return' statement is
* reached. Although it is a function-like macro, no argument
* will be passed.
*/
#define RETURN_HOOK() statement

/**
* Switch special jump statements off with:
*/
#define JUMPHOOK_MACROS_OFF
/*
* Each of the 'c_*' jump statements will then expand to
* the corresponding regular jump statement. You can switch
* them back on with:
*/
#undef JUMPHOOK_MACROS_OFF
/*
* And they will expand to include the semantics described above.
*/

/**
* Hook each of the actual jump statements with the corresponding
* 'c_*' jump statements with:
*/
#define JUMPHOOK_REALLY_HOOK
/*
* And switch this behaviour off with:
*/
#undef JUMPHOOK_REALLY_HOOK

/*** Private */
#define JUMPHOOK_PRIVATE__
#ifndef ALL_IN_ONE
#include "jmphookp.h"
#undef JUMPHOOK_PRIVATE__
#endif /* ALL_IN_ONE */

#endif /* JUMPHOOK_H__ */



/****
* jmphookp.h
* 2011, Shao Miller
*
* Implementation details for "jump hooks."
* Don't expect to be able to make any sense of it.
*/
#ifdef JUMPHOOK_PRIVATE__

/*** Macros */

/* #include guard */
#define JUMPHOOK_H__

/** Joiners */
#define JUMPHOOK_JOIN2__(x, y) x ## y ## __
#define JUMPHOOK_JOIN__(x, y) JUMPHOOK_JOIN2__(x, y)
#define JUMPHOOK_JOIN4__(x, y) x ## y ## __
#define JUMPHOOK_JOIN3__(x, y) JUMPHOOK_JOIN4__(x, y)

/**
* Jump to a label.
* Note the trailing space after 'goto'
*/
#undef c_goto
#define c_goto \
JUMPHOOK_JOIN__(C_GOTO_, JUMPHOOK_MACROS_OFF)
#define C_GOTO___ goto
#define C_GOTO_JUMPHOOK_MACROS_OFF__ C_GOTO__
#define C_GOTO__ \
switch (1) \
while (1) \
if (0) { \
default: { \
GOTO_HOOK(); \
} \
} else \
goto

/** Continue a loop. */
#undef c_continue
#define c_continue \
JUMPHOOK_JOIN__(C_CONTINUE_, JUMPHOOK_MACROS_OFF)
#define C_CONTINUE___ continue
#define C_CONTINUE_JUMPHOOK_MACROS_OFF__ C_CONTINUE__
#define C_CONTINUE__ \
if (1) { \
CONTINUE_HOOK(); \
continue; \
} else do ; while (0)

/** Break out of a loop or 'switch' statement */
#undef c_break
#define c_break \
JUMPHOOK_JOIN__(C_BREAK_, JUMPHOOK_MACROS_OFF)
#define C_BREAK___ break
#define C_BREAK_JUMPHOOK_MACROS_OFF__ C_BREAK__
#define C_BREAK__ \
if (1) { \
BREAK_HOOK(); \
break; \
} else do ; while (0)

/**
* Return from a function, possibly with a value.
* Note the trailing space after 'return'
*/
#undef c_return
#define c_return \
JUMPHOOK_JOIN__(C_RETURN_, JUMPHOOK_MACROS_OFF)
#define C_RETURN___ return
#define C_RETURN_JUMPHOOK_MACROS_OFF__ C_RETURN__
#define C_RETURN__ \
switch (1) \
while (1) \
if (0) { \
default: { \
RETURN_HOOK(); \
} \
} else \
return

#undef GOTO_HOOK
#undef CONTINUE_HOOK
#undef BREAK_HOOK
#undef RETURN_HOOK

/** Really hook the C jump statements (or not) */
#define goto \
JUMPHOOK_JOIN3__(JUMPHOOK_GOTO_, JUMPHOOK_REALLY_HOOK)
#define JUMPHOOK_GOTO___ c_goto
#define JUMPHOOK_GOTO_JUMPHOOK_REALLY_HOOK__ goto

#define continue \
JUMPHOOK_JOIN3__(JUMPHOOK_CONTINUE_, JUMPHOOK_REALLY_HOOK)
#define JUMPHOOK_CONTINUE___ c_continue
#define JUMPHOOK_CONTINUE_JUMPHOOK_REALLY_HOOK__ continue

#define break \
JUMPHOOK_JOIN3__(JUMPHOOK_BREAK_, JUMPHOOK_REALLY_HOOK)
#define JUMPHOOK_BREAK___ c_break
#define JUMPHOOK_BREAK_JUMPHOOK_REALLY_HOOK__ break

#define return \
JUMPHOOK_JOIN3__(JUMPHOOK_RETURN_, JUMPHOOK_REALLY_HOOK)
#define JUMPHOOK_RETURN___ c_return
#define JUMPHOOK_RETURN_JUMPHOOK_REALLY_HOOK__ return

/*** Function types */
typedef void f_jumphook_dummy__(void);

/*** Function declarations */
static f_jumphook_dummy__ jumphook_dummy__;

/*** Objects */
static f_jumphook_dummy__ * GOTO_HOOK = jumphook_dummy__;
static f_jumphook_dummy__ * CONTINUE_HOOK = jumphook_dummy__;
static f_jumphook_dummy__ * BREAK_HOOK = jumphook_dummy__;
static f_jumphook_dummy__ * RETURN_HOOK = jumphook_dummy__;

/*** Function definitions */
static void jumphook_dummy__(void) {
(void)sizeof GOTO_HOOK;
(void)sizeof CONTINUE_HOOK;
(void)sizeof BREAK_HOOK;
(void)sizeof RETURN_HOOK;
return;
}

#endif /* JUMPHOOK_PRIVATE__ */



/****
* test.c
* 2011, Shao Miller
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#ifndef ALL_IN_ONE
#include "jumphook.h"
#endif /* ALL_IN_ONE */

/*
* Make your choices. The hooks for this particular testing
* program are only active if 'JUMPHOOK_STATEMENTS_OFF' is '0'
* and 'TEST_REALLY_HOOK' is '1', because this program uses the
* actual C jump statements and not the special 'c_*' "jump
* statements."
*/
#define JUMPHOOK_STATEMENTS_OFF 0
#define TEST_GOTO_HOOK 1
#define TEST_CONTINUE_HOOK 1
#define TEST_BREAK_HOOK 1
#define TEST_RETURN_HOOK 1
#define TEST_REALLY_HOOK 1

void notice(const char * hook, int line) {
printf("%s() reached @ line %d\n", hook, line);
return;
}

#define NOTICE(str) (notice((str), __LINE__))

#if JUMPHOOK_STATEMENTS_OFF
#define JUMPHOOK_MACROS_OFF
#endif

#if TEST_GOTO_HOOK
#define GOTO_HOOK() \
NOTICE("GOTO_HOOK")
#endif

#if TEST_CONTINUE_HOOK
#define CONTINUE_HOOK() \
NOTICE("CONTINUE_HOOK")
#endif

#if TEST_BREAK_HOOK
#define BREAK_HOOK() \
NOTICE("BREAK_HOOK")
#endif

#if TEST_RETURN_HOOK
#define RETURN_HOOK() \
NOTICE("RETURN_HOOK")
#endif

#if TEST_REALLY_HOOK
#define JUMPHOOK_REALLY_HOOK
#endif

int main(void) {
/* Test 'c_goto' */
if (1)
goto test_label;
else
assert(!"c_goto failed!");

/* Test 'c_continue' */
while (0)
test_label:
if (1)
continue;
else
assert(!"c_continue failed!");

/* Test 'c_break' */
while (1)
if (1)
break;
else
assert(!"c_break failed!");

/* Test 'c_return' */
if (1)
return EXIT_SUCCESS;
else
assert(!"c_return failed!");

/* Hopefully impossible */
assert(!"Please report!");
}
 

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,769
Messages
2,569,577
Members
45,054
Latest member
LucyCarper

Latest Threads

Top