Issue
I am attempting to create a class of functions which can be called with any number and any type of arguments--similar to printf
, but with one key difference: I would like to cast all arguments to uint64_t
before passing them to the function. Ideally this should be implicit, i.e. without me having to write ( uint64_t ) arg1 , ( uint64_t ) arg2 , ( uint64_t ) arg3 , . . .
every time.
My current solution is based on an answer to this previous question.
Here is the basic way to do what I want for any non-pointer types:
// The variadic function.
void _f ( char c , uint64_t arg_count , uint64_t* args );
// Macro to append two extra arguments comprising the variadic argument list to the function.
#define ARGS(...) \
sizeof ( ( uint64_t[] ){ __VA_ARGS__ } ) / sizeof ( uint64_t ) \
, ( uint64_t[] ){ __VA_ARGS__ }
// Macro to call the variadic function.
#define f(c,...) \
_f ( (c) , ARGS ( __VA_ARGS__ ) )
It can be used like this:
f ( 'A' , 27 , -30L , '/' );
I also wanted to be able to pass pointers to the function, but this trips the compiler warning -Wint-conversion
, so I had to add some extra macros I got from an answer to this question to suppress the warning each time a function which uses ARGS
is called:
#define PRAGMA(args) \
_Pragma ( #args )
#define DISABLE_WARNING(warning) \
PRAGMA ( GCC diagnostic push ) \
PRAGMA ( GCC diagnostic ignored #warning )
#define REENABLE_WARNING() \
PRAGMA ( GCC diagnostic pop )
// New macro to call the variadic function.
#define f(c,...) \
({ \
DISABLE_WARNING ( -Wint-conversion ) \
_f ( (c) , ARGS ( __VA_ARGS__ ) ); \
REENABLE_WARNING () \
})
Now the function can also be called like this as well:
float f_ = 32.2f;
f ( 'A' , 27 , -30L , '/' , &f_ , &"Hello world" );
But, because of the limitation in syntax where the #pragma
cannot be embedded within an expression, the function cannot return a value. Sure, I could make the function have an extra pointer argument and write a return value to it, but ideally I would want the function itself to return a uint64_t
directly. Is there a way to do this?
Solution
OP mentioned a limitation that the preprocessing #pragma
directive cannot be embedded within an expression. Actually, it can be embedded within an expression (spread out over several lines), but it cannot be embedded within a macro definition. However, OP is not using the #pragma
directive, they are using the _Pragma( )
operator, and that can be embedded within a macro definition.
OP is making use of the GNU GCC "statement expression" extension (a compound statement contained within parentheses) in the definition of their f(c,...)
macro:
#define f(c,...) \
({ \
DISABLE_WARNING ( -Wint-conversion ) \
_f ( (c) , ARGS ( __VA_ARGS__ ) ); \
REENABLE_WARNING () \
})
If the last statement or declaration within a GNU statement expression is an expression statement, the type and value of the GNU statement expression is the type and value of the expression within that expression statement, otherwise the GNU statement expression has no value.
The DISABLE_WARNING( -Wint-conversion )
and REENABLE_WARNING()
macro expansions ultimately expand to sequences of _Pragma( )
operators that are removed after the preprocessing phase, so do not form part of any C expressions, declarations, or statements. Therefore the last statement or declaration within the GNU statement expression is the expression statement that results from the expansion of _f ( (c) , ARGS ( __VA_ARGS__ ) );
. If _f
is declared with a return type other than void
, the type and value of the GNU statement expression will be the type and return value of that call to _f
.
In conclusion, OP's statement that the expansion of f(c,...)
cannot return a value from the called function _f
is false.
Answered By - Ian Abbott Answer Checked By - Marilyn (WPSolving Volunteer)