Имя файла печати сохранено во время компиляции - PullRequest
0 голосов
/ 15 мая 2018

Моя цель - напечатать имена файлов, а не относительный путь к имени файла. Я экспериментирую с этим с помощью макроса TRACE(). Поскольку все это в одном файле, я имитирую имя файла в качестве ввода для TRACE(). Так что в реальной жизни можно сказать, что входы заменены на __FILE__.

Код:

#include <stdio.h>
#include <string.h>

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))

#define TRACE(s, ...) \
    { \
    if (strrchr(s, '\\')) { \
        static const char str[] = __FILENAME__(s) "\n\r"; \
        printf(str, ##__VA_ARGS__); \
    } else { \
        static const char str[] = s "\n\r"; \
        printf(str, ##__VA_ARGS__); \
    } \
    }

int main() {
    TRACE("file.c");
    TRACE("parent\\file.c");
    return 0;
}

Выход:

file.c
strrchr("parent\\file.c", '\\')

Так что, если это локальный файл, он печатается как file.c, и это здорово. Это означает, что случай if в макросе работает :). Но когда это файл в другой папке, мне не удается «заштриховать» вычисления strrchr(s, '\\'). Зачем?

Кроме того, я не вижу проблемы с вычислением в определении, так как все определяется во время компиляции !! (Вот почему дело if работает, верно?)

Если я удаляю TOSTRING() из __FILENAME__, я получаю кучу ошибок. Поскольку он не может объединить выходные данные __FILENAME__ с str[]

Есть ли способ решить эту проблему?

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Предварительные наблюдения

Обратите внимание, что в C (в отличие от C ++) вы не можете инициализировать массив static const char str[] с результатом вызова функции.Если strrchr() обнаружил обратную косую черту, вы, вероятно, захотите напечатать имя из одной после обратной косой черты.И строковая спецификация не собирается приводить к строковому результату вызова strrchr().

. Также обратите внимание, что в общем случае не следует создавать имена функций или переменных, начинающиеся с подчеркивания. C11 §7.1.3 Зарезервированные идентификаторы говорит (частично):

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

См. Также Что означает двойное подчеркивание (__const) в C?

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

Простая адаптация

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

#define TRACE(s, ...) \
    do { \
        const char *basename = strrchr(s, '\\'); \
        if (basename == 0) \
            basename = s; \
        else \
            basename++; \
        printf(basename, ## __VA_ARGS__); \
    } while (0)

идиома do { … } while (0) iстандарт s;это позволяет вам написать:

if (something)
    TRACE("hocuspocus.c: test passed\n");
else
    TRACE("abracadabra.c: test failed\n");

Если вы используете в вопросе только фигурные скобки, точка с запятой после первой TRACE приводит к синтаксической ошибке else.См. Также макрос C #define для отладочной печати и Зачем использовать явно макросы do { … } while (0) и if … else в макросах? и do { … } while (0) - что это хорошодля?

Трюк ## __VA_ARGS__ подойдет, если вы знаете, что это расширение GCC (и Clang, потому что оно совместимо с GCC), а не часть стандартного C.

Также не совсем понятно, как вы планируете использовать переменные аргументы.Выглядит так, как будто вы могли бы сделать:

TRACE("some\\kibbitzer.c: value %d is out of the range [%d..%d]\n",
      value, MIN_RANGE, MAX_RANGE);

, где имя файла встроено в строку формата.Возможно, вы имеете в виду:

TRACE(__FILE__ ": value %d is out of the range [%d..%d]\n",
      value, MIN_RANGE, MAX_RANGE);

Это может сработать;__FILE__ является строковым литералом, в отличие от __func__, который является предопределенным идентификатором (static const char __func__[] = "…function name…";).

Наконец (пока), подумайте, должен ли вывод трассировки идти в стандартный вывод или в стандартную ошибку.Легко утверждать, что это должно идти к стандартной ошибке;это (вероятно) не является частью обычной работы программы.

Я рекомендую взглянуть на вопрос и ответ «Макрос отладки» - но я предвзят, поскольку написал ответ с наивысшими оценками.

Сокращение накладных расходов времени выполнения

Вы можете уменьшить накладные расходы времени выполнения до одного вызова до strrchr() для имени файла, если вы не будете связываться с автоматическими переменными и т. Д. Вы будете в порядке, есливы используете строковые литералы.

#define TRACE(s, ...) \
    do { \
        static const char *basename = 0;
        if (basename == 0) \
        {
            if ((basename = strrchr(s, '\\')) == 0) \
                basename = s; \
            else \
                basename++; \
        } \
        printf(basename, ## __VA_ARGS__); \
    } while (0)

Это инициализирует basename в ноль;при первом проходе по коду basename устанавливается в правильную позицию в строке;после этого больше нет вызова strrchr().

Предупреждение: указанный код не был скомпилирован.

0 голосов
/ 15 мая 2018

Я думаю, что есть некоторая проблема с пониманием того, как работают макросы и функции.

Макросы не «выполняются», они просто заменяют текст.Да, это происходит во время компиляции (фактически перед компиляцией), но только при замене.

Макросы не будут выполняться и кодировать или вызывать какие-либо функции (например, strrchr) во время компиляции.

В вашем коде у вас есть -

#define __FILENAME__(x) TOSTRING(strrchr(x, '\\'))

Когда используется __FILENAME__(foo), он заменяется на "strrchr(foo, '\\')".Я уверен, что это не то, что вы хотите.

Лично я не вижу здесь никакой причины использовать макросы.Просто превратите это в нормальную функцию.Компилятор оптимизирует его для вас.

...