Форум программистов, компьютерный форум, киберфорум
C для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.83/6: Рейтинг темы: голосов - 6, средняя оценка - 4.83
4 / 4 / 1
Регистрация: 20.04.2011
Сообщений: 141
1

Как использовать #undef или вообще не использовать

10.09.2013, 13:11. Просмотров 1102. Ответов 8
Метки нет (Все метки)

Посоветуйте какую лучше применить концепцию.

Хочу сделать в проекте унифицированные сообщения об ошибках в едином стиле. Чтобы они выглядели так:

C
1
2
ERROR:   main.c foo -1 UNKNOWN_ERROR
Message: main.c foo 0 OK
И чтобы в коде они выглядели одинаково, примерно так:

C
1
2
if (error) 
printf("%s %s %s %d %s\n", ERROR_PREFIX, SOURCE_PREFIX, FUNC_PREFIX, error, GetStringOfError(error));
Здесь ERROR_PREFIX и SOURCE_PREFIX можно задать дефайнами в начале каждого исходника, а вот как быть с FUNC_PREFIX? Можно ли его тоже дефайном делать в каждой функции? Например так:

C
1
2
3
4
5
6
7
8
9
10
11
12
int foo(void)
{
#define FUNC_PREFIX foo
 
<...>
<... use FUNC_PREFIX ...>
if (something) return 1;
<... use FUNC_PREFIX ...>
 
#undef FUNC_PREFIX
return 0;
}
Смущает что: если #undef будет стоять всегда в конце, как быть с промежуточными return-ами? Если мы не доходим до строки #undef, а выходим раньше, что станет с FUNC_PREFIX?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
10.09.2013, 13:11
Ответы с готовыми решениями:

Как в VC++2008 использовать Framewok 2.0 или не использовать вообще??
интересует консольное приложение - по умолчанию .Net3.5, и поменять нельзя, бо соответствующий ...

Оставить все как есть || использовать парсер xml || или использовать struct
Собственно имеется код... #include &lt;io.h&gt; #include &lt;string&gt; #include &lt;vector&gt; #include...

Где стоит использовать bootstrap и стоит ли вообще использовать CSS фреймворки?
Здравствуйте. Лично я ужасаюсь ковырять стили, когда к сайту подключен bootstrap и мало понимаю,...

Rgb2gray как вообще это использовать?
Добрый день = imread('C:\w211.jpg'); I = ind2gray(D,map);! расскажите дилетанту почему всегда...

8
Клюг
7662 / 3177 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
10.09.2013, 17:07 2
А чем assert() не устраивает?
0
Эксперт С++
5036 / 3096 / 271
Регистрация: 11.11.2009
Сообщений: 7,047
11.09.2013, 15:14 3
tiger(), существует встроенный идентификатор __func__, генерируемый компилятором в теле каждой функции и содержащий строковое имя содержащей его функции.
1
Эксперт С++
5810 / 3461 / 356
Регистрация: 08.02.2010
Сообщений: 7,448
12.09.2013, 06:32 4
Цитата Сообщение от tiger() Посмотреть сообщение
Смущает что: если #undef будет стоять всегда в конце, как быть с промежуточными return-ами? Если мы не доходим до строки #undef, а выходим раньше, что станет с FUNC_PREFIX?
Препроцессор работает не в рантайме, а ещё до непосредственной компиляции.

Вручную ничего определять не надо, можно воспользоваться стандартными макросами __FILE__ и __LINE__ (ERROR_PREFIX можно передавать в качестве аргумента) вместе с идентификатором __func__, как предложил silent_1991. Для большего удобства всё это можно обернуть в макрос:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
 
#ifndef NDEBUG
#define CHECK_ERRNO(FMT, ...)                           \
    do {                                                \
        if(errno) {                                     \
            int errsv = errno;                          \
            fprintf(stderr,                             \
                    "Error at %s:%s:%d "                \
                    "(code: %d reason: \"%s\"): "       \
                    FMT "\n", __FILE__,                 \
                    __func__, __LINE__, errno,          \
                    strerror(errno),                    \
                    ##__VA_ARGS__);                     \
            errno = errsv;                              \
       }                                                \
    } while(0)
#else
#define CHECH_ERRNO(FMT, ...) ((void) 0)
#endif /* NDEBUG */
 
void test_fun(const char *filename)
{
    FILE *file = fopen(filename, "r");
    CHECK_ERRNO("Opening \"%s\"...", filename);
    if(file) {
        fclose(file);
    }
}
 
int main(void)
{
    test_fun("/there/is/no/such/file");
    exit(0);
}
Возможный вывод:
Код
~/samples/c $ bin/sample 
Error at /home/nameless/samples/c/main.c:test_fun:28 (code: 2 reason: "No such file or directory"): Opening "/there/is/no/such/file"...
Цитата Сообщение от silent_1991 Посмотреть сообщение
существует встроенный идентификатор __func__, генерируемый компилятором в теле каждой функции и содержащий строковое имя содержащей его функции.
+ GCC определяет __FUNCTION__ для той же самой цели. Вдруг у ТС gcc, не поддерживающий C99?
0
4 / 4 / 1
Регистрация: 20.04.2011
Сообщений: 141
16.09.2013, 07:29  [ТС] 5
Цитата Сообщение от Nameless One Посмотреть сообщение
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
 
#ifndef NDEBUG
#define CHECK_ERRNO(FMT, ...)                           \
    do {                                                \
        if(errno) {                                     \
            int errsv = errno;                          \
            fprintf(stderr,                             \
                    "Error at %s:%s:%d "                \
                    "(code: %d reason: \"%s\"): "       \
                    FMT "\n", __FILE__,                 \
                    __func__, __LINE__, errno,          \
                    strerror(errno),                    \
                    ##__VA_ARGS__);                     \
            errno = errsv;                              \
       }                                                \
    } while(0)
#else
#define CHECH_ERRNO(FMT, ...) ((void) 0)
#endif /* NDEBUG */
Во, такой макрос как раз то что надо. Только я не все понял здесь..

Во-первых зачем нужен do {} while (0), если можно просто написать этот кусок кода и он тоже выполнится один раз?
Во-вторых я не понял откуда берутся переменные errno, errsv, чему они равны, здесь нигде не инициализируются.
И еще - зачем в CHECH_ERRNO неопределенное количество аргументов?
0
Эксперт С++
5810 / 3461 / 356
Регистрация: 08.02.2010
Сообщений: 7,448
16.09.2013, 07:41 6
Цитата Сообщение от tiger() Посмотреть сообщение
Во-первых зачем нужен do {} while (0)
Это стандартная идиома при написании многострочных макросов. Объяснять, зачем тут do {} while(0), если честно, лень, я это уже делал здесь (читать комментарии). Ну или можешь загуглить «c multiline macro».

Цитата Сообщение от tiger() Посмотреть сообщение
Во-вторых я не понял откуда берутся переменные errno, errsv, чему они равны, здесь нигде не инициализируются.
Символ errno объявляется в заголовочном файле errno.h. Он содержит код ошибки последней вызванной библиотечной функции (если ошибка была). errsv мы определяем сами.

Цитата Сообщение от tiger() Посмотреть сообщение
И еще - зачем в CHECH_ERRNO неопределенное количество аргументов?
Там опечатка, должно быть CHECK_ERRNO. Зачем? Чтобы можно было эти аргументы вывести. Вывод производится по аналогии с функцией форматного вывода printf, которая принимает форматную строку и неопределённое число аргументов. Например, строку 19 можно было бы заменить на
C
1
    CHECK_ERRNO("Opening \"%s\"...; my mood today is %s", filename, get_mood());
При этом вывелось бы
Код
Error at /home/nameless/samples/c/main.c:test_fun:28 (code: 2 reason: "No such file or directory"): Opening "/there/is/no/such/file"...; my mood today is sad
Это сделано лишь для удобства. Не нравится — можешь так не делать.
0
4 / 4 / 1
Регистрация: 20.04.2011
Сообщений: 141
16.09.2013, 08:05  [ТС] 7
Цитата Сообщение от Nameless One Посмотреть сообщение
Это стандартная идиома при написании многострочных макросов. Объяснять, зачем тут do {} while(0), если честно, лень, я это уже делал здесь (читать комментарии). Ну или можешь загуглить «c multiline macro».
Ага, сам почитаю, спасибо за ссылку

Цитата Сообщение от Nameless One Посмотреть сообщение
Символ errno объявляется в заголовочном файле errno.h. Он содержит код ошибки последней вызванной библиотечной функции (если ошибка была).
Я не уверен что мой код ошибки будет там находиться. Я отлаживаю такой код ошибки, который почти в каждой функции из моего API определен как локальная переменная int32_t например и возвращается по return. То есть я после таких функций имею переменную result условно говоря, которую проверяю на 0 - все хорошо или не0 - код ошибки.

Вот что у меня получилось
C
1
2
3
4
5
6
7
8
9
10
11
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
 
#ifdef UART_DEBUG
#define OUTPUT_ERROR(FMT, ...)                   \
    do {                                         \
        fprintf("ERROR:   in %s:%s"): FMT,  \
        FILE, __func__, ##__VA_ARGS__);                          \
    } while(0)
#else
#define CHECH_ERRNO(FMT, ...) ((void) 0)
#endif // UART_DEBUG
Еще добавил вначале макрос чтобы путь файла состоял из одного имени файла.

В целом возможно два варианта ошибочных ситуаций: мы знаем что была ошибка и ее код или просто знаем что была ошибка. То есть в тексте после очередной функции API я хочу использовать
C
1
OUTPUT_ERROR("Function API_1 failed\n");
или
C
1
OUTPUT_ERROR("Function API_1 failed with (%d) - %s\n", error_code, GetString(error_code));
Цитата Сообщение от Nameless One Посмотреть сообщение
Это сделано лишь для удобства. Не нравится — можешь так не делать.
Наоборот нравится, просто хочу разобраться как и что.
0
Эксперт С++
5810 / 3461 / 356
Регистрация: 08.02.2010
Сообщений: 7,448
16.09.2013, 08:14 8
Цитата Сообщение от tiger() Посмотреть сообщение
Я не уверен что мой код ошибки будет там находиться. Я отлаживаю такой код ошибки, который почти в каждой функции из моего API определен как локальная переменная int32_t например и возвращается по return. То есть я после таких функций имею переменную result условно говоря, которую проверяю на 0 - все хорошо или не0 - код ошибки.
Это был просто пример. Естественно, твоего кода ошибки там не будет, и тебе нужно изменить errno на свою переменную.
1
4 / 4 / 1
Регистрация: 20.04.2011
Сообщений: 141
16.09.2013, 08:52  [ТС] 9
Цитата Сообщение от Nameless One Посмотреть сообщение
Это был просто пример. Естественно, твоего кода ошибки там не будет, и тебе нужно изменить errno на свою переменную.
Спасибо Nameless One, очень удобный макрос!
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
16.09.2013, 08:52

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Локальная сеть предприятия. Использовать или не использовать AD, за и против?
Добрый день! Есть сервер на ubuntu server 14.04 1. isc-dhcp-server 2. squid3 - в процессе...

Смена настройки параметра сети (использовать или не использовать прокси-сервер для локальных подключений)
Всем доброго дня! Опишу сложившуюся ситуацию: На работу постоянно хожу с домашним ноутом. У нас в...

Использовать или не использовать шаблонизатор smarty?
Всем доброго времени суток. Возник такой вопрос на тему шаблонизатора smarty. В учебниках по PHP о...

Использовать или не использовать MasterPage
Я прекрасно понимаю чем прекрасен MasterPages но меня смущают некоторые моменты. разбейте мои мысли...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2020, vBulletin Solutions, Inc.