Alternative zu __FILE__?



  • Hallo,

    ich nutze für Loggings und Exceptions die Präprozessordefinition --FILE-- (sollen Unterstriche sein). Für das Debugging und eine Fehlersuche ganz gut zu gebrauchen. Einen Aspekt finde ich allerdings etwas unschön. Es wird immer der komplette Dateipfad eingesetzt, also ist beispielsweise auch der Laufwerksbuchstabe enthalten.

    Wenn ich nun Unit- und/oder Integrationstest laufen lasse und ein Kollege das Projekt in einem anderen Pfad ausgecheckt hat, falle ich auf die Nase.

    Daher meine Frage, ob es von Hause aus (c++ Standard, Visual Studio, etc.) ein etwas eleganteres Äquivalent gibt? Ich möchte mir ehrlich gesagt die Arbeit sparen, eine Art Präprozessor zusätzlich zu schreiben.

    Vielen Dank im Voraus

    lg Torsten



    1. Warum fällst du auf die Nase? (bzw. was meinst du damit?)
    2. Was möchtest du denn anzeigen? Was ist, wenn du gleichnamige Dateien in unterschiedlichen Ordnern hast? Schau mal auf SO für ein paar Vorschläge: https://stackoverflow.com/questions/8487986/file-macro-shows-full-path




  • Hallo,

    @wob: Ich möchte beispielsweise Nachrichten und StackTrace-Einträge von Exceptions auswerten, wenn sich eben der Laufwerksbuchstabe in einem StackTrace ändert schlagen natürlich die Tests fehl.

    @manni66: Vielen Dank, das könnte sein, was ich suche. Ich schaue es mir gleich mal genauer an.

    Edit: Hm... C++20 😉 Schade, schön wäre es gewesen. Ich bin leider gezwungen, der 11er zu nutzen 😞

    lg Torsten



  • Kannst es ja mal mit Boost.Assert probieren: Source Location Support, <boost/assert/source_location.hpp>

    It’s similar to std::source_location from C++20, but only requires C++03.

    Edit: Andererseits basiert es auch auf __FILE__ und __LINE__:

    #define BOOST_CURRENT_LOCATION \
      ::boost::source_location(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
    

    Kannst du denn die Dateinamen (in deinen Tests) nicht so vergleichen, daß es die Laufwerksangaben ignoriert?



  • Hm... ja, ginge sicherlich. Aber das macht die Tests enorm aufwendig, das soll nicht das Ziel sein. Und abgesehen davon ist das Problem, dass ein Kollege auf seinem Rechner in einem anderen Verzeichnis auschecken kann, damit auch nicht gebannt.

    Ich denke ich nehme die Lösung, die @wob im Beispielthread vorgeschlagen hat:

    template <typename Argument, size_t size>
    inline constexpr size_t get_file_name_offset(const Argument(&string)[size], size_t index = size - 1)
    {
    	return (str[i] == '/' || str[i] == '\\') ? i + 1 : (i > 0 ? get_file_name_offset(str, i - 1) : 0);
    }
    
    template <typename Argument>
    inline constexpr size_t get_file_name_offset(Argument(&str)[1])
    {
    	return 0;
    }
    
    signed main(const signed nArguments, const char** const arguments)
    {
    	std::cout << &__FILE__[get_file_name_offset(__FILE__)] << std::endl;
    
    	return 0;
    }
    

    Scheint erstmal die einfachste Lösung zu sein.

    Danke für eure Hilfe und Vorschläge.

    lg Torsten


  • Mod

    @Th69 sagte in Alternative zu __FILE__?:

    Kannst du denn die Dateinamen (in deinen Tests) nicht so vergleichen, daß es die Laufwerksangaben ignoriert?

    Ich fänd's ungünstig, wenn meine Programme Information über ihre Build-Umgebung leaken.

    Die kanonische Lösung wäre so etwas wie hinter wob's 2. Vorschlag. strchr sollte auch im Exceptionfall ungefährlich sein. Ein bisschen unportabler aber wahrscheinlich etwas sicherer bezüglich benötigter Header wäre wohl so etwas wie __builtin_strrchr (für GCC, für andere eben anders). Wobei man dann aber auch gleich die oft eingebauten nicht-Standardmakros des Compilers nutzen kann.
    PS: Die constexpr-Lösung ist schöner 👍



  • Wenn Du in Visual Studio unterwegs bist, dann schau mal hier.

    Und beachte den Hinweis bzgl. /ZI.



  • @TorDev
    Wenn du __FILE__ als eigenen Parameter an die Logging-Funktion übergibst, kannst du den Variablen Teil wegschneiden bevor du die Log-Message schreibst.
    Dazu brauchst du bloss eine Hilfsfunktion die den relativen Pfad "ihres" .cpp Files kennt.

    In dieser Hilfsfunktion kannst du dir __FILE__ holen und mit dem bekannten relativen Pfad vergleichen. Das könnte z.B. so aussehen:

    #include <algorithm>
    #include <string>
    #include <assert.h>
    #include <stddef.h>
    
    std::string normalizeSourceFilePath(std::string str) {
        constexpr size_t myPathLen = sizeof(__FILE__) - 1;
        constexpr size_t myRelativePathLen = sizeof("src/diagnostics/utility.cpp") - 1;
        static_assert(myPathLen > myRelativePathLen);
        constexpr size_t prefixLen = myPathLen - myRelativePathLen;
        if (str.size() > prefixLen) {
            str.erase(str.begin(), str.begin() + prefixLen);
        } else {
            assert(0 && "unexpected source path");
        }
    #ifdef _WIN32
        std::replace(str.begin(), str.end(), '\\', '/');
    #endif
        return str;
    }
    

    Funktioniert natürlich nur wenn __FILE__ im gesamten Projekt wirklich immer mit dem selben Prefix anfängt.

    Das ganze liesse sich natürlich auch mit der Lösung von @wob kombinieren.

    #include <stddef.h>
    
    static inline constexpr size_t sourcePathPrefixLength() {
        constexpr size_t myPathLen = sizeof(__FILE__) - 1;
        constexpr size_t myRelativePathLen = sizeof("src/diagnostics/utility.h") - 1;
        static_assert(myPathLen > myRelativePathLen);
        return myPathLen - myRelativePathLen;
    }
    
    #define RELATIVE_FILE (&__FILE__[sourcePathPrefixLength()])
    

    Wobei du dann wieder das Problem mit den \\ vs. / hast.

    Dafür hast du den Vorteil dass du /checkout/src/foo/utility.cpp und /checkout/src/bar/utility.cpp unterscheiden kannst.

    BTW: Im Binary sind in allen gezeigten Fällen immer noch die vollen, absoluten Pfade enthalten. Bloss ausgegeben wird halt nur mehr der interessante Teil.


Log in to reply