Skip to content

Commit

Permalink
Move definitions of FormatValue and CachedFormatter to format-inl.h
Browse files Browse the repository at this point in the history
Signed-off-by: Pavel Artsishevsky <polter.rnd@gmail.com>
  • Loading branch information
polter-rnd committed Nov 30, 2024
1 parent 0a70b9c commit 400281b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 109 deletions.
103 changes: 103 additions & 0 deletions include/slimlog/format-inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @file format-inl.h
* @brief Contains definitions of FormatValue and CachedFormatter classes.
*/

#pragma once

// IWYU pragma: private, include <slimlog/format.h>

#ifndef SLIMLOG_HEADER_ONLY
#include <slimlog/format.h>
#endif

#ifdef SLIMLOG_FMTLIB
#if __has_include(<fmt/base.h>)
#include <fmt/base.h>
#else
#include <fmt/core.h>
#endif

#include <iterator>
#include <type_traits>
#else
#include <array>
#endif

#include <chrono>
#include <string_view>
#include <utility>

namespace SlimLog {

#ifndef SLIMLOG_FMTLIB
template<typename T, Formattable<T> Char>
FormatValue<T, Char>::FormatValue(const CachedFormatter<T, Char>& formatter, T value)
: m_formatter(formatter)
, m_value(std::move(value))
{
}

template<typename T, Formattable<T> Char>
template<typename Context>
auto FormatValue<T, Char>::format(Context& context) const
{
return m_formatter.format(m_value, context);
}
#endif

template<typename T, Formattable<T> Char>
CachedFormatter<T, Char>::CachedFormatter(std::basic_string_view<Char> fmt)
#ifdef SLIMLOG_FMTLIB
: m_empty(fmt.empty())
#endif
{
FormatParseContext<Char> parse_context(std::move(fmt));
// Suppress buggy GCC warning on fmtlib sources
#if defined(__GNUC__) and not defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
Formatter<T, Char>::parse(parse_context);
#if defined(__GNUC__) and not defined(__clang__)
#pragma GCC diagnostic pop
#endif
}

template<typename T, Formattable<T> Char>
template<typename Out>
void CachedFormatter<T, Char>::format(Out& out, T value) const
{
if (!m_value || value != *m_value) [[unlikely]] {
m_value = std::move(value);
m_buffer.clear();
#ifdef SLIMLOG_FMTLIB
// Shortcut for numeric types without formatting
if constexpr (std::is_arithmetic_v<T>) {
if (m_empty) [[likely]] {
m_buffer.append(fmt::format_int(*m_value));
out.append(m_buffer);
return;
}
}

// For libfmt it's possible to create a custom fmt::basic_format_context
// appending to the buffer directly, which is the most efficient way.
using Appender = std::conditional_t<
std::is_same_v<Char, char>,
fmt::appender,
std::back_insert_iterator<decltype(m_buffer)>>;
fmt::basic_format_context<Appender, Char> fmt_context(Appender(m_buffer), {});
Formatter<T, Char>::format(*m_value, fmt_context);
#else
// For std::format there is no way to build a custom format context,
// so we have to use dummy format string (empty string will be omitted),
// and pass FormatValue with a reference to CachedFormatter as an argument.
static constexpr std::array<Char, 3> Fmt{'{', '}', '\0'};
m_buffer.vformat(Fmt.data(), m_buffer.make_format_args(FormatValue(*this, *m_value)));
#endif
}
out.append(m_buffer);
}

} // namespace SlimLog
164 changes: 55 additions & 109 deletions include/slimlog/format.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @file format.h
* @brief Contains definitions of FormatString, FormatBuffer, and Format classes.
* @brief Contains declarations and definitions of classes related to formatting.
*/

#pragma once
Expand All @@ -17,13 +17,13 @@
#else
#include <slimlog/util/types.h>

#include <array>
#include <format>
#endif

#include <slimlog/location.h>
#include <slimlog/util/buffer.h>

#include <chrono>
#include <concepts>
#include <cstring>
#include <iterator>
Expand Down Expand Up @@ -142,60 +142,6 @@ concept Formattable = requires(const Char* fmt, Args... args) {
#endif
};

#ifndef SLIMLOG_FMTLIB
template<typename T, Formattable<T> Char>
class CachedFormatter;

/**
* @brief Wrapper for formatting a value with a cached formatter.
*
* Stores the value and reference to the cached formatter (see CachedFormatter).
* Used in combination with `std::formatter<FormatValue<T, Char>, Char>` specialization.
*
* @tparam T Value type.
* @tparam Char Output character type.
*/
template<typename T, Formattable<T> Char>
class FormatValue final {
public:
/**
* @brief Constructs a new FormatValue object from a reference to the formatter and value.
*
* @param formatter Reference to the formatter.
* @param value Value to be formatted.
*/
constexpr explicit FormatValue(const CachedFormatter<T, Char>& formatter, T value)
: m_formatter(formatter)
, m_value(std::move(value))
{
}

~FormatValue() = default;

FormatValue(const FormatValue&) = delete;
FormatValue(FormatValue&&) = delete;
auto operator=(const FormatValue&) -> FormatValue& = delete;
auto operator=(FormatValue&&) noexcept -> FormatValue& = delete;

/**
* @brief Formats the value with the cached formatter.
*
* @tparam Context Format context type (`std::format_context`).
* @param context Format context.
* @return Output iterator to the resulting buffer.
*/
template<typename Context>
auto format(Context& context) const
{
return m_formatter.format(m_value, context);
}

private:
const CachedFormatter<T, Char>& m_formatter;
T m_value;
};
#endif

/**
* @brief Buffer used for log message formatting.
*
Expand Down Expand Up @@ -288,6 +234,52 @@ class FormatBuffer final : public Util::MemoryBuffer<Char, BufferSize, Allocator
}
};

#ifndef SLIMLOG_FMTLIB
template<typename T, Formattable<T> Char>
class CachedFormatter;

/**
* @brief Wrapper for formatting a value with a cached formatter.
*
* Stores the value and reference to the cached formatter (see CachedFormatter).
* Used in combination with `std::formatter<FormatValue<T, Char>, Char>` specialization.
*
* @tparam T Value type.
* @tparam Char Output character type.
*/
template<typename T, Formattable<T> Char>
class FormatValue final {
public:
/**
* @brief Constructs a new FormatValue object from a reference to the formatter and value.
*
* @param formatter Reference to the formatter.
* @param value Value to be formatted.
*/
explicit FormatValue(const CachedFormatter<T, Char>& formatter, T value);
~FormatValue() = default;

FormatValue(const FormatValue&) = delete;
FormatValue(FormatValue&&) = delete;
auto operator=(const FormatValue&) -> FormatValue& = delete;
auto operator=(FormatValue&&) noexcept -> FormatValue& = delete;

/**
* @brief Formats the value with the cached formatter.
*
* @tparam Context Format context type (`std::format_context`).
* @param context Format context.
* @return Output iterator to the resulting buffer.
*/
template<typename Context>
auto format(Context& context) const;

private:
const CachedFormatter<T, Char>& m_formatter;
T m_value;
};
#endif

/**
* @brief Wrapper to parse the format string and store the format context.
*
Expand All @@ -301,30 +293,12 @@ class CachedFormatter final : Formatter<T, Char> {
public:
using Formatter<T, Char>::format;

/** @brief Default size of formatted string (enough for numbers and date/time) */
constexpr static size_t CachedBufferSize = 20;

/**
* @brief Constructs a new CachedFormatter object from a format string.
*
* @param fmt Format string.
*/
constexpr explicit CachedFormatter(std::basic_string_view<Char> fmt)
#ifdef SLIMLOG_FMTLIB
: m_empty(fmt.empty())
#endif
{
FormatParseContext<Char> parse_context(std::move(fmt));
// Suppress buggy GCC warning on fmtlib sources
#if defined(__GNUC__) and not defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif
Formatter<T, Char>::parse(parse_context);
#if defined(__GNUC__) and not defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
explicit CachedFormatter(std::basic_string_view<Char> fmt);

/**
* @brief Formats the value and writes to the output buffer.
Expand All @@ -334,46 +308,14 @@ class CachedFormatter final : Formatter<T, Char> {
* @param value Value to be formatted.
*/
template<typename Out>
void format(Out& out, T value) const
{
if (!m_value || value != *m_value) [[unlikely]] {
m_value = std::move(value);
m_buffer.clear();
#ifdef SLIMLOG_FMTLIB
// Shortcut for numeric types without formatting
if constexpr (std::is_arithmetic_v<T>) {
if (m_empty) [[likely]] {
m_buffer.append(fmt::format_int(*m_value));
out.append(m_buffer);
return;
}
}

// For libfmt it's possible to create a custom fmt::basic_format_context
// appending to the buffer directly, which is the most efficient way.
using Appender = std::conditional_t<
std::is_same_v<Char, char>,
fmt::appender,
std::back_insert_iterator<decltype(m_buffer)>>;
fmt::basic_format_context<Appender, Char> fmt_context(Appender(m_buffer), {});
Formatter<T, Char>::format(*m_value, fmt_context);
#else
// For std::format there is no way to build a custom format context,
// so we have to use dummy format string (empty string will be omitted),
// and pass FormatValue with a reference to CachedFormatter as an argument.
static constexpr std::array<Char, 3> Fmt{'{', '}', '\0'};
m_buffer.vformat(Fmt.data(), m_buffer.make_format_args(FormatValue(*this, *m_value)));
#endif
}
out.append(m_buffer);
}
void format(Out& out, T value) const;

private:
#ifdef SLIMLOG_FMTLIB
bool m_empty;
#endif
mutable std::optional<T> m_value;
mutable FormatBuffer<Char, CachedBufferSize> m_buffer;
mutable FormatBuffer<Char, 32> m_buffer;
};

} // namespace SlimLog
Expand All @@ -395,3 +337,7 @@ struct std::formatter<SlimLog::FormatValue<T, Char>, Char> { // NOLINT (cert-dcl
};
/** @endcond */
#endif

#ifdef SLIMLOG_HEADER_ONLY
#include <slimlog/format-inl.h> // IWYU pragma: keep
#endif
16 changes: 16 additions & 0 deletions src/slimlog.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <slimlog/format.h>
#include <slimlog/logger.h>
#include <slimlog/pattern.h>
#include <slimlog/policy.h>
Expand All @@ -6,12 +7,15 @@

#ifndef SLIMLOG_HEADER_ONLY
// IWYU pragma: begin_keep
#include <slimlog/format-inl.h>
#include <slimlog/pattern-inl.h>
#include <slimlog/record-inl.h>
#include <slimlog/sink-inl.h>
// IWYU pragma: end_keep
#endif

#include <chrono>
#include <cstddef>
#include <string_view>

namespace SlimLog {
Expand All @@ -21,13 +25,25 @@ template class SinkDriver<Logger<std::string_view>, MultiThreadedPolicy>;
template class Sink<Logger<std::string_view>>;
template class RecordStringView<char>;
template class Pattern<char>;
template class CachedFormatter<std::size_t, char>;
template class CachedFormatter<std::chrono::sys_seconds, char>;
#ifndef SLIMLOG_FMTLIB
template class FormatValue<std::size_t, char>;
template class FormatValue<std::chrono::sys_seconds, char>;
#endif

// wchar_t
template class SinkDriver<Logger<std::wstring_view>, SingleThreadedPolicy>;
template class SinkDriver<Logger<std::wstring_view>, MultiThreadedPolicy>;
template class Sink<Logger<std::wstring_view>>;
template class RecordStringView<wchar_t>;
template class Pattern<wchar_t>;
template class CachedFormatter<std::size_t, wchar_t>;
template class CachedFormatter<std::chrono::sys_seconds, wchar_t>;
#ifndef SLIMLOG_FMTLIB
template class FormatValue<std::size_t, wchar_t>;
template class FormatValue<std::chrono::sys_seconds, wchar_t>;
#endif

// char8_t (std::mbrtoc8() is supported only for newer versions of glibc++ and libc++)
/*#if defined(__cpp_char8_t)
Expand Down

0 comments on commit 400281b

Please sign in to comment.