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 colocarLOG_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__
comotest.c
-
gcc c:/tmp/test.c -otest.exe
me da__FILE__
comoc:/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