1

I have a macro to do a version of default arguments:

#define Log_getMacro4(_1, _2, _3, _4, NAME, ...) NAME
#define Log_logWarning4(...) Log_log__("warning", __VA_ARGS__)
#define Log_logWarning3(...) Log_log__("warning", __VA_ARGS__, __LINE__)
#define Log_logWarning2(...) Log_log__("warning", __VA_ARGS__, __FILE__, __LINE__)
#define Log_logWarning1(...) Log_log__("warning", __VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__)
#define Log_logWarning(...) Log_getMacro4(__VA_ARGS__, Log_logWarning4, Log_logWarning3, Log_logWarning2, Log_logWarning1)(__VA_ARGS__)

The problem is that I now want to provide a variant on that function which frees the first argument to Log_logWarning after:

#define Log_logWarning_free(str, ...) Log_logWarning(str, __VA_ARGS__); if (str) free(str);

The problem is that this cannot be used with the return value of a function. For example:

char *foo(){
    char *ret = (char*)malloc(30*sizeof(char));
    strcpy(ret, "Hello World");
    return ret;
}

void bar(){
    Log_logWarning_free(foo());
}

Therefore, I was wondering if there is a way to create a local variable first to which I will assign the first argument of the macro and then use that variable in the test and subsequent free.

chacham15
  • 13,719
  • 26
  • 104
  • 207
  • You tagged both C++ and C, which one? From the code fragment, I suppose C. – Stefano Sanfilippo Jan 22 '14 at 18:49
  • I would ideally like the macro to be general enough to work with both. (It also explains why I casted the result of malloc() to char*). – chacham15 Jan 22 '14 at 18:50
  • @StefanoSanfilippo Since he's asking about macros, the answer will almost certainly be the same for both languages. – James Kanze Jan 22 '14 at 18:51
  • @JamesKanze I would avoid macros in C++ as much as it is practically possible (and that was the point of the question :) ). But strictly speaking about macros, yes, I guess you are right. – Stefano Sanfilippo Jan 22 '14 at 19:03
  • For C++ it would be a good idea to separate the aspects of picking up contextual info, and handling argument defaults. For C the macro trickery is perhaps the best you can do. So, the language matters. – Cheers and hth. - Alf Jan 22 '14 at 19:21

3 Answers3

2

I would use an inline function instead, if at all possible.

If you have to use a macro, use a do { ... } while (0) construct:

#define Log_logWarning_free(str, ...) \
    do { \ 
        char * logtmp_ = str; \
        Log_logWarning(logtmp_, __VA_ARGS__); \
        if (logtmp_) free(logtmp_); \
    } while (0)

The do-while-0 trick allows you to have a code block, while it prevents accidentally attaching the block to an another flow control construct incorrectly.

This full test program compiles with 4.7.2:

#include <stdlib.h>

#define Log_logWarning_free(str, ...) \
    do { \
        char * logtmp_ = str; \
        Log_logWarning(logtmp_, __VA_ARGS__); \
        if (logtmp_) free(logtmp_); \
    } while (0)


void Log_logWarning(char* fmt, ...);
char * get_log_str(void);

int main()
{
    Log_logWarning_free(get_log_str(), 1, 2, 3);
    return 0;
}
Community
  • 1
  • 1
Colin D Bennett
  • 11,294
  • 5
  • 49
  • 66
  • But of course, it won't tell him the type he needs for `logtmp_`. And it won't guarantee that the symbol isn't used in one of the expressions he's passing in. (There's no really good solution for the latter; I'll munge in `__LINE__`, and make something really weird, but it's still not a guarantee.) – James Kanze Jan 22 '14 at 18:58
  • Sure, you could go crazy with it. But it appears from the context that the `str` argument is a printf-style format string as the __VA_ARGS__ is passed to Log_logWarning after it. Really, macros are ugly anyway and if you create a variable called logtmp_ too bad. Using a function or inline function would be much better. – Colin D Bennett Jan 22 '14 at 19:05
1

Firstly, wrap your functions in do { } while(0) so you can add a semicolon to your usage of the functions and avoid weird errors.

Secondly, yes, you can use a local variable. See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html for examples.

An simple example would be

#define swap(a,b) do {int _c=b; b=a; a=_c;} while(0)

That can then be safely invoked as:

int x = 5;
int y = 7;
swap(x, y);

See also: Can a C macro contain temporary variables?

Community
  • 1
  • 1
abligh
  • 24,573
  • 4
  • 47
  • 84
  • On the other hand, `swap(a, _c);` is not going to do what you want. – James Kanze Jan 22 '14 at 18:59
  • True. `__swap_c` is less likely to be reused, or use `__swap_c_## # __FILE__ ## __LINE__` (untested). – abligh Jan 22 '14 at 19:03
  • You can't use `__FILE__`, because it is a string literal. I usually work `__LINE__` into the name, however. (Formally, starting the name with `__` is undefined behavior. Practically, the risk is small, and it may be worth it to avoid naming conflicts otherwise. Usually, you can establish some sort of naming convention, which reserves names for this sort of thing.) – James Kanze Jan 23 '14 at 09:03
1

The problem is knowing the type of the variable (except in C++11). For the rest, you can use the usual trick for scoping:

#define X(y) do { auto CONCAT(_log_tmp_,__LINE__) = (y); ...  } while(false);

In C and in C++ pre-C++11, you'll probably have to pass the type of the variable in as an argument to the macro.

James Kanze
  • 150,581
  • 18
  • 184
  • 329