chacham15
chacham15

Reputation: 14281

How can I create a macro which defines a variable?

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.

Upvotes: 1

Views: 240

Answers (3)

Colin D Bennett
Colin D Bennett

Reputation: 12140

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;
}

Upvotes: 2

James Kanze
James Kanze

Reputation: 154047

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.

Upvotes: 1

abligh
abligh

Reputation: 25199

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?

Upvotes: 1

Related Questions