0

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.

4
  • 1
    Macros can't evaluate the value of the expression num_symbols. You'll need an integer constant expression somewhere or no can do. Is num_symbols guaranteed to be set to FREQ_MAX_SYMBOL somewhere?
    – Lundin
    Commented Nov 26, 2024 at 10:08
  • 2
    Macros are handled at compile time. num_symbols has a run time value so the answer is no. This is not the job for a macro. Can't you just use a simple function for this?
    – 4386427
    Commented Nov 26, 2024 at 10:09
  • Seems a bit of an XY-problem. Why do want to do this. What problem are you trying to solve?
    – 4386427
    Commented Nov 26, 2024 at 10:19
  • I suggest writing a function to print the structure to a stream. It can use for loops to print the req_freqs and cur_freqs elements. In multi-threaded applications, you can call flockfile(fp); / funlockfile(fp); (for POSIX) or _lock_file(fp); / _unlock_file(fp); (for MS Windows) at the start / end of the function to prevent interference from other threads writing to the same stream.
    – Ian Abbott
    Commented Nov 26, 2024 at 11:54

2 Answers 2

2

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]}
3
  • 1
    OP has: int num_symbols; // <= FREQ_MAX_SYMBOL so how would this help?
    – 4386427
    Commented Nov 26, 2024 at 10:39
  • @4386427 "if you can guarantee that the num_symbols member corresponds to FREQ_MAX_SYMBOL". Otherwise no can do.
    – Lundin
    Commented Nov 26, 2024 at 10:48
  • okay. well, I could be wrong but to me it seems num_symbols can have any value <= FREQ_MAX_SYMBOL
    – 4386427
    Commented Nov 26, 2024 at 10:59
-1

printf ignores unused arguments (C 2024 7.23.6.2, third sentence of first paragraph), so you can do this by using f->num_symbols to index into an array of format strings and then listing all the values. You would use your existing FREQ_VALUES listing all the arguments and change FREQ_FORMAT:

#define FREQ_FORMAT FormatStrings[f->num_symbols]

where FormatStrings is defined as an array of format strings in the obvious way. This supposes you will use this in a printf by itself, not with an additional string concatenated to the format.

However, the common way to print this structure as you desire would be to write a function which accepts a pointer to the structure type, qualified with const, that prints the initial part and then uses a loop to print the values.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.