Hi
i would like to have fff
to support the dynamic exchange of the functions.
Lets say i have a function foo+bar
and i want to replacy it with my fakes called fooTest+barTest
. Then i would like to have expanded code like this:
In case for working with unittesting, i set the #define UNIT_TESTING
header:
extern void (*foo_TestPtr)( int a );
void foo( int a );
extern int (*bar_TestPtr)( int a );
int bar( int a );
source:
void (*foo_TestPtr)( int a ) = NULL;
void foo( int a ){
if( foo_TestPtr ){
foo_TestPtr( a );
return;
}
// ... implementation
}
int (*bar_TestPtr)( int a ) = NULL;
int bar( int a ){
if( bar_TestPtr ){
return bar_TestPtr( a );
}
// ... implementation
}
When #define UNIT_TESTING is not set, this expands to:
header:
void foo( int a );
int bar( int a );
source:
void foo( int a ){
// ... implementation
}
int bar( int a ){
// ... implementation
}
And this is, how i want to write it:
header:
FUNC_DECL( void, foo,( int a ));
FUNC_DECL( int, bar,( int a ));
source:
FUNC_IMPL( void, foo,( int a )){
FUNC_VOID_REDIR( foo, (a))
// ... implementation
}
FUNC_IMPL( int, bar,( int a )){
FUNC_VALUE_REDIR( bar, (a))
// ... implementation
}
To implement, this can look like this:
#ifdef UNIT_TESTING
/**
* Declare a function normally and a function pointer with _TestPtr postfix.
*
* Example:
* int example( char* name );
*
* Use of the macro:
* FUNC_DECL(int, example, ( char* name ));
*
* Expands to:
* int example( char* name );
* int (*example_TestPtr)( char* name );
*/
# define FUNC_DECL(r,n,a) \
r n a;\
extern r (*n##_TestPtr) a
#else
# define FUNC_DECL(r,n,a) extern r n a
#endif
#ifdef UNIT_TESTING
/**
* Define the function pointer and the function signature
*
* Example:
* int example( char* name ){ ...
*
* Use of the macro:
* FUNC_IMPL(int, example, ( char* name )) { ...
*
* Expands to:
* int (*example_TestPtr)( char* name ) = NULL;
* int example( char* name ) { ...
*
*
*/
# define FUNC_IMPL(r,n,a) \
r (*n##_TestPtr) a = NULL;\
r n a
#else
# define FUNC_IMPL(r,n,a) r n a
#endif
#ifdef UNIT_TESTING
/**
* Function redirect for functions without return value.
*
* FUNC_IMPL(void, example, ( char* name )) {
* FUNC_REDIR_VOID( example, name )
* ...
* }
*
* Expands to:
* void (*example_TestPtr)( char* name ) = NULL;
* void example( char* name ) {
* if( example_TestPtr ){
* return example_TestPtr( name );
* }
*/
# define FUNC_REDIR_VOID(n,a) \
do{ if( n##_TestPtr ){\
n##_TestPtr a;\
return;\
} } while( false )
#else
# define FUNC_REDIR_VOID(n,a) \
do{ } while( false )
#endif
#ifdef UNIT_TESTING
/**
* Function redirect for function with return value.
*
* FUNC_IMPL(int, example, ( char* name )) {
* FUNC_REDIR_VALUE( example, name )
* ...
* }
*
* Expands to:
* int (*example_TestPtr)( char* name ) = NULL;
* int example( char* name ) {
* if( example_TestPtr ){
* example_TestPtr( name );
* return;
* }
*/
# define FUNC_REDIR_VALUE(n,a) \
do{ if( n##_TestPtr ){\
return n##_TestPtr a;\
}} while( false )
#else
# define FUNC_REDIR_VALUE(n,a) \
do{ } while( false )
#endif
This way, you have function runtime substitution, like you discussed here:
https://meekrosoft.wordpress.com/2012/07/20/test-seams-in-c-function-pointers-vs-preprocessor-hash-defines-vs-link-time-substitution/
But the two contra arguments are removed. Now the IDE can resolve the function calls, do auto completion and do code analysis.
Finally, if there would be a way to make the FUNC_IMPL and FUNC_VOID_REDIR a single macro call, this would be super cool. But I don't know how to do that. Perhaps you?
Frank