×


Custom Formatter Specializations And Flag Formatting Helpers
  • - Found In Files:
    • Common.h - Format_Source_Loc And Format_Thread_ID
    • argfmt_backend.h - ArgFormatter specializations
    • stdfmt_backend.h - <format> specializations
    • fmtlib_backend.h - fmtlib specializations





This page covers the formatting template specializations used in Serenity for the formatter backends as well as the helper classes that aid in formatting the source location info and thread hash identifier.

This includes formatter specializations for:
  • LoggerLevel
  • std::tm
This also includes the helper classes:
  • Format_Source_Loc
  • Format_Thread_ID

NOTE:
  • ArgFormatter natively supports std::tm formatting for the specified use case of sub-second precision as well as the default behavior of strftime flag formatting

  • While fmtlib provides native formatting of std::tm as well, a work-around had to be made for adding sub-second precision support used in Serenity

  • <format> has no built-in support for std::tm by default as it uses std::chrono times instead, so a work-around needed to be made for this case as well.


Click here for a file snippet of the helper classes:

These classes are both found under the namespace serenity::msg_details::custom_flags in file Common.h

   inline static constexpr size_t SOURCE_BUFF_SIZE { 6 };

   // Not All Functions Can Be Made `constexpr` due to the usage of `std::memset()` and `std::to_chars()`
   class Format_Source_Loc
   {
     public:
       explicit Format_Source_Loc(const std::source_location& src, const source_flag& flag)
           : srcLocation(src), buff(std::array<char, SOURCE_BUFF_SIZE> {}), spec(flag), result(std::string {}) { }
       Format_Source_Loc(const Format_Source_Loc&)            = delete;
       Format_Source_Loc& operator=(const Format_Source_Loc&) = delete;
       ~Format_Source_Loc()                                   = default;

       void FormatAll() {
           auto data { buff.data() };
           result.append("[ ").append(std::move(std::filesystem::path { srcLocation.file_name() }.filename().string())).append("(");
           std::memset(data, 0, SOURCE_BUFF_SIZE);
           result.append(data, std::to_chars(data, data + SOURCE_BUFF_SIZE, srcLocation.line()).ptr - data).append(":");
           std::memset(data, 0, SOURCE_BUFF_SIZE);
           result.append(data, std::to_chars(data, data + SOURCE_BUFF_SIZE, srcLocation.column()).ptr - data).append(") ");
           result.append(srcLocation.function_name()).append(" ]");
       }

       void FormatLine() {
           auto data { buff.data() };
           std::memset(data, 0, SOURCE_BUFF_SIZE);
           result.append(data, std::to_chars(data, data + SOURCE_BUFF_SIZE, srcLocation.line()).ptr - data);
       }

       void FormatColumn() {
           auto data { buff.data() };
           std::memset(data, 0, SOURCE_BUFF_SIZE);
           result.append(data, std::to_chars(data, data + SOURCE_BUFF_SIZE, srcLocation.column()).ptr - data);
       }

       void FormatFile() {
           result.append(std::move(std::filesystem::path { srcLocation.file_name() }.filename().string()));
       }

       inline constexpr void FormatFunction() {
           result.append(srcLocation.function_name());
       }

       std::string FormatUserPattern() {
           using enum source_flag;
           result.clear();
           switch( static_cast(spec) ) {
                   case 1: FormatLine(); break;
                   case 2: FormatColumn(); break;
                   case 3:
                       FormatLine();
                       result.append(":");
                       FormatColumn();
                       break;
                   case 4: FormatFile(); break;
                   case 5:
                       FormatFile();
                       result.append(" ");
                       FormatLine();
                       break;
                   case 7:
                       FormatFile();
                       result.append("(");
                       FormatLine();
                       result.append(":");
                       FormatColumn();
                       result.append(")");
                       break;
                   case 8: FormatFunction(); break;
                   case 9:
                       FormatFunction();
                       result.append(" ");
                       FormatLine();
                       break;
                   case 11:
                       FormatFunction();
                       result.append(": (");
                       FormatLine();
                       result.append(":");
                       FormatColumn();
                       result.append(")");
                       break;
                   case 12:
                       FormatFile();
                       result.append(" ");
                       FormatFunction();
                       break;
                   default: FormatAll(); break;
               }
           return result;
       }

     private:
       const std::source_location& srcLocation;
       std::array<char, SOURCE_BUFF_SIZE> buff;
       const source_flag& spec;
       std::string result;
   };

   inline static constexpr size_t defaultThreadIdLength = 10;    // Precision argument dictates the length of the hashed id returned (0-10)
   inline static constexpr size_t THREAD_BUFF_SIZE = 64;
   class Format_Thread_ID
   {
       public:
       explicit Format_Thread_ID(const std::thread::id& id): thread(id) { }
       Format_Thread_ID(const Format_Thread_ID&)            = delete;
       Format_Thread_ID& operator=(const Format_Thread_ID&) = delete;
       ~Format_Thread_ID()                                   = default;

       std::string FormatUserPattern(size_t precision) {
           std::array<char, THREAD_BUFF_SIZE> buf {};
           std::to_chars(buf.data(), buf.data() + THREAD_BUFF_SIZE, std::hash<std::thread::id>()(thread));
           return std::string(buf.data(), buf.data() + ((precision != 0) ? precision : defaultThreadIdLength));
       }

     private:
       const std::thread::id& thread;
   };
            

Format_Source_Loc Public Functions
explicit Format_Source_Loc(const std::source_location& src, const source_flag& flag);
This is the only constructor for this class.
~Format_Source_Loc()=default;
Default Destructor.
inline static constexpr size_t SOURCE_BUFF_SIZE;
This constexpr variable is used to keep consistency with the buffers used in calls to std::to_chars().
void FormatAll();
This function formats the source file name, source line, source column, and source function into the member variable result
void FormatLine();
This function formats the source line into the member variable result
void FormatColumn();
This function formats the source column into the member variable result
void FormatFile();
This function formats the source file name into the member variable result
inline constexpr void FormatFunction();
This function formats the source function into the member variable result
std::string FormatUserPattern()
This function formats the source location info stored from the constructor call based on the source flag taken in from the same call and returns a string containing the formatted result. The default behavior is to call FormatAll(), however, this can be changed by adding modifiers in the user pattern after "%s:"
Format_Thread_ID Public Functions
explicit Format_Thread_ID(const std::thread::id& id);
This is the only constructor for this class.
~Format_Thread_ID() = default;
Default Destructor.
inline static constexpr size_t THREAD_BUFF_SIZE;
This constexpr variable is used to keep consistency with the buffer instantiation for std::to_chars().
inline static constexpr size_t defaultThreadIdLength;
This constexpr variable is used to set the default length of the hashed identifier for the thread.
  • This value is set to 10
  • std::string FormatUserPattern(size_t precision);
    This function formats the thread identifier that was stored from the constructor call by hashing the thread and returning a string representation of the identifier. The length of the identifier is defaultThreadIdLength digits long by default. This can be changed by adding a modifier flag to the user pattern after "%t:"
    serenity::LoggerLevel Formatter Specialization Public Functions
    ArgFormatter Formatter Specializations
    template<> constexpr void Parse(std::string_view parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
    template<> template<class resultCtx> constexpr void Format(const serenity::LoggerLevel& lvl, resultCtx& ctx) const;
    This function will format the result of the parsed substitution block by mapping the LoggerLevel value to it's string representation.
    <format> Formatter Specializations
    template<> constexpr std::format_parse_context::const_iterator parse(std::format_parse_context parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
    template<> std::back_insert_iterator<std::Fmt_buffer<char>> format(const serenity::LoggerLevel& lvl, std::format_context& ctx) const;
    This function will format the result of the parsed substitution block by mapping the LoggerLevel value to it's string representation.
    fmtlib Formatter Specializations
    template<> constexpr const char * parse(fmt::format_parse_context& parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
    template<> inline fmt::v9::appender format(serenity::LoggerLevel& lvl, fmt::format_context& ctx) const;
    This function will format the result of the parsed substitution block by mapping the LoggerLevel value to it's string representation.
    std::tm Formatter Specialization Public Functions
    macro FMT_BUFF_SIZE 128
    This macro is used to set the format buffer size for the time related formatting.
    <format> Formatter Specializations
    template<> constexpr std::format_parse_context::const_iterator parse(std::format_parse_context& parse);
    This function simply parses the substitution blocks that correspond to time related flags. This function specialization will accumulate the flags found for one efficient formatting call.
    template<> std::back_insert_iterator<std::Fmt_buffer<char>> format(const std::tm& tmStruct, std::format_context& ctx) const;
    This function specialization will format the accumulated time flags found in the parse portion and format them into the underlying container. If sub-second precision was found in parsing, this function makes three formatting calls; one for flags up to the sub-second portion, the actual sub-second portion, and then anything that was let remaining after the sub-second portion.
    fmtlib Formatter Specializations
    template<> constexpr const char * parse(fmt::format_parse_context& parse);
    This function simply parses the substitution blocks that correspond to time related flags. This function specialization will accumulate the flags found for one efficient formatting call.
    template<> inline fmt::v9::appender format(const std::tm& tmStruct, fmt::format_context& ctx) const;
    This function specialization will format the accumulated time flags found in the parse portion and format them into the underlying container. If sub-second precision was found in parsing, this function makes three formatting calls; one for flags up to the sub-second portion, the actual sub-second portion, and then anything that was let remaining after the sub-second portion.



    More Information About Class FileHelper


    explicit Format_Source_Loc(const std::source_location& src, const source_flag& flag);
    This is the only constructor for this class.
  • Parameter src holds the source location collected at the log call site via the implicit capture using the MsgWithLoc struct
  • Parameter flag holds the formatting modifier flags which controls how the src parameter is formatted. This enum is set when internally storing the user pattern when the function SetPattern() is called.



  • ~Format_Source_Loc();
    Default Destructor.



    inline static constexpr size_t SOURCE_BUFF_SIZE;
    This constexpr variable is used to keep consistency with the buffers used in calls to std::to_chars() and is set to "6", which should be adequate for even the largest of files.



    void FormatAll();
    This function formats the source file name, source line, source column, and source function into the member variable result.
    • This function uses std::source_location's:
      • file_name() function
      • line() function
      • column() function
      • function_name() function
    • For the line() and column() functions:
      • std::to_chars() is used to convert these values into their string counterparts
    • The output of the formatted source location will be:
      • [ File_Name (Line:Column) Function_Name ]


    void FormatLine();
    This function formats the source line into the member variable result.
    • This function uses std::source_location's line() function
    • std::to_chars() is used to convert the line() result into its string counterpart.


    void FormatColumn();
    This function formats the source column into the member variable result.
    • This function uses std::source_location's column() function
    • std::to_chars() is used to convert the column() result into its string counterpart.


    void FormatFile();
    This function formats the source file name into the member variable result.
    • This function uses std::source_location's file_name() function.


    inline constexpr void FormatFunction();
    This function formats the source function name into the member variable result.
    • This function uses std::source_location's function_name() function.


    std::string Format_Source_Loc::FormatUserPattern();
    This function formats the source location info stored from the constructor call based on the source flag taken in from the same call and returns a string containing the formatted result. The default behavior is to call FormatAll(), however, this can be changed by adding modifiers in the user pattern after "%s:"
    • This function will do the following based on the flags set:
      • Format source file, line, column, and function
      • Format source file, line, and column
      • Format source function, line, and column
      • Format source file and function
      • Format source file and line
      • Format source function and line
      • Format source line and column
      • Format source line
      • Format source column
      • Format source function
      • Format source file
    • The flag is set based on the following found in the user pattern provided on target creation:
      • The base flag "%s"
      • Followed by any combination of the below modifiers (requires "%s:" before any modifiers):
        • "a" - calls FormatAll();
        • "f" - calls FormatFile();
        • "l" - calls FormatLine();
        • "c" - calls FormatColumn();
        • "F" - calls FormatFunction();
      • If no modifiers are present, then this is the same as calling "%s:a"


    inline static constexpr size_t defaultThreadIdLength = 10
    This value is used internally to limit the default length of the hashed ID used when formatting the thread ID.



    inline static constexpr size_t THREAD_BUFF_SIZE = 64
    This value is used internally to ensure the hashed thread ID can be adequately stored before being returned the calling function.



    Format_Thread_ID(const std::thread::id& id): thread(id) { }
    The default constructor used when formatting the thread ID. This constructor takes in a std::thread::id object and stores it in order to format it later on when Format_Thread_ID::FormatUserPattern() is called.



    ~Format_Thread_ID()
    This class uses a default destructor.



    std::string Format_Thread_ID::FormatUserPattern(size_t precision)
    This function takes in a thread precision spec that is found in the user defined pattern of the thread ID flag. If no modifier is present, the thread ID length defaults to the "defaultThreadIdLength" value of '10'. This function will hash the thread ID into a function local buffer before returning its string representation to the calling function.



    template<> constexpr void Parse(std::string_view parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
  • Parameter parse is the substitution context being parsed for the serenity::LoggerLevel specific flags.
    • This function will parse the user defined pattern portion that deals with the serenity::LoggerLevel flag
    • When this function encounters "L"
      • This function will set the method of formatting to be the long level representation
    • When this function encounters "l"
      • This function will set the method of formatting to be the short level representation
    • Otherwise, this function will issue a warning on the following:
      • Missing closing bracket
      • Modifier token with no flag present - meaning a "%" was found without a corresponding flag
    • If no modifying flags are present, then the state will be set to LevelView::Invalid
      • This results in the string "INVALID_LEVEL" being formatted into your container


    template<typename resultCtx> constexpr auto Format(const serenity::LoggerLevel& lvl, resultCtx& ctx) const
    This function is in charge of the actual formatting that occurs for the logging level when logging a message if using the custom native formatting backend, "ArgFormatter".
    • Parameters:
      • lvl - While the loggers use LoggerLevel to determine whether or not a certain message is actually logged, parameter "lvl" is the message level in this case and not the logger's level threshold.
      • ctx - Parameter "ctx" is the container that the formatted output string will be written to.
    This function will do the following:
    • For the short form representation of the logger levels, this function formats the following directly into the container:
        LoggerLevel Output
        Info I
        Trace T
        Debug D
        Warning W
        Error E
        Fatal F


    • For the long form representation of the logger levels, this function formats the following directly into the container:
        LoggerLevel Output
        Info Info
        Trace Trace
        Debug Debug
        Warning Warn
        Error Error
        Fatal Fatal
    NOTE: In the case that the logger level provided is not a known or valid level, then the formatted output of this function will be the string "INVALID_LEVEL"



    template<> constexpr std::format_parse_context::const_iterator parse(std::format_parse_context& parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
  • Parameter parse is the substitution context being parsed for the serenity::LoggerLevel specific flags.
    • This function will parse the user defined pattern portion that deals with the serenity::LoggerLevel flag
    • When this function encounters "L"
      • This function will set the method of formatting to be the long level representation
    • When this function encounters "l"
      • This function will set the method of formatting to be the short level representation
    • Otherwise, this function will issue a warning on the following:
      • Missing closing bracket
      • Modifier token with no flag present - meaning a "%" was found without a corresponding flag
    • If no modifying flags are present, then the state will be set to LevelView::Invalid
      • This results in the string "INVALID_LEVEL" being formatted into your container


    template <> inline fmt::v9::appender fmt::v9::formatter<serenity::LoggerLevel>::format(serenity::LoggerLevel& lvl, fmt::format_context& ctx) const
    This function is in charge of the actual formatting that occurs for the logging level when logging a message if using the "fmtlib" backend option.
    • Parameters:
      • lvl - While the loggers use LoggerLevel to determine whether or not a certain message is actually logged, parameter "lvl" is the message level in this case and not the logger's level threshold.
      • ctx - Parameter "ctx" is the container that the formatted output string will be written to.
    This function will do the following:
    • For the short form representation of the logger levels, this function formats the following directly into the container:
        LoggerLevel Output
        Info I
        Trace T
        Debug D
        Warning W
        Error E
        Fatal F


    • For the long form representation of the logger levels, this function formats the following directly into the container:
        LoggerLevel Output
        Info Info
        Trace Trace
        Debug Debug
        Warning Warn
        Error Error
        Fatal Fatal
    NOTE: In the case that the logger level provided is not a known or valid level, then the formatted output of this function will be the string "INVALID_LEVEL"



    template<> constexpr const char * parse(fmt::format_parse_context& parse);
    This function simply parses the portion of the user pattern that deals with the message log level representation.
  • Parameter parse is the substitution context being parsed for the serenity::LoggerLevel specific flags.
    • This function will parse the user defined pattern portion that deals with the serenity::LoggerLevel flag
    • When this function encounters "L"
      • This function will set the method of formatting to be the long level representation
    • When this function encounters "l"
      • This function will set the method of formatting to be the short level representation
    • Otherwise, this function will issue a warning on the following:
      • Missing closing bracket
      • Modifier token with no flag present - meaning a "%" was found without a corresponding flag
    • If no modifying flags are present, then the state will be set to LevelView::Invalid
      • This results in the string "INVALID_LEVEL" being formatted into your container


    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]



    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]



    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]



    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]



    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]



    [PLACE_HOLDER_TEXT]
    [PLACE_HOLDER_TEXT]