Как извлечь исходное имя файла без пути и суффикса во время компиляции?

Использование обоих gcc с -std = c11 и g ++ с -std = c ++ 14.

Например. для файла с именем src/dir/Hello.cxx он должен расширяться до чего-то вроде, например:

const char basename[] = "Hello";

или же

const char basename[] = getStaticBasename(__FILE__);

как где getStaticBasename() это макрос (для источников C) или функция constexpr (для источников C ++), которая приводит к «Hello».

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

Решение должно быть без зависимостей от огромных библиотек, таких как boost.

Поскольку у меня нет make-файлов, такие решения, как этот не может быть использовано в моем случае.

У кого-нибудь было решение для этого?

Изменить 2015-07-02:

  • Я не имею никакого влияния на то, как вызывался компилятор и компоновщик (иногда с помощью make-файла, иногда из командной строки или некоторой IDE (Eclipse CDT, управляемый make, Crossworks, Xcode и так далее. Поэтому решение должно быть только в коде).
  • Мой вариант использования заключается в предоставлении своего рода «универсального идентификатора региона» для небольшого решения для регистрации. Код приложения (который использует мой регистратор) должен только #include <Joe/Logger.h> и в последующих звонках, например, LOG_DEBUG(...) Я неявно использую автоматически сгенерированный «универсальный идентификатор региона».
  • Мое текущее решение состоит в том, что код приложения должен объявить JOE_LOG_FILE_REGION(Hello); (после #include <Joe/Logger.h>), прежде чем он мог разместить LOG_DEBUG(...) в своем коде.

18

Решение

  • Встроенная функция gcc может получить имя файла полного пути во время компиляции.

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

  • C ++ 11 constexpr также может сделать это во время компиляции.

пример:

#include <stdio.h>

constexpr const char* str_end(const char *str) {
return *str ? str_end(str + 1) : str;
}

constexpr bool str_slant(const char *str) {
return *str == '/' ? true : (*str ? str_slant(str + 1) : false);
}

constexpr const char* r_slant(const char* str) {
return *str == '/' ? (str + 1) : r_slant(str - 1);
}
constexpr const char* file_name(const char* str) {
return str_slant(str) ? r_slant(str_end(str)) : str;
}

int main() {
constexpr const char *const_file = file_name(__FILE__);
puts(const_file);
return 0;
}

имя исходного файла foo/foo1/foo2/foo3/foo4.cpp

использование g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps скомпилировать этот файл.

Вы можете видеть это.

        .file   "foo4.cpp".section        .rodata
.LC0:
.string "foo/foo1/foo2/foo3/foo4.cpp".text
.globl  main
.type   main, @function
main:
.LFB4:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $16, %rsp
movq    $.LC0+19, -8(%rbp)
movl    $.LC0+19, %edi
call    puts
movl    $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE4:
.size   main, .-main
.ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4".section        .note.GNU-stack,"",@progbits
14

Другие решения

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

  • gcc test.c -otest.exe дает мне __FILE__ как test.c,
  • gcc c:\tmp\test.c -otest.exe дает мне __FILE__ как c:\tmp\test.c,

Возможно, вызова gcc с пути, где расположен источник, достаточно в качестве обходного пути?


РЕДАКТИРОВАТЬ

Вот «грязный», но безопасный хак, который удаляет расширение файла во время компиляции. Не совсем то, что я бы порекомендовал, но было весело писать 🙂 Так что принимайте это за то, что оно того стоит. Работает только на C.

#include <stdio.h>

#define EXT_LENGTH (sizeof(".c") - 1) // -1 null term

typedef union
{
char filename_no_nul [sizeof(__FILE__)-EXT_LENGTH-1]; // -1 null term
char filename_nul    [sizeof(__FILE__)-EXT_LENGTH];
} remove_ext_t;

int main (void)
{
const remove_ext_t file = { __FILE__ };

puts(file.filename_nul);

return 0;
}

Объединение выделяет один член, который является достаточно большим, чтобы содержать полный путь минус расширение и нулевой терминатор. И он выделяет один член, который достаточно большой, чтобы содержать полный путь минус расширение, хотя с нулевым терминатором.

Член, который слишком мал, чтобы держать полный __FILE__ инициализируется как можно больше __FILE__ как может соответствовать. Это нормально в C, но не разрешено в C ++. Если __FILE__ содержит test.c, член профсоюза теперь будет инициализирован, чтобы содержать test без нулевого терминатора.

Однако после этой строки все еще будут конечные нули, потому что этот хак злоупотребляет тем фактом, что другой член объединения был инициализирован в соответствии с правилами инициализации «агрегат / объединение». Это правило принудительно инициализирует все оставшиеся элементы в «агрегате», как если бы они имели статическую продолжительность хранения, т. Е. Обнулились. Что является значением нулевого терминатора.

7

извлечь базовое имя файла во время компиляции без трюков препроцессора и без внешних скриптов? C ++ 14? нет проблем, сэр.

#include <iostream>
#include <string>

using namespace std;

namespace detail {
constexpr bool is_path_sep(char c) {
return c == '/' || c == '\\';
}

constexpr const char* strip_path(const char* path)
{
auto lastname = path;
for (auto p = path ; *p ; ++p) {
if (is_path_sep(*p) && *(p+1)) lastname = p+1;
}
return lastname;
}

struct basename_impl
{
constexpr basename_impl(const char* begin, const char* end)
: _begin(begin), _end(end)
{}

void write(std::ostream& os) const {
os.write(_begin, _end - _begin);
}

std::string as_string() const {
return std::string(_begin, _end);
}

const char* const _begin;
const char* const _end;
};

inline std::ostream& operator<<(std::ostream& os, const basename_impl& bi) {
bi.write(os);
return os;
}

inline std::string to_string(const basename_impl& bi) {
return bi.as_string();
}

constexpr const char* last_dot_of(const char* p) {
const char* last_dot = nullptr;
for ( ; *p ; ++p) {
if (*p == '.')
last_dot = p;
}
return last_dot ? last_dot : p;
}
}

// the filename with extension but no path
constexpr auto filename = detail::strip_path(__FILE__);
constexpr auto basename = detail::basename_impl(filename, detail::last_dot_of(filename));

auto main() -> int
{
cout << filename << endl;
cout << basename << endl;

cout << to_string(basename) << endl;

return 0;
}
5

Оказывается, все очень просто, вам просто нужно #line директива препроцессора, пример

#line 0 "Hello"

в верхней части файла, это как есть, если все, что вам нужно, это полностью скрыть имя файла, то

#line 0 ""

должно сработать.

Если вы не хотите использовать Makefiles, вы можете использовать это

file=cfile;
content=$(sed -e "1s/^/#line 0 \"$file\"\n/" example/${file}.c);
echo $content | gcc -xc -O3 -o ${file} -

-xc Указанный выше флаг gcc означает (из документации gcc):

-x язык:

Укажите явно язык для следующих входных файлов (вместо того, чтобы позволить компилятору выбирать значение по умолчанию на основе суффикса имени файла). Эта опция применяется ко всем следующим входным файлам до следующей опции -x. Возможные значения для языка:

          c  c-header  cpp-output
c++  c++-header  c++-cpp-output
objective-c  objective-c-header  objective-c-cpp-output
objective-c++ objective-c++-header objective-c++-cpp-output
assembler  assembler-with-cpp
ada
f77  f77-cpp-input f95  f95-cpp-input
go
java

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

Кроме того, вы можете видеть из приведенной цитаты документации gcc, что вы можете сохранять файлы без каких-либо расширений, а затем комбинировать @Лундин«s оригинал Решение с этим и использовать

gcc -xc -o file filename_without_extension

в этом случае __FILE__ будет расширяться до "filename_without_extension"и вы добьетесь желаемого, хотя вам нужно скомпилировать файл в том же каталоге, где он находится, потому что в противном случае он будет содержать путь к файлу.

3
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector