Использование обоих 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:
#include <Joe/Logger.h>
и в последующих звонках, например, LOG_DEBUG(...)
Я неявно использую автоматически сгенерированный «универсальный идентификатор региона».JOE_LOG_FILE_REGION(Hello);
(после #include <Joe/Logger.h>
), прежде чем он мог разместить LOG_DEBUG(...)
в своем коде.#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
пример:
#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
Если вы запустите 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
без нулевого терминатора.
Однако после этой строки все еще будут конечные нули, потому что этот хак злоупотребляет тем фактом, что другой член объединения был инициализирован в соответствии с правилами инициализации «агрегат / объединение». Это правило принудительно инициализирует все оставшиеся элементы в «агрегате», как если бы они имели статическую продолжительность хранения, т. Е. Обнулились. Что является значением нулевого терминатора.
извлечь базовое имя файла во время компиляции без трюков препроцессора и без внешних скриптов? 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;
}
Оказывается, все очень просто, вам просто нужно #line
директива препроцессора, пример
#line 0 "Hello"
в верхней части файла, это как есть, если все, что вам нужно, это полностью скрыть имя файла, то
#line 0 ""
должно сработать.
Если вы не хотите использовать Makefile
s, вы можете использовать это
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"
и вы добьетесь желаемого, хотя вам нужно скомпилировать файл в том же каталоге, где он находится, потому что в противном случае он будет содержать путь к файлу.