Было бы действительно здорово найти решение только для препроцессора. Я боюсь, что предложения, основанные на строковых операциях, будут выполняться во время выполнения.
cdleonard
9
Поскольку вы используете gcc, я думаю, вы можете изменить __FILE__содержимое, изменив имя файла, которое вы передаете в командной строке. Так что вместо того gcc /full/path/to/file.c, чтобы попробовать cd /full/path/to; gcc file.c; cd -;. Конечно, это немного больше, чем если вы полагаетесь на текущий каталог gcc для пути включения или расположения выходного файла. Редактировать: документы gcc предполагают, что это полный путь, а не аргумент имени входного файла, но это не то, что я вижу для gcc 4.5.3 на Cygwin. Так что вы можете попробовать это на Linux и посмотреть.
Стив Джессоп
4
GCC 4.5.1 (специально для arm-none-eabi) использует точный текст имени файла в командной строке. В моем случае это была ошибка среды IDE, вызвавшая GCC со всеми полными именами файлов, вместо того, чтобы помещать текущий каталог где-нибудь разумным (местоположение файла проекта, возможно?) Или настраиваемым и используя относительные пути оттуда. Я подозреваю, что многие IDE делают это (особенно в Windows) из-за некоторого дискомфорта, связанного с объяснением, где на самом деле находится «текущий» каталог для приложения с графическим интерфейсом.
RBerteig
3
@SteveJessop - надеюсь, что вы прочитали этот комментарий. У меня есть ситуация, когда я вижу __FILE__напечатанный как, ../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.cи я хочу знать, где gcc скомпилировал файл так, что этот файл расположен относительно, как показано.
Чан Ким
1
Этот вопрос не является дураком связанного. Во-первых, связанный относится к C ++, и ответы, следовательно, углубляются в C ++ macro esoterica. Во-вторых, в вопросе OP нет ничего, что требовало бы макрорешения. Это только торжественно указывает на проблему и задает открытый вопрос.
/является допустимым разделителем пути в именах файлов, передаваемых в CreateFile () и т. д. Однако это не всегда означает, что вы можете использовать его /где угодно в Windows, поскольку существует традиция (унаследованная от CPM) использовать /в качестве аргумента в командной строке. Но качественный инструмент был бы осторожен с именами файлов раскол в обоих слэш и обратной косой черты , чтобы избежать каких - либо проблем для людей , которые умудряются использовать /.
RBerteig
5
@AmigableClarkKant, нет, вы можете смешивать оба разделителя в одном и том же имени файла.
RBerteig
2
Если ваша платформа поддерживает это, char* fileName = basename(__FILE__); это определенно есть в Linux и OS X, но не знаю о Windows.
JeremyP
14
Это может быть сокращено до strrchr("/" __FILE__, '/') + 1. __FILE__Заранее добавив "/" к , strrchr гарантированно найдет что-то, и, следовательно, условный?: Больше не нужен.
Если вы используете GNU make, я не вижу причин, по которым вы не могли бы распространить это на ваши собственные make-файлы. Например, у вас может быть такая строка:
Боюсь, тогда это не работает для ФАЙЛА, указанного в заголовочном файле.
Байян Хуан,
2
Согласен с @BaiyanHuang, но не уверен, что комментарий понятен. __FILE__не является простым символом препроцессора, это изменение текущего файла, часто используется для выдачи имени текущего файла (заголовок или исходный модуль). Это __FILENAME__будет иметь только самый внешний источник
nhed
3
Решение этого ответа не является переносимым, поскольку оно использует экранирование оболочки Bourne. Лучше использовать CMake, чтобы реализовать его чистым и переносимым способом. Видеть здесь макро-ответ define_file_basename_for_sources .
Колин Д. Беннетт
3
Вариант GNU Make: CFLAGS + = -D__FILENAME __ = \ "$ (notdir $ <) \"
ctuffli,
4
@firegurafiku На встроенных платформах размер кода часто является скорее ограничением, чем скоростью. Если в каждом файле есть операторы отладки, это может быстро увеличить размер файла со строками полного пути. В данный момент я имею дело с такой платформой, и ссылки __FILE__везде выдуваются из пространства кода.
mrtumnus
26
Я только что подумал об отличном решении, которое работает как с исходными файлами, так и с заголовочными файлами, очень эффективно и работает во время компиляции на всех платформах без специфичных для компилятора расширений. Это решение также сохраняет относительную структуру каталогов вашего проекта, чтобы вы знали, в какой папке находится файл, и только относительно корня вашего проекта.
Идея состоит в том, чтобы получить размер исходного каталога с помощью инструмента сборки и просто добавить его в __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__макросу:
Тогда просто используйте этот новый макрос вместо __FILE__ макроса. Это работает, потому что __FILE__путь всегда будет начинаться с пути к вашему исходному каталогу CMake. Удалив его из __FILE__строки, препроцессор позаботится о том, чтобы указать правильное имя файла, и все это будет относиться к корню вашего проекта CMake.
Если вы заботитесь о производительности, это так же эффективно, как использование __FILE__ , потому что оба __FILE__и SOURCE_PATH_SIZEявляются известными константами времени компиляции, поэтому он может быть оптимизирован компилятором.
Единственное место, где это может потерпеть неудачу, - это если вы используете это в сгенерированных файлах, а они находятся в папке для сборки вне исходного кода. Тогда вам, вероятно, придется создать другой макрос, используя CMAKE_BUILD_DIRпеременную вместо CMAKE_SOURCE_DIR.
Сначала я этого не поняла. Я закодировал примеры и сравнил их с gcc и clang, и они работают. Я также экспериментирую с простым добавлением различных буквенных числовых значений, и это ведет себя так, как я ожидал. Затем меня наконец осенило. __FILE__указатель на массив байтов Таким образом, добавление числового литерала является просто добавлением указателя. Очень умный @RenatoUtsch
Даниэль
Это работает, только если исходный файл находится в каталоге cmake list. Если исходный файл находится за пределами, он сломается, возможно, с доступом вне литеральной строки. Так что будьте осторожны с этим.
Андри
Это на самом деле не уменьшает размер кода. Я предполагаю, что весь путь все еще скомпилирован в двоичный файл, только указатель изменен.
Тарион
И __FILE__макрос, и макрос SOURCE_PATH_SIZE являются константами, известными во время компиляции. Я ожидаю, что современные оптимизирующие компиляторы смогут обнаружить, что часть строки не используется, и просто удалить ее из двоичного файла. Во всяком случае, я не думаю, что эти несколько байтов будут иметь существенное значение в размере двоичного файла, поэтому меня это не особо волнует.
RenatoUtsch
@RenatoUtsch В проекте, над которым я работаю, есть изменение, которое просто указывает имя файла, но имеет недостаток, заключающийся в том, что имя C-файла также указывается в заголовке. Изменение было сделано для того, чтобы получить воспроизводимые сборки. Так что с gcc с -O2 будет ли строка действительно оптимизирована и сборка сделана воспроизводимой?
Пол Стелиан
19
Чисто скомпилируйте временное решение здесь. Он основан на том факте, что sizeof()строковый литерал возвращает свою длину + 1.
Не стесняйтесь расширять каскад условных операторов до максимально разумного имени файла в проекте. Длина пути не имеет значения, если вы проверяете достаточно далеко от конца строки.
Я посмотрю, смогу ли я получить подобный макрос без жестко запрограммированной длины с макрорекурсией ...
Классный ответ. Вы должны использовать по крайней мере, -O1чтобы использовать это во время компиляции.
Цифровая травма
1
Разве это не задом наперед? Вы хотите найти последнее вхождение '/', что означает, что вы должны начать с sizeof(s) > 2проверки в первую очередь. Кроме того, это не сработало во время компиляции для меня, в -Os. Строки полного пути присутствовали в выходном двоичном файле.
mrtumnus
15
По крайней мере для 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 это также повлияет на имя файла, указанное в диагностических сообщениях.
Кит Томпсон, это отличное решение, спасибо. Единственная проблема состоит в том, что кажется, что этот макрос сокращает __LINE__значение на единицу. Так __LINE__написано в строке xжест оценивается в x-1. По крайней мере, с gcc 5.4.
elklepo
1
@Klepak: Вы правы, и это стандартное поведение. Директива «заставляет реализацию вести себя так, как если бы следующая последовательность строк источника начиналась с строки источника, которая имеет номер строки, указанный в последовательности цифр». И это должна быть последовательность цифр , чтобы вы не могли ее использовать #line __LINE__-1, "file.c". Я обновлю свой ответ.
Кит Томпсон
1
Как это будет для других компиляторов, таких как Clang, MSVC или Intel C Compiler?
Франклин Ю.
8
Немного опоздал на вечеринку, но для GCC взглянем на -ffile-prefix-map=old=newвариант:
При компиляции файлов, находящихся в старом каталоге , запишите любые ссылки на них в результате компиляции, как если бы файлы находились в новом каталоге . Указание этого параметра эквивалентно указанию всех отдельных -f*-prefix-mapпараметров. Это может использоваться для создания воспроизводимых сборок, которые не зависят от местоположения. Смотрите также
-fmacro-prefix-mapи -fdebug-prefix-map.
Так что для моих сборок Jenkins я добавлю-ffile-prefix-map=${WORKSPACE}/=/ и еще один, чтобы удалить префикс локальной установки пакета dev.
П р и м е ч а н и е - К сожалению, эта -ffile-prefix-mapопция доступна только в GCC 8 и более поздних версиях, -fmacro-prefix-mapчто, я думаю , и делает свою __FILE__роль. Например, GCC 5, у нас есть только то, -fdebug-prefix-mapчто не влияет __FILE__.
template<typename T,size_t S>inlineconstexprsize_t get_file_name_offset(const T (& 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<typename T>inlineconstexprsize_t get_file_name_offset(T (& str)[1]){return0;}
'
int main(){
printf("%s\n",&__FILE__[get_file_name_offset(__FILE__)]);}
Код генерирует смещение времени компиляции, когда:
clang: сохраняется при некомпилированной оценке времени
Есть хитрость, заставляющая все 3 компилятора выполнять оценку времени компиляции даже в конфигурации отладки с отключенной оптимизацией:
namespace utility {template<typename T, T v>struct const_expr_value
{staticconstexprconst 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__))]);}
Там нет времени компиляции способ сделать это. Очевидно, что вы можете сделать это во время выполнения, используя среду выполнения C, как продемонстрировали некоторые другие ответы, но во время компиляции, когда включается препроцессор, вам не повезло.
strrchrответ мог правдоподобно быть вычислено во время компиляции, хотя, конечно , до сих пор не препроцессором. Я не знаю, действительно ли это делает gcc, я не проверял, но я уверен, что он вычисляет strlenстроковые литералы во время компиляции.
Стив Джессоп
@ Steve - может быть, но это большая зависимость от конкретного поведения компилятора.
Шон
Я не думаю, что это большая зависимость, потому что я очень сомневаюсь, что этот код критичен к производительности. И если это так, выведите его из цикла. В тех случаях, когда это огромная сделка, потому что вам абсолютно необходим строковый литерал, содержащий только базовое имя, вы, возможно, могли бы вычислить правильную строку во время сборки , запустив некоторый скрипт над исходным кодом.
Стив Джессоп
5
Возможно, это не критично для производительности, но это легко можно рассматривать как критичное для конфиденциальности. Нет никаких веских причин для раскрытия моей организационной практики для каждого клиента в строках, помещенных в выпущенный EXE-файл. Хуже того, для приложений, созданных от имени клиента, эти строки могут показывать вещи, которые мой клиент может предпочесть не делать, например не быть автором своего собственного продукта. Поскольку __FILE__неявно вызывается assert(), эта утечка может произойти без каких-либо других явных действий.
RBerteig
@RBerteig само по __FILE__себе базовое имя может также указывать на то, что клиент может предпочесть не делать, поэтому при __FILE__любом его использовании - будь то полный абсолютный путь или просто базовое имя - возникают те же проблемы, на которые вы указали. В этой ситуации все результаты должны быть тщательно изучены, и для вывода клиентам необходимо ввести специальный API. Остальные выходные данные должны быть выгружены в / dev / NULL или stdout, а stderr должен быть закрыт. :-)
@mahmood: char file_copy[] = __FILE__; const char *filename = basename(__FILE__);. Причина для копирования заключается в том, что basename может изменить входную строку. Вы также должны следить за тем, чтобы указатель результата был действителен только до basenameповторного вызова. Это означает, что это не потокобезопасно.
Стив Джессоп
@ SteveJessop, ах, я забыл. Правда.
Профессор Фалькен
1
@Amigable: честно говоря, я подозреваю, что basenameна самом деле не изменит входную строку, которая получается из-за того __FILE__, что у входной строки нет /конца в конце, и поэтому нет необходимости изменять . Так что вам это может сойти с рук, но я думаю, что в первый раз, когда кто-то увидит basename, он должен увидеть это со всеми ограничениями.
Стив Джессоп
@SteveJessop В справочной странице BSd для basename () упоминается, что устаревшая версия basename () принимает const char * и не изменяет строку. В справочной странице linux ничего не говорится о const, но упоминается, что она может вернуть часть строки аргумента. Так что лучше всего быть консервативным в отношении basename ().
Профессор Фалькен
@SteveJessop, хе-хе, я только сейчас, внимательно изучив ваш комментарий, через четыре года осознаю, что /в конце строки означает, что у basename может быть веская причина изменить свой аргумент.
Профессор Фалькен,
4
Если вы используете CMAKEкомпилятор GNU, это globalопределение отлично работает:
-Wno-builtin-macro-redefinedотключить предупреждения компилятора для переопределения __FILE__макроса.
Для тех компиляторов, которые не поддерживают это, обратитесь к Robust способ ниже.
Уберите путь проекта из пути к файлу - это ваше реальное требование. Вам не понравится тратить время, чтобы узнать, где находится header.hфайл, src/foo/header.hили src/bar/header.h.
Мы должны раздеть __FILE__макрос вcmake конфигурационном файле.
Этот макрос используется в большинстве существующих кодов. Просто переопределите это может освободить вас.
Компиляторы вроде gccпредопределяют этот макрос из аргументов командной строки. И полный путь записывается в makefileсгенерированном cmake.
Жесткий код в CMAKE_*_FLAGSТребуется .
Есть некоторые команды для добавления опций или определений компилятора в более позднюю версию, например, add_definitions()и add_compile_definitions(). Эти команды будут анализировать функции make, как и substраньше, к исходным файлам. Этого мы не хотим.
Это не работает для содержимого, поступающего из включаемых файлов.
Chqrlie
Вы можете опубликовать код? Распространенным случаем является установка переменных в локальной области и использование их в другой.
Levi.G
Например, если вы используете в __FILE__своем определении диагностику во встроенной функции, определенной в заголовочном файле, диагностика во время выполнения сообщит имя файла, переданного компилятору, а не имя включаемого файла, тогда как номер строки будет обратитесь к включаемому файлу.
chqrlie
да, он предназначен для этого, для наиболее распространенного использования #define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args). при использовании LOG()макроса вы не очень хотите видеть log.hв сообщениях. в конце концов, __FILE__макрос раскрывается в каждом файле C / Cpp (модуле компиляции) вместо включаемых файлов.
Levi.G
3
Небольшое отклонение от предложенного @ red1ynx будет заключаться в создании следующего макроса:
Я сделал макрос __FILENAME__ который избегает разрезания полного пути каждый раз. Проблема заключается в том, чтобы хранить полученное имя файла в локальной переменной cpp.
Это можно легко сделать, определив статическую глобальную переменную в файле .h . Это определение дает отдельные и независимые переменные в каждом файле .cpp, который включает в себя .h . Для того, чтобы быть многопоточным, стоит сделать переменную (и) также поточно локальной (TLS).
Одна переменная хранит имя файла (сокращенное). Другой содержит необрезанное значение, которое __FILE__дал. Файл h:
constchar*GetSourceFileName(constchar* strFilePath,constchar*& rstrFilePathHolder,constchar*& 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:
push_macroи pop_macroнестандартны. (gcc поддерживает их для совместимости с компиляторами Microsoft Windows.) В любом случае нет смысла толкать и вставлять определение __FILE__; восстановленное значение не будет использовано после окончания исходного файла в любом случае. Уборщик способ изменить значение __FILE__является#line __LINE__ "foobar.c"
Если вы оказались на этой странице в поисках способа удалить абсолютный исходный путь, указывающий на некрасивое местоположение сборки из бинарного файла, который вы отправляете, приведенный ниже может удовлетворить ваши потребности.
Хотя это не дает точного ответа, о котором автор выразил свое желание, так как предполагает использование CMake , он довольно близок. Жаль, что никто не упоминал об этом раньше, так как это сэкономило бы мне массу времени.
OPTION(CMAKE_USE_RELATIVE_PATHS "If true, cmake will use relative paths" ON)
Установка вышеуказанной переменной в ONсгенерирует команду сборки в формате:
cd /ugly/absolute/path/to/project/build/src &&
gcc <.. other flags ..>-c ../../src/path/to/source.c
В результате __FILE__макрос разрешится в../../src/path/to/source.c
В документации CMake 3.4+ говорится, что «эта переменная не имеет никакого эффекта. Частично реализованный эффект, который она имела в предыдущих выпусках, был удален в CMake 3.4». Если у вас работает 3.13, есть еще одна причина.
__BASE_FILE__Этот макрос расширяется до имени основного входного файла в форме строковой константы C. Это исходный файл, который был указан в командной строке препроцессора или компилятора Си
а затем управляйте тем, как вы хотите отобразить имя файла, изменив представление исходного файла (полный путь / относительный путь / базовое имя) во время компиляции.
не имеет значения. Я использовал __FILE__и __BASE_FILE__однако они оба показывают полный путь к файлу
mahmood
как вы вызываете компилятор?
Зиу
2
Тогда держу пари, что SCONS вызывает gcc вот так gcc /absolute/path/to/file.c. Если вы найдете способ изменить это поведение (открыв еще один вопрос о SO, смеется?), Вам не нужно изменять строку во время выполнения
ziu
17
Этот ответ на 100% неверен. __BASE_FILE__(как говорят документы, хотя и неясно) создает имя файла, указанного в командной строке , например, test.cили в /tmp/test.cзависимости от того, как вы вызывали компилятор. Это точно так же, как __FILE__, за исключением случаев, когда вы находитесь внутри файла заголовка, в этом случае __FILE__выдает имя текущего файла (например foo.h), тогда как __BASE_FILE__продолжает создавать test.c.
Quuxplusone
0
Вот решение, которое работает для сред, в которых нет строковой библиотеки (ядро Linux, встроенные системы и т. Д.):
Возможно, вы захотите проверить и «/», и «\», в зависимости от платформы.
Технофил
0
#include<algorithm>#include<string>usingnamespace std;
string f( __FILE__ );
f = string((find(f.rbegin(), f.rend(),'/')+1).base()+1, f.end());// searches for the '/' from the back, transfers the reverse iterator // into a forward iterator and constructs a new sting with both
__FILE__
содержимое, изменив имя файла, которое вы передаете в командной строке. Так что вместо тогоgcc /full/path/to/file.c
, чтобы попробоватьcd /full/path/to; gcc file.c; cd -;
. Конечно, это немного больше, чем если вы полагаетесь на текущий каталог gcc для пути включения или расположения выходного файла. Редактировать: документы gcc предполагают, что это полный путь, а не аргумент имени входного файла, но это не то, что я вижу для gcc 4.5.3 на Cygwin. Так что вы можете попробовать это на Linux и посмотреть.__FILE__
напечатанный как,../../../../../../../../rtems/c/src/lib/libbsp/sparc/leon2/../../shared/bootcard.c
и я хочу знать, где gcc скомпилировал файл так, что этот файл расположен относительно, как показано.Ответы:
Пытаться
Для Windows используйте «\\» вместо «/».
источник
/
является допустимым разделителем пути в Windows./
является допустимым разделителем пути в именах файлов, передаваемых в CreateFile () и т. д. Однако это не всегда означает, что вы можете использовать его/
где угодно в Windows, поскольку существует традиция (унаследованная от CPM) использовать/
в качестве аргумента в командной строке. Но качественный инструмент был бы осторожен с именами файлов раскол в обоих слэш и обратной косой черты , чтобы избежать каких - либо проблем для людей , которые умудряются использовать/
.char* fileName = basename(__FILE__);
это определенно есть в Linux и OS X, но не знаю о Windows.strrchr("/" __FILE__, '/') + 1
.__FILE__
Заранее добавив "/" к , strrchr гарантированно найдет что-то, и, следовательно, условный?: Больше не нужен.Вот совет, если вы используете cmake. От: http://public.kitware.com/pipermail/cmake/2013-January/053117.html
Я копирую совет, так что все это на этой странице:
Если вы используете GNU make, я не вижу причин, по которым вы не могли бы распространить это на ваши собственные make-файлы. Например, у вас может быть такая строка:
где
$(SOURCE_PREFIX)
префикс, который вы хотите удалить.Тогда используйте
__FILENAME__
вместо__FILE__
.источник
__FILE__
не является простым символом препроцессора, это изменение текущего файла, часто используется для выдачи имени текущего файла (заголовок или исходный модуль). Это__FILENAME__
будет иметь только самый внешний источник__FILE__
везде выдуваются из пространства кода.Я только что подумал об отличном решении, которое работает как с исходными файлами, так и с заголовочными файлами, очень эффективно и работает во время компиляции на всех платформах без специфичных для компилятора расширений. Это решение также сохраняет относительную структуру каталогов вашего проекта, чтобы вы знали, в какой папке находится файл, и только относительно корня вашего проекта.
Идея состоит в том, чтобы получить размер исходного каталога с помощью инструмента сборки и просто добавить его в
__FILE__
макрос, полностью удалив каталог и показывая только имя файла, начиная с исходного каталога.Следующий пример реализован с использованием CMake, но нет никаких причин, по которым он не будет работать с любыми другими инструментами сборки, потому что трюк очень прост.
В файле CMakeLists.txt определите макрос, имеющий длину пути к вашему проекту в CMake:
В исходном коде определите
__FILENAME__
макрос, который просто добавляет размер исходного пути к__FILE__
макросу:Тогда просто используйте этот новый макрос вместо
__FILE__
макроса. Это работает, потому что__FILE__
путь всегда будет начинаться с пути к вашему исходному каталогу CMake. Удалив его из__FILE__
строки, препроцессор позаботится о том, чтобы указать правильное имя файла, и все это будет относиться к корню вашего проекта CMake.Если вы заботитесь о производительности, это так же эффективно, как использование
__FILE__
, потому что оба__FILE__
иSOURCE_PATH_SIZE
являются известными константами времени компиляции, поэтому он может быть оптимизирован компилятором.Единственное место, где это может потерпеть неудачу, - это если вы используете это в сгенерированных файлах, а они находятся в папке для сборки вне исходного кода. Тогда вам, вероятно, придется создать другой макрос, используя
CMAKE_BUILD_DIR
переменную вместоCMAKE_SOURCE_DIR
.источник
__FILE__
указатель на массив байтов Таким образом, добавление числового литерала является просто добавлением указателя. Очень умный @RenatoUtsch__FILE__
макрос, и макрос SOURCE_PATH_SIZE являются константами, известными во время компиляции. Я ожидаю, что современные оптимизирующие компиляторы смогут обнаружить, что часть строки не используется, и просто удалить ее из двоичного файла. Во всяком случае, я не думаю, что эти несколько байтов будут иметь существенное значение в размере двоичного файла, поэтому меня это не особо волнует.Чисто скомпилируйте временное решение здесь. Он основан на том факте, что
sizeof()
строковый литерал возвращает свою длину + 1.Не стесняйтесь расширять каскад условных операторов до максимально разумного имени файла в проекте. Длина пути не имеет значения, если вы проверяете достаточно далеко от конца строки.
Я посмотрю, смогу ли я получить подобный макрос без жестко запрограммированной длины с макрорекурсией ...
источник
-O1
чтобы использовать это во время компиляции.sizeof(s) > 2
проверки в первую очередь. Кроме того, это не сработало во время компиляции для меня, в -Os. Строки полного пути присутствовали в выходном двоичном файле.По крайней мере для gcc значение
__FILE__
- это путь к файлу, указанный в командной строке компилятора . Если вы компилируетеfile.c
так:__FILE__
будет расширяться"/full/path/to/file.c"
. Если вы вместо этого сделаете это:затем
__FILE__
расширится до всего"file.c"
.Это может или не может быть практичным.
Стандарт C не требует такого поведения. Все это говорит о
__FILE__
это то, что он расширяется до «Предполагаемого имени текущего исходного файла (символьная строка литерала)».Альтернативой является использование
#line
директивы. Он переопределяет текущий номер строки и, необязательно, имя исходного файла. Если вы хотите переопределить имя файла, но оставить номер строки в покое, используйте__LINE__
макрос.Например, вы можете добавить это в верхней части
file.c
:Единственная проблема с этим состоит в том, что он назначает указанный номер строки следующей строке, и первый аргумент
#line
должен быть последовательностью цифр, чтобы вы не могли сделать что-то вродеПроверка того, что имя файла в
#line
директиве соответствует фактическому имени файла, оставляется в качестве упражнения.По крайней мере для gcc это также повлияет на имя файла, указанное в диагностических сообщениях.
источник
__LINE__
значение на единицу. Так__LINE__
написано в строкеx
жест оценивается вx-1
. По крайней мере, с gcc 5.4.#line __LINE__-1, "file.c"
. Я обновлю свой ответ.Немного опоздал на вечеринку, но для GCC взглянем на
-ffile-prefix-map=old=new
вариант:Так что для моих сборок Jenkins я добавлю
-ffile-prefix-map=${WORKSPACE}/=/
и еще один, чтобы удалить префикс локальной установки пакета dev.П р и м е ч а н и е - К сожалению, эта
-ffile-prefix-map
опция доступна только в GCC 8 и более поздних версиях,-fmacro-prefix-map
что, я думаю , и делает свою__FILE__
роль. Например, GCC 5, у нас есть только то,-fdebug-prefix-map
что не влияет__FILE__
.источник
-ffile-prefix-map
действительно подразумевает как-fdebug-prefix-map
и-fmacro-prefix-map
варианты. См. Также ссылки на reproducible-builds.org/docs/build-path . Ошибка GCC, которая отслеживает-fmacro-prefix-map
и-ffile-prefix-map
является gcc.gnu.org/bugzilla/show_bug.cgi?id=70268msvc2015u3, gcc5.4, clang3.8.0
'
Код генерирует смещение времени компиляции, когда:
gcc
: по крайней мере, gcc6.1 +-O1
msvc
: положить результат в переменную constexpr:clang
: сохраняется при некомпилированной оценке времениЕсть хитрость, заставляющая все 3 компилятора выполнять оценку времени компиляции даже в конфигурации отладки с отключенной оптимизацией:
https://godbolt.org/z/u6s8j3
источник
В VC, при использовании
/FC
,__FILE__
расширяется до полного пути, без/FC
опции__FILE__
расширения имени файла. ссылка: здесьисточник
Там нет времени компиляции способ сделать это. Очевидно, что вы можете сделать это во время выполнения, используя среду выполнения C, как продемонстрировали некоторые другие ответы, но во время компиляции, когда включается препроцессор, вам не повезло.
источник
strrchr
ответ мог правдоподобно быть вычислено во время компиляции, хотя, конечно , до сих пор не препроцессором. Я не знаю, действительно ли это делает gcc, я не проверял, но я уверен, что он вычисляетstrlen
строковые литералы во время компиляции.__FILE__
неявно вызываетсяassert()
, эта утечка может произойти без каких-либо других явных действий.__FILE__
себе базовое имя может также указывать на то, что клиент может предпочесть не делать, поэтому при__FILE__
любом его использовании - будь то полный абсолютный путь или просто базовое имя - возникают те же проблемы, на которые вы указали. В этой ситуации все результаты должны быть тщательно изучены, и для вывода клиентам необходимо ввести специальный API. Остальные выходные данные должны быть выгружены в / dev / NULL или stdout, а stderr должен быть закрыт. :-)Используйте функцию basename () или, если вы работаете в Windows, _splitpath () .
Также попробуйте
man 3 basename
в оболочке.источник
char file_copy[] = __FILE__; const char *filename = basename(__FILE__);
. Причина для копирования заключается в том, что basename может изменить входную строку. Вы также должны следить за тем, чтобы указатель результата был действителен только доbasename
повторного вызова. Это означает, что это не потокобезопасно.basename
на самом деле не изменит входную строку, которая получается из-за того__FILE__
, что у входной строки нет/
конца в конце, и поэтому нет необходимости изменять . Так что вам это может сойти с рук, но я думаю, что в первый раз, когда кто-то увидитbasename
, он должен увидеть это со всеми ограничениями./
в конце строки означает, что у basename может быть веская причина изменить свой аргумент.Если вы используете
CMAKE
компилятор GNU, этоglobal
определение отлично работает:источник
У меня есть использовать один и тот же раствор с @Patrick «s ответа в течение многих лет.
Есть небольшая проблема, когда полный путь содержит символьную ссылку.
Лучшее решение.
Зачем использовать это?
-Wno-builtin-macro-redefined
отключить предупреждения компилятора для переопределения__FILE__
макроса.Уберите путь проекта из пути к файлу - это ваше реальное требование. Вам не понравится тратить время, чтобы узнать, где находится
header.h
файл,src/foo/header.h
илиsrc/bar/header.h
.Мы должны раздеть
__FILE__
макрос вcmake
конфигурационном файле.Этот макрос используется в большинстве существующих кодов. Просто переопределите это может освободить вас.
Компиляторы вроде
gcc
предопределяют этот макрос из аргументов командной строки. И полный путь записывается вmakefile
сгенерированномcmake
.Жесткий код в
CMAKE_*_FLAGS
Требуется .Есть некоторые команды для добавления опций или определений компилятора в более позднюю версию, например,
add_definitions()
иadd_compile_definitions()
. Эти команды будут анализировать функции make, как иsubst
раньше, к исходным файлам. Этого мы не хотим.Надежный путь для
-Wno-builtin-macro-redefined
.источник
__FILE__
своем определении диагностику во встроенной функции, определенной в заголовочном файле, диагностика во время выполнения сообщит имя файла, переданного компилятору, а не имя включаемого файла, тогда как номер строки будет обратитесь к включаемому файлу.#define LOG(fmt, args...) printf("%s " fmt, __FILE__, ##args)
. при использованииLOG()
макроса вы не очень хотите видетьlog.h
в сообщениях. в конце концов,__FILE__
макрос раскрывается в каждом файле C / Cpp (модуле компиляции) вместо включаемых файлов.Небольшое отклонение от предложенного @ red1ynx будет заключаться в создании следующего макроса:
В каждом из ваших .c (pp) файлов добавьте:
Тогда вы можете обратиться к
THIS_FILE_NAME
вместо__FILE__
:Это означает, что построение выполняется один раз для каждого файла .c (pp) вместо каждого обращения к макросу.
Он ограничен использованием только из файлов .c (pp) и будет непригодным для использования из заголовочных файлов.
источник
Я сделал макрос
__FILENAME__
который избегает разрезания полного пути каждый раз. Проблема заключается в том, чтобы хранить полученное имя файла в локальной переменной cpp.Это можно легко сделать, определив статическую глобальную переменную в файле .h . Это определение дает отдельные и независимые переменные в каждом файле .cpp, который включает в себя .h . Для того, чтобы быть многопоточным, стоит сделать переменную (и) также поточно локальной (TLS).
Одна переменная хранит имя файла (сокращенное). Другой содержит необрезанное значение, которое
__FILE__
дал. Файл h:Сам макрос вызывает метод со всей логикой:
И функция реализована так:
RemovePath () может быть реализован по-разному, но быстрый и простой, кажется, с strrchr:
источник
В недавнем компиляторе Clang есть
__FILE_NAME__
макрос (см. Здесь ).источник
просто надеюсь немного улучшить макрос FILE:
#define FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
это ловит / и \, как просил Царек Томчак, и это прекрасно работает в моей смешанной среде.
источник
FILE
- очень плохая идея, если вы включите его<stdio.h>
.Пытаться
после включения операторов в ваш исходный файл и добавить
в конце вашего исходного файла.
источник
push_macro
иpop_macro
нестандартны. (gcc поддерживает их для совместимости с компиляторами Microsoft Windows.) В любом случае нет смысла толкать и вставлять определение__FILE__
; восстановленное значение не будет использовано после окончания исходного файла в любом случае. Уборщик способ изменить значение__FILE__
является#line __LINE__ "foobar.c"
Вот переносимая функция, которая работает как для Linux (путь '/'), так и для Windows (сочетание '\' и '/').
Компилируется с gcc, clang и vs.
Стандартный вывод:
источник
Вот решение, которое использует вычисление времени компиляции:
источник
Если вы оказались на этой странице в поисках способа удалить абсолютный исходный путь, указывающий на некрасивое местоположение сборки из бинарного файла, который вы отправляете, приведенный ниже может удовлетворить ваши потребности.
Хотя это не дает точного ответа, о котором автор выразил свое желание, так как предполагает использование CMake , он довольно близок. Жаль, что никто не упоминал об этом раньше, так как это сэкономило бы мне массу времени.
Установка вышеуказанной переменной в
ON
сгенерирует команду сборки в формате:В результате
__FILE__
макрос разрешится в../../src/path/to/source.c
CMake документация
Остерегайтесь предупреждения на странице документации, хотя:
Не гарантируется работа во всех случаях, но работал в моем - CMake 3.13 + gcc 4.5
источник
Так как вы используете GCC, вы можете воспользоваться в
а затем управляйте тем, как вы хотите отобразить имя файла, изменив представление исходного файла (полный путь / относительный путь / базовое имя) во время компиляции.
источник
__FILE__
и__BASE_FILE__
однако они оба показывают полный путь к файлуgcc /absolute/path/to/file.c
. Если вы найдете способ изменить это поведение (открыв еще один вопрос о SO, смеется?), Вам не нужно изменять строку во время выполнения__BASE_FILE__
(как говорят документы, хотя и неясно) создает имя файла, указанного в командной строке , например,test.c
или в/tmp/test.c
зависимости от того, как вы вызывали компилятор. Это точно так же, как__FILE__
, за исключением случаев, когда вы находитесь внутри файла заголовка, в этом случае__FILE__
выдает имя текущего файла (напримерfoo.h
), тогда как__BASE_FILE__
продолжает создаватьtest.c
.Вот решение, которое работает для сред, в которых нет строковой библиотеки (ядро Linux, встроенные системы и т. Д.):
Теперь просто используйте
FILENAME
вместо__FILENAME__
. Да, это все еще во время выполнения, но это работает.источник
источник
Краткий рабочий ответ для Windows и * nix:
источник
std::max<const char*>
а не простоstd::max
?