0

I have the following macros (follow up to: When writing a macro in C, how do I find the type and printf specifier for an argument? ):

#define mu_format_specifier(expression) _Generic((expression), unsigned long: "%lu", int: "%i")

#define mu_assert_equal(actual, expected) do {                                                      \
  if (actual != expected) {                                                                         \
    char *message = malloc(MAX_ERROR_MESSAGE_LENGTH);                                               \
    if (message == NULL) { printf("malloc failed"); exit(1); }                                      \
    snprintf(message, MAX_ERROR_MESSAGE_LENGTH,                                                     \
      "required: %s != %s, reality: %s == " mu_format_specifier(actual),                            \
      #actual, #expected, #actual, actual);                                                         \
    return message;                                                                                 \
  }                                                                                                 \
} while (0)

The problem is that the mu_format_specifier seems to resolve to a char * rather than simply substituting "%lu" into "required: %s != %s, reality: %s == " mu_format_specifier(actual), which I would like to yield "required: %s != %s, reality: %s == " "%lu" which C would understand as one literal string.

I can see two horrible work arounds:

  1. make a version of version of mu_assert_equal for each type, and call the right one with a _Generic.

  2. snprintf the formatting string, though I may run into problems if the formatting string has to be const.

Is there a better alternative?

(The problem is that _Generic actually evaluates an expression, rather than simply substituting source characters - i.e. the "%lu" becomes it's own literal string, rather than just producing source characters which are unified with "required: %s != %s, reality: %s == ".)

Community
  • 1
  • 1
fadedbee
  • 42,671
  • 44
  • 178
  • 308
  • 1
    What is the output of the pre-processing phase ? – Serge Ballesta Jan 16 '15 at 10:27
  • @SergeBallesta That's how I fixed it - I saved the pre-processor output, then read the error messages for that, which were far better. That lead me to see that the output of `_Generic` is an expression, not a series of source-code bytes. I had wrongly thought that _Generic was some form of simple macro expansion. – fadedbee Jan 16 '15 at 11:47

1 Answers1

0

I made it work:

#define mu_assert_equal(actual, expected) do {                                            \
  if (actual != expected) {                                                               \
    char *message = malloc(MAX_ERROR_MESSAGE_LENGTH);                                     \
    if (message == NULL) { printf("malloc failed"); exit(1); }                            \
    snprintf(message, MAX_ERROR_MESSAGE_LENGTH, _Generic(                                 \
        (actual),                                                                         \
        unsigned long: "required: %s != %s, reality: %s == %lu",                          \
        int:           "required: %s != %s, reality: %s == %i"                            \
      ),                                                                                  \
      #actual, #expected, #actual, actual);                                               \
    return message;                                                                       \
  }                                                                                       \
} while (0)

The solution was to make whole formatting string the output expression from _Generic.

From test code:

mu_assert_equal(bytes_parsed, 1);

I get output:

required: bytes_parsed != 1, reality: bytes_parsed == 0
fadedbee
  • 42,671
  • 44
  • 178
  • 308