Макрос __FILE__ показывает полный путь - PullRequest
141 голосов
/ 13 декабря 2011

Стандартный предопределенный MACRO __FILE__, доступный в C, показывает полный путь к файлу. Есть ли способ сократить путь? Я имею в виду вместо

/full/path/to/file.c

Понятно

to/file.c

или

file.c

Ответы [ 22 ]

142 голосов
/ 13 декабря 2011

Попробуйте

#include <string.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

Для Windows используйте «\\» вместо «/».

50 голосов
/ 21 мая 2013

Вот совет, если вы используете cmake.От: http://public.kitware.com/pipermail/cmake/2013-January/053117.html

Я копирую подсказку, так что все это на этой странице:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst
  ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")

Если вы используете GNU make, я не вижу причин, по которым вы не смогли расширитьэто для ваших собственных make-файлов.Например, у вас может быть такая строка:

CXX_FLAGS+=-D__FILENAME__='\"$(subst $(SOURCE_PREFIX)/,,$(abspath $<))\"'"

, где $(SOURCE_PREFIX) - префикс, который вы хотите удалить.

Затем используйте __FILENAME__ вместо __FILE__.

21 голосов
/ 03 декабря 2016

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

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

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

В файле CMakeLists.txt определите макрос, имеющий длину пути к вашему проекту в CMake:

# The additional / is important to remove the last character from the path.
# Note that it does not matter if the OS uses / or \, because we are only
# saving the path size.
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")

В исходном коде определите макрос __FILENAME__, который просто добавляет размер исходного пути к макросу __FILE__:

#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)

Тогда просто используйте этот новый макрос вместо __FILE__ макроса. Это работает, потому что путь __FILE__ всегда начинается с пути к вашему исходному каталогу CMake. Удалив его из строки __FILE__, препроцессор позаботится об указании правильного имени файла, и все это будет относиться к корню вашего проекта CMake.

Если вы заботитесь о производительности, это так же эффективно, как и использование __FILE__, поскольку и __FILE__, и SOURCE_PATH_SIZE являются известными константами времени компиляции, поэтому компилятор может оптимизировать их.

Единственное место, где это может потерпеть неудачу, - это если вы используете это для сгенерированных файлов, а они находятся в папке сборки вне исходного кода. Тогда вам, вероятно, придется создать еще один макрос, используя переменную CMAKE_BUILD_DIR вместо CMAKE_SOURCE_DIR.

11 голосов
/ 10 июня 2015

Как минимум для gcc, значение __FILE__ - это путь к файлу , указанный в командной строке компилятора .Если вы скомпилируете file.c следующим образом:

gcc -c /full/path/to/file.c

, __FILE__ расширится до "/full/path/to/file.c".Если вы вместо этого сделаете это:

cd /full/path/to
gcc -c file.c

, тогда __FILE__ расширится до "file.c".

Это может быть или не быть практичным.

Стандарт Cне требует такого поведения.Все, что он говорит о __FILE__, - это то, что он расширяется до «Предполагаемого имени текущего исходного файла (символьная строка-литерал)».

Альтернативой является использование директивы #line.Он переопределяет текущий номер строки и, необязательно, имя исходного файла.Если вы хотите переопределить имя файла, но оставить номер строки в покое, используйте макрос __LINE__.

Например, вы можете добавить это в верхней части file.c:

#line __LINE__ "file.c"

Единственная проблема с этим заключается в том, что он назначает указанный номер строки после строки, а первый аргумент #line должен быть последовательность цифр , чтобы вы моглине делать что-то вроде

#line (__LINE__-1) "file.c"  // This is invalid

Проверка того, что имя файла в директиве #line соответствует фактическому имени файла, оставлена ​​в качестве упражнения.

По крайней мере для gcc, этотакже повлияет на имя файла, указанное в диагностических сообщениях.

11 голосов
/ 28 декабря 2016

Чисто время компиляции здесь.Он основан на том факте, что sizeof() строкового литерала возвращает его длину + 1.

#define STRIPPATH(s)\
    (sizeof(s) > 2 && (s)[sizeof(s)-2] == '/' ? (s) + sizeof(s) - 1 : \
    sizeof(s) > 3 && (s)[sizeof(s)-3] == '/' ? (s) + sizeof(s) - 2 : \
    sizeof(s) > 4 && (s)[sizeof(s)-4] == '/' ? (s) + sizeof(s) - 3 : \
    sizeof(s) > 5 && (s)[sizeof(s)-5] == '/' ? (s) + sizeof(s) - 4 : \
    sizeof(s) > 6 && (s)[sizeof(s)-6] == '/' ? (s) + sizeof(s) - 5 : \
    sizeof(s) > 7 && (s)[sizeof(s)-7] == '/' ? (s) + sizeof(s) - 6 : \
    sizeof(s) > 8 && (s)[sizeof(s)-8] == '/' ? (s) + sizeof(s) - 7 : \
    sizeof(s) > 9 && (s)[sizeof(s)-9] == '/' ? (s) + sizeof(s) - 8 : \
    sizeof(s) > 10 && (s)[sizeof(s)-10] == '/' ? (s) + sizeof(s) - 9 : \
    sizeof(s) > 11 && (s)[sizeof(s)-11] == '/' ? (s) + sizeof(s) - 10 : (s))

#define __JUSTFILE__ STRIPPATH(__FILE__)

Не стесняйтесь расширять каскад условных операторов до максимально разумного имени файла в проекте.Длина пути не имеет значения, пока вы проверяете достаточно далеко от конца строки.

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

4 голосов
/ 13 декабря 2011

Нет времени для компиляции.Очевидно, что вы можете сделать это во время выполнения, используя среду выполнения C, как продемонстрировали некоторые другие ответы, но во время компиляции, когда включается препроцессор, вам не повезло.

4 голосов
/ 13 декабря 2011

Используйте функцию basename () или, если вы работаете в Windows, _splitpath () .

#include <libgen.h>

#define PRINTFILE() { char buf[] = __FILE__; printf("Filename:  %s\n", basename(buf)); }

Также попробуйте man 3 basename воболочки.

4 голосов
/ 06 января 2017

в против, когда с / FC, FILE равняется полному пути, без / FC FILE равняется имени файла. ref здесь

4 голосов
/ 28 февраля 2017

Если вы используете CMAKE с компилятором GNU, это global определение работает нормально:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")
3 голосов
/ 24 января 2019
  • C ++ 11
  • msvc2015u3, gcc5.4, clang3.8.0

    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char (& str)[S], size_t i = S - 1)
    {
        return (str[i] == '/' || str[i] == '\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <size_t S>
    inline constexpr size_t get_file_name_offset(const wchar_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == L'/' || str[i] == L'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char16_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == u'/' || str[i] == u'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <size_t S>
    inline constexpr size_t get_file_name_offset(const char32_t (& str)[S], size_t i = S - 1)
    {
        return (str[i] == U'/' || str[i] == U'\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <typename T>
    inline constexpr size_t get_file_name_offset(T (& str)[1])
    {
        return 0;
    }
    

    int main()
    {
         printf("%s\n", &__FILE__[get_file_name_offset(__FILE__)]);
    }
    

Код генерирует смещение времени компиляции, когда:

  • gcc: не менее gcc6,1 + -O1
  • msvc: поместить результат в переменную constexpr:

      constexpr auto file = &__FILE__[get_file_name_offset(__FILE__)];
      printf("%s\n", file);
    
  • clang: сохраняется при оценке времени некомпиляции

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

    namespace utility {

        template <typename T, T v>
        struct const_expr_value
        {
            static constexpr const T value = v;
        };

    }

    #define UTILITY_CONST_EXPR_VALUE(exp) ::utility::const_expr_value<decltype(exp), exp>::value

    int main()
    {
         printf("%s\n", &__FILE__[UTILITY_CONST_EXPR_VALUE(get_file_name_offset(__FILE__))]);
    }

https://godbolt.org/z/P1y3ZI

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...