Макрос файла показывает полный путь



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



/full/path/to/file.c


Я вижу



to/file.c


или



file.c
704   17  

17 ответов:

попробовать

#include <string.h>

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

для Windows используйте '\ \ 'вместо'/'.

вот совет, если вы используете 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, я не вижу причин, по которым вы не могли бы распространить это на свои собственные файлы makefiles. Например, у вас может быть такая строка:

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

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

затем использовать __FILENAME__ на месте __FILE__.

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

идея состоит в том, чтобы получить размер исходного каталога с помощью инструмента сборки и просто добавить его в __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.

чисто время компиляции решение здесь. Это основано на том, что 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__)

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

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

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

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

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

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

затем __FILE__ расширится до просто "file.c".

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

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

альтернативой является использование

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

Так как вы используете GCC, вы можете воспользоваться на

__BASE_FILE__ этот макрос расширяется до имени основного входного файла в виде Строковой константы C. Это исходный файл, который был указанный в командной строке препроцессора или компилятора C

а затем управлять тем, как вы хотите отобразить имя файла, изменив представление исходного файла (полный путь/относительный путь/базовое имя) во время компиляции.

использовать basename ()

небольшое изменение того, что @red1ynx предложил бы создать следующий макрос:

#define SET_THIS_FILE_NAME() \
    static const char* const THIS_FILE_NAME = \
        strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__;

в каждом из ваших .c(ПП) файлы добавить:

SET_THIS_FILE_NAME();

затем вы можете обратиться к THIS_FILE_NAME вместо __FILE__:

printf("%s\n", THIS_FILE_NAME);

Это означает, что строительство выполняется один раз за .файл c(pp) вместо каждого обращения к макросу.

он ограничен в использовании только .C (pp) файлы и были бы непригодны для использования из заголовочных файлов.

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

это можно легко сделать, определив статическую глобальную переменную в .h. Это определение дает отдельные и независимые переменные в каждом .cpp файл, который включает в себя .h. Чтобы быть многопоточным, стоит сделать переменную (ы) также потоковой локальной (TLS).

один переменная хранит имя файла (сжатое). Другой содержит нерезанное значение, которое __FILE__ дал. Файл h:

static __declspec( thread ) const char* fileAndThreadLocal_strFilePath = NULL;
static __declspec( thread ) const char* fileAndThreadLocal_strFileName = NULL;

сам макрос вызывает метод со всей логикой:

#define __FILENAME__ \
    GetSourceFileName(__FILE__, fileAndThreadLocal_strFilePath, fileAndThreadLocal_strFileName)

и функция реализована таким образом:

const char* GetSourceFileName(const char* strFilePath, 
                              const char*& rstrFilePathHolder, 
                              const char*& rstrFileNameHolder)
{
    if(strFilePath != rstrFilePathHolder)
    {
        // 
        // This if works in 2 cases: 
        // - when first time called in the cpp (ordinary case) or
        // - when the macro __FILENAME__ is used in both h and cpp files 
        //   and so the method is consequentially called 
        //     once with strFilePath == "UserPath/HeaderFileThatUsesMyMACRO.h" and 
        //     once with strFilePath == "UserPath/CPPFileThatUsesMyMACRO.cpp"
        //
        rstrFileNameHolder = removePath(strFilePath);
        rstrFilePathHolder = strFilePath;
    }
    return rstrFileNameHolder;
}

removePath () может быть реализован по-разному, но быстрый и простой, кажется, с strrchr:

const char* removePath(const char* path)
{
    const char* pDelimeter = strrchr (path, '\');
    if (pDelimeter)
        path = pDelimeter+1;

    pDelimeter = strrchr (path, '/');
    if (pDelimeter)
        path = pDelimeter+1;

    return path;
}

просто надеюсь немного улучшить макрос файла:

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

это ловит / и\, как просил царек Томчак, и это отлично работает в моей смешанной среде.

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

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

попробовать

#pragma push_macro("__FILE__")
#define __FILE__ "foobar.c"

после включения операторов в исходный файл и добавить

#pragma pop_macro("__FILE__")

в конце исходного файла.

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

вот решение, которое работает для сред, в которых нет библиотеки строк (ядро Linux, встроенные системы и т. д.):

#define FILENAME ({ \
    const char* filename_start = __FILE__; \
    const char* filename = filename_start; \
    while(*filename != '') \
        filename++; \
    while((filename != filename_start) && (*(filename - 1) != '/')) \
        filename--; \
    filename; })

Теперь просто используйте FILENAME вместо __FILENAME__. Да, это еще во время выполнения, но это работает.

вот портативная функция, которая работает как для Linux (путь '/'), так и для Windows (сочетание ' \ 'и'/').
компилируется с GCC, Clang и против

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

const char* GetFileName(const char *path)
{
    const char *name = NULL, *tmp = NULL;
    if (path && *path) {
        name = strrchr(path, '/');
        tmp = strrchr(path, '\');
        if (tmp) {
             return name && name > tmp ? name + 1 : tmp + 1;
        }
    }
    return name ? name + 1 : path;
}

int main() {
    const char *name = NULL, *path = NULL;

    path = __FILE__;
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path ="/tmp/device.log";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\Downloads\crisis.avi";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:\Downloads/nda.pdf";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = "C:/Downloads\word.doc";
    name = GetFileName(path);
    printf("path: %s, filename: %s\n", path, name);

    path = NULL;
    name = GetFileName(NULL);
    printf("path: %s, filename: %s\n", path, name);

    path = "";
    name = GetFileName("");
    printf("path: %s, filename: %s\n", path, name);

    return 0;
}

стандартный вывод:

path: test.c, filename: test.c
path: /tmp/device.log, filename: device.log
path: C:\Downloads\crisis.avi, filename: crisis.avi
path: C:\Downloads/nda.pdf, filename: nda.pdf
path: C:/Downloads\word.doc, filename: word.doc
path: (null), filename: (null)
path: , filename: 

вот решение, которое использует вычисление времени компиляции:

constexpr auto* getFileName(const char* const path)
{
    const auto* startPosition = path;
    for (const auto* currentCharacter = path;*currentCharacter != ''; ++currentCharacter)
    {
        if (*currentCharacter == '\' || *currentCharacter == '/')
        {
            startPosition = currentCharacter;
        }
    }

    if (startPosition != path)
    {
        ++startPosition;
    }

    return startPosition;
}

std::cout << getFileName(__FILE__);

Comments

    Ничего не найдено.