I have a C struct, for which I've written printf
formatting and value macros:
#define FREQ_MAX_SYMBOL 8
struct freq {
int num_symbols; // <= FREQ_MAX_SYMBOL
int tot_freq; // sum of all the frequencies
int req_freqs[FREQ_MAX_SYMBOL]; // required frequencies
int cur_freqs[FREQ_MAX_SYMBOL]; // current frequencies
};
#define FREQ_FORMAT "{num_symbols: %i, tot_freq: %i, req_freqs: [%i,%i,%i,%i,%i,%i.%i,%i], cur_freqs: [%i,%i,%i,%i,%i,%i,%i,%i]}"
#define FREQ_VALUES(f) f->num_symbols, f->tot_freq, \
f->req_freqs[0], f->req_freqs[1], f->req_freqs[2], f->req_freqs[3], f->req_freqs[4], f->req_freqs[5], f->req_freqs[6], f->req_freqs[7], \
f->cur_freqs[0], f->cur_freqs[1], f->cur_freqs[2], f->cur_freqs[3], f->cur_freqs[4], f->cur_freqs[5], f->cur_freqs[6], f->cur_freqs[7]
Is it possible to alter the macros so that they only create a num_symbols
number of formats and values for req_freqs[]
and cur_freqs[]
?
e.g. If num_symbols == 2
only req_freqs[0]
and [1]
and cur_freqs[0]
and [1]
should be printed.
This is likely not a good idea to begin with and you'd be much better off using a function + loops in run-time. Especially since you already threw performance out the window by using printf
, so there aren't many arguments for doing all of this at compile-time.
That being said, if you insist on macros and if you can guarantee that the num_symbols
member corresponds to FREQ_MAX_SYMBOL
, you could cook up a clunky, evil list of macros such as these:
#define FREQ_FORMAT_1 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i], cur_freqs: [%i]}"
#define FREQ_FORMAT_2 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i,%i], cur_freqs: [%i,%i]}"
#define FREQ_FORMAT_3 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i,%i,%i], cur_freqs: [%i,%i,%i]}"
#define FREQ_PARAM_1 (f).req_freqs[0], \
(f).cur_freqs[0]
#define FREQ_PARAM_2 (f).req_freqs[0], (f).req_freqs[1], \
(f).cur_freqs[0], (f).cur_freqs[1]
#define FREQ_PARAM_3 (f).req_freqs[0], (f).req_freqs[1], (f).req_freqs[2], \
(f).cur_freqs[0], (f).cur_freqs[1], (f).cur_freqs[2]
You'll need an upper limit for how many arguments you support. You'd then call these like:
#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)
#define FREQ_FORMAT CONCAT(FREQ_FORMAT_, FREQ_MAX_SYMBOL)
#define FREQ_VALUES(f) (f).num_symbols, (f).tot_freq, CONCAT(FREQ_PARAM_, FREQ_MAX_SYMBOL)
At least the first macro FREQ_FORMAT
requires an extra level of macro expansion since it is an object-like macro and not a function-like macro. Hence we need a helper macro to expand that one. And then the usual problem with ## getting applied before further macro expansion, so that one needs to be in a helper macro of its own.
Full example:
#include <stdio.h>
#define FREQ_MAX_SYMBOL 3
struct freq {
int num_symbols; // <= FREQ_MAX_SYMBOL
int tot_freq; // sum of all the frequencies
int req_freqs[FREQ_MAX_SYMBOL]; // required frequencies
int cur_freqs[FREQ_MAX_SYMBOL]; // current frequencies
};
#define FREQ_FORMAT_1 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i], cur_freqs: [%i]}"
#define FREQ_FORMAT_2 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i,%i], cur_freqs: [%i,%i]}"
#define FREQ_FORMAT_3 "{num_symbols: %i, tot_freq: %i, req_freqs: [%i,%i,%i], cur_freqs: [%i,%i,%i]}"
#define FREQ_PARAM_1 (f).req_freqs[0], \
(f).cur_freqs[0]
#define FREQ_PARAM_2 (f).req_freqs[0], (f).req_freqs[1], \
(f).cur_freqs[0], (f).cur_freqs[1]
#define FREQ_PARAM_3 (f).req_freqs[0], (f).req_freqs[1], (f).req_freqs[2], \
(f).cur_freqs[0], (f).cur_freqs[1], (f).cur_freqs[2]
#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)
#define FREQ_FORMAT CONCAT(FREQ_FORMAT_, FREQ_MAX_SYMBOL)
#define FREQ_VALUES(f) (f).num_symbols, (f).tot_freq, CONCAT(FREQ_PARAM_, FREQ_MAX_SYMBOL)
int main()
{
struct freq f =
{
.num_symbols = FREQ_MAX_SYMBOL,
.tot_freq = 123,
.req_freqs = {1,2,3},
.cur_freqs = {4,5,6},
};
printf(FREQ_FORMAT, FREQ_VALUES(f));
}
Output:
{num_symbols: 3, tot_freq: 123, req_freqs: [1,2,3], cur_freqs: [4,5,6]}