Utiliser à la fois gcc avec -std = c11 et g ++ avec -std = c ++ 14.
Par exemple. pour un fichier nommé src/dir/Hello.cxx
, il devrait s'étendre à quelque chose comme par exemple:
const char basename[] = "Hello";
ou
const char basename[] = getStaticBasename(__FILE__);
comme où getStaticBasename()
est une macro (pour les sources C) ou une fonction constexpr (pour les sources C++) qui se traduit par "Bonjour".
Je dois éviter de diviser la chaîne de __FILE__
Au moment de l'exécution, car le chemin et le suffixe ne doivent en aucun cas être compilés dans l'exécutable.
La solution doit être sans dépendances à d'énormes bibliothèques telles que boost.
Comme je n'ai pas de makefile, des solutions comme this ne peuvent pas être utilisées dans mon cas.
Avait-on une solution à cela?
Modifier 2015-07-02:
#include <Joe/Logger.h>
Et dans les appels ultérieurs à par exemple LOG_DEBUG(...)
Je vais implicitement utiliser "l'identifiant générique de région" généré automatiquement.JOE_LOG_FILE_REGION(Hello);
(après #include <Joe/Logger.h>
) Avant de pouvoir placer LOG_DEBUG(...)
dans son code.#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
ou
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
exemple:
#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;
}
le nom du fichier source est foo/foo1/foo2/foo3/foo4.cpp
utilisation g++ -o foo.exe foo/foo1/foo2/foo3/foo4.cpp -std=c++11 --save-temps
pour compiler ce fichier.
vous pouvez le voir.
.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
movl $.LC0+19, %edi
.LC0 + 19 est l'adresse de la chaîne de nom de fichier sans chemin ni suffixe
Si vous exécutez gcc à partir du dossier où se trouve le fichier source, vous obtiendrez un __FILE__
Différent que si vous passez un chemin absolu (c'est-à-dire remis à gcc via un IDE).
gcc test.c -otest.exe
Me donne __FILE__
En tant que test.c
.gcc c:\tmp\test.c -otest.exe
Me donne __FILE__
En tant que c:\tmp\test.c
.Peut-être qu'appeler gcc à partir du chemin où se trouve la source est suffisant comme solution de contournement?
MODIFIER
Voici un hack "sale" mais sûr qui supprime l'extension du fichier au moment de la compilation. Pas vraiment quelque chose que je recommanderais, mais c'était amusant d'écrire :) Alors, prenez-le pour ce qu'il vaut. Cela ne fonctionne qu'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;
}
L'union alloue un membre qui est assez grand pour contenir l'extension du chemin complet moins et le terminateur nul. Et il alloue un membre qui est assez grand pour contenir l'extension complète du chemin moins, mais avec un terminateur nul.
Le membre qui est trop petit pour contenir le __FILE__
Est initialisé avec autant de __FILE__
Que possible. C'est ok en C mais pas autorisé en C++. Si __FILE__
Contient test.c
, Le membre de l'union sera maintenant initialisé pour contenir test
sans terminateur nul.
Il y aura cependant toujours des zéros à la fin après cette chaîne, car ce hack abuse du fait que l'autre membre du syndicat a été initialisé selon les règles d'initialisation "agrégat/union". Cette règle force tous les éléments restants de l '"agrégat" à être initialisés comme s'ils avaient une durée de stockage statique, c'est-à-dire à zéro. Ce qui se trouve être la valeur du terminateur nul.
extraire le nom de fichier de base au moment de la compilation sans astuces de préprocesseur et sans scripts externes? c ++ 14? pas de problème Monsieur.
#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;
}
Cela se révèle très simple, il vous suffit de #line
directive préprocesseur , exemple
#line 0 "Hello"
en haut du fichier, tel quel, si tout ce que vous voulez est de masquer complètement le nom du fichier,
#line 0 ""
travaillerait.
Si vous ne voulez pas utiliser Makefile
s, vous pouvez utiliser ceci
file=cfile;
content=$(sed -e "1s/^/#line 0 \"$file\"\n/" example/${file}.c);
echo $content | gcc -xc -O3 -o ${file} -
Le -xc
Le drapeau gcc ci-dessus signifie (d'après la documentation de gcc):
-x
Langue:Spécifiez explicitement la langue des fichiers d'entrée suivants (plutôt que de laisser le compilateur choisir une valeur par défaut en fonction du suffixe du nom de fichier). Cette option s'applique à tous les fichiers d'entrée suivants jusqu'à l'option -x suivante. Les valeurs possibles pour la langue sont:
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 vous n'avez pas de script qui vous aide à construire la source, il n'y a aucun moyen de le faire, je pense.
En outre, vous pouvez voir dans la citation ci-dessus de la documentation gcc, que vous pouvez enregistrer les fichiers sans aucune extension, puis combiner @ Lundin original solution avec ceci et utiliser
gcc -xc -o file filename_without_extension
dans ce cas __FILE__
s'étendrait à "filename_without_extension"
, et vous obtiendrez ce que vous voulez, bien que vous deviez compiler le fichier dans le même répertoire où il se trouve, car sinon il contiendra le chemin d'accès au fichier.