compiler c++ c c++11 c++14 c11

c++ - compiler - ¿Cómo extraer el nombre de archivo fuente sin ruta y sufijo en tiempo de compilación?



c++11 compiler download (4)

Usando tanto gcc con -std = c11 y g ++ con -std = c ++ 14.

Por ejemplo, para un archivo llamado src/dir/Hello.cxx debería expandirse a algo como por ejemplo:

const char basename[] = "Hello";

o

const char basename[] = getStaticBasename(__FILE__);

como donde getStaticBasename() es una macro (para fuentes C) o una función constexpr (para fuentes C ++) que da como resultado "Hola".

Tengo que evitar dividir la cadena de __FILE__ en tiempo de ejecución, porque la ruta y el sufijo no deben compilarse en el ejecutable de ninguna manera.

La solución debe ser sin dependencias para grandes bibliotecas como boost.

Como no tengo archivos make, no se pueden usar soluciones como this en mi caso.

¿Tenía uno una solución para eso?

Edición 2015-07-02:

  • No tengo ninguna influencia sobre cómo se invocó el compilador y el vinculador (a veces a través de makefile, a veces desde la línea de comandos o algún IDE (marca administrada por Eclipse CDT, Crossworks, Xcode, etc.) Por lo tanto, la solución solo debe estar en el código.
  • Mi caso de uso es proporcionar algún tipo de "identificador de región genérico" para una solución de registro de espacio reducido. El código de la aplicación (que utiliza mi registrador) solo debe #include <Joe/Logger.h> y, dentro de las llamadas posteriores a, por ejemplo, LOG_DEBUG(...) , utilizaré implícitamente el "identificador genérico de región" generado automáticamente.
  • Mi solución actual es que el código de la aplicación tenga que declarar un JOE_LOG_FILE_REGION(Hello); (después de #include <Joe/Logger.h> ) antes de que pueda colocar LOG_DEBUG(...) en su código.

¿extrae el nombre de archivo base en tiempo de compilación sin trucos de preprocesador ni scripts externos? c ++ 14? no hay problema señor.

#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; }


Resulta ser muy simple, solo necesita la directiva #line preprocesador , ejemplo

#line 0 "Hello"

en la parte superior del archivo, esto es, si todo lo que desea es ocultar el nombre del archivo completamente, entonces

#line 0 ""

trabajaría.

Si no desea utilizar Makefile s, puede usar este

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

El -xc gcc -xc anterior significa (de la documentación de gcc):

-x idioma:

Especifique explícitamente el idioma para los siguientes archivos de entrada (en lugar de dejar que el compilador elija un valor predeterminado basado en el sufijo del nombre del archivo). Esta opción se aplica a todos los archivos de entrada siguientes hasta la siguiente opción -x. Los valores posibles para el lenguaje son:

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

Si no tienes ningún tipo de script que te ayude a construir la fuente, creo que no hay forma de hacerlo.

Además, puede ver en la cita anterior de la documentación de gcc, que puede guardar los archivos sin ninguna extensión, y luego combinar la solución original de @Lundin con esto y usar

gcc -xc -o file filename_without_extension

en este caso, __FILE__ se expandirá a "filename_without_extension" , y logrará lo que desea, aunque necesita compilar el archivo en el mismo directorio en el que se encuentra, porque de lo contrario contendrá la ruta al archivo.


Si ejecuta gcc desde la carpeta donde se encuentra el archivo de origen, obtendrá un __FILE__ diferente que si pasa una ruta absoluta (es decir, entregado a gcc a través de un IDE).

  • gcc test.c -otest.exe me da __FILE__ como test.c
  • gcc c:/tmp/test.c -otest.exe me da __FILE__ como c:/tmp/test.c

¿Tal vez llamar gcc desde la ruta donde se encuentra la fuente es suficiente como una solución alternativa?

EDITAR

Aquí hay un truco "sucio" pero seguro que elimina la extensión del archivo en tiempo de compilación. Realmente no es algo que recomiendo, pero fue divertido escribir :) Así que tómalo por lo que vale. Solo funciona en 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; }

La unión asigna un miembro que es lo suficientemente grande como para contener la ruta completa menos la extensión y el terminador nulo. Y asigna un miembro que es lo suficientemente grande como para contener la ruta completa menos la extensión, aunque con un terminador nulo.

El miembro que es demasiado pequeño para contener el __FILE__ completo se inicializa con tanto __FILE__ como pueda caber. Esto está bien en C pero no está permitido en C ++. Si __FILE__ contiene test.c , el miembro de la unión ahora se inicializará para contener la test sin terminador nulo.

Sin embargo, seguirá habiendo ceros finales después de esa cadena, porque este truco abusa del hecho de que el otro miembro de la unión se haya inicializado de acuerdo con las reglas de inicialización de "agregado / unión". Esta regla obliga a que los elementos restantes en el "agregado" se inicialicen como si tuvieran una duración de almacenamiento estático, es decir, a cero. Que pasa a ser el valor del terminador nulo.


  • La función gcc builtin puede obtener el nombre de archivo de una ruta completa en tiempo de compilación.

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

  • c ++ 11 constexpr también puede hacer esto en tiempo de compilación.

ejemplo:

#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; }

el nombre del archivo fuente es foo/foo1/foo2/foo3/foo4.cpp

use g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps para compilar este archivo.

puedes ver esto

.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