2014-01-22 2 views
1

У меня есть макрос, чтобы сделать версию аргументов по умолчанию:Как создать макрос, который определяет переменную?

#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__)

Проблема заключается в том, что теперь я хочу, чтобы обеспечить вариант на той функции, которая освобождает первый аргумент Log_logWarning после:

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

Проблема в том, что это нельзя использовать с возвращаемым значением функции. Например:

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

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

Поэтому мне было интересно, если есть способ создать локальную переменную первой, в которой я присвоит первый аргумент макроса, а затем использовать эту переменную в тесте и последующее свободное.

+0

Вы отметили как C++, так и C, какой? Из фрагмента кода, я полагаю, C. –

+0

Я бы идеально хотел, чтобы макрос был достаточно общим для работы с обоими. (Это также объясняет, почему я выдал результат malloc() в char *). – chacham15

+0

@StefanoSanfilippo Поскольку он спрашивает о макросах, ответ почти наверняка будет одинаковым для обоих языков. –

ответ

2

Вместо этого я бы использовал встроенную функцию, если вообще возможно.

Если вы должны использовать макрос, используйте do { ... } while (0) конструкцию:

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

do-while-0 trick позволяет иметь блок кода, в то время как он предотвращает случайное присоединение блока к конструкции другого управления потоком неправильно.

Эта полная тестовая программа компилируется 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; 
} 
+0

Но, конечно, он не скажет ему, что ему нужно для 'logtmp_'. И это не гарантирует, что символ не используется в одном из выражений, которые он передает. (Для последнего нет хорошего решения, я буду входить в '__LINE__' и сделать что-то действительно странное, но оно все равно не гарантия.) –

+0

Конечно, вы могли бы сходить с ума. Но из контекста видно, что аргумент 'str' является строкой формата printf, когда __VA_ARGS__ передается в Log_logWarning после него. Действительно, макросы уродливы в любом случае, и если вы создадите переменную с именем logtmp_ слишком плохо. Использование функции или встроенной функции было бы намного лучше. –

1

Во-первых, оберните ваши функции в do { } while(0), так что вы можете добавить точку с запятой вашего использования функций и избежать странные ошибки.

Во-вторых, да, вы можете использовать локальную переменную. См. Примеры http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html.

Простой пример:

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

Это может быть безопасно использоваться в качестве:

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

Смотрите также: Can a C macro contain temporary variables?

+0

С другой стороны, 'swap (a, _c);' не собирается делать то, что вы хотите. –

+0

Правда. '__swap_c' с меньшей вероятностью будет повторно использоваться или использовать' __swap_c _ ## # __FILE__ ## __LINE__' (непроверенный). – abligh

+0

Вы не можете использовать '__FILE__', потому что это строковый литерал. Однако я обычно работаю '__LINE__' в имени. (Формально, начиная имя с '__' является неопределенным поведением. Практически, риск мал, и, возможно, стоит избегать именования конфликтов иначе. Обычно вы можете установить какое-то соглашение об именах, которое резервирует имена для этого типа вещи.) –

1

Проблема знающего тип переменной (за исключением в C++ 11). Для остальных, вы можете использовать обычный трюк для обзорного:

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

В C и C++ до C++ 11, вы, вероятно, придется передать тип переменной в качестве аргумента макрос.

Смежные вопросы