Compare commits

...

3 Commits

3 changed files with 139 additions and 7 deletions

View File

@ -0,0 +1,122 @@
#pragma once
#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1
#include <format>
#include "../container/boxed_object.hpp"
namespace mijin
{
//
// public concepts
//
template<typename T>
concept parse_result_type = requires
{
typename T::parse_result_t;
};
template<typename T, typename TParseContext>
concept parseable_by_type = parse_result_type &&
requires(const T& object, TParseContext& parseContext)
{
{ T::parseFormat(parseContext) } -> std::convertible_to<std::pair<typename TParseContext::iterator, typename T::parse_result_t>>;
};
template<typename T, typename TFmtContext>
concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext)
{
{ object.format(formatContext) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept complex_formattable_to_type = parseable_by_type<T, TParseContext> &&
requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult)
{
{ object.format(formatContext, parseResult) } -> std::convertible_to<typename TFmtContext::iterator>;
};
template<typename T, typename TFmtContext, typename TParseContext>
concept formattable_to_type = simple_formattable_to_type<T, TFmtContext> || complex_formattable_to_type<T, TFmtContext, TParseContext>;
template<typename T>
concept cformattable_type = formattable_to_type<T, std::format_context, std::format_parse_context>;
template<typename T>
concept wformattable_type = formattable_to_type<T, std::wformat_context, std::wformat_parse_context>;
template<typename T>
concept formattable_type = cformattable_type<T> && wformattable_type<T>;
template<typename T>
concept any_formattable_type = cformattable_type<T> || wformattable_type<T>;
//
// internal types
//
namespace impl
{
template<typename T>
struct ParseResult
{
};
template<parse_result_type T>
struct ParseResult<T>
{
BoxedObject<typename T::parse_result_t> value;
};
}
} // namespace mijin
template<mijin::any_formattable_type T>
struct std::formatter<T>
{
[[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult<T> parseResult;
template<typename TContext>
constexpr TContext::iterator parse(TContext& ctx)
{
if constexpr (mijin::parse_result_type<T>)
{
static_assert(mijin::parseable_by_type<T, TContext>, "Type does not support parsing by this context.");
auto [it, result] = T::parseFormat(ctx);
parseResult.value.construct(std::move(result));
return it;
}
else
{
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && *it != '}')
{
throw std::format_error("invalid format");
}
return it;
}
}
template<typename TContext>
TContext::iterator format(const T& object, TContext& ctx) const
{
if constexpr (mijin::parse_result_type<T>)
{
auto it = object.format(ctx, std::move(*parseResult.value));
parseResult.destroy();
return it;
}
else
{
static_assert(mijin::simple_formattable_to_type<T, TContext>, "Type does not support formatting to this context.");
return object.format(ctx);
}
}
};
#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED

View File

@ -3,6 +3,7 @@
#if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED) #if !defined(MIJIN_UTIL_ITERATORS_HPP_INCLUDED)
#define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1 #define MIJIN_UTIL_ITERATORS_HPP_INCLUDED 1
#include <array>
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
#include <optional> #include <optional>

View File

@ -39,9 +39,9 @@ namespace mijin
// internal variables // internal variables
// //
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
namespace namespace
{ {
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX
std::mutex gDlErrorMutex; // dlerror may not be thread-safe std::mutex gDlErrorMutex; // dlerror may not be thread-safe
const std::uint64_t gCPUClockResolution = []() const std::uint64_t gCPUClockResolution = []()
@ -52,8 +52,16 @@ const std::uint64_t gCPUClockResolution = []()
MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?"); MIJIN_ASSERT(time.tv_sec == 0, "What kind of cpu clock is this?");
return static_cast<std::uint64_t>(time.tv_nsec); return static_cast<std::uint64_t>(time.tv_nsec);
}(); }();
}; #elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX const std::uint64_t gCPUTickFrequency = []()
{
LARGE_INTEGER ticks;
[[maybe_unused]] const BOOL result = QueryPerformanceFrequency(&ticks);
MIJIN_ASSERT(result, "Error getting cpu frequency.");
return static_cast<std::uint64_t>(ticks.QuadPart);
}();
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX || MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
}
// //
// internal functions // internal functions
@ -196,8 +204,10 @@ std::uint64_t getCPUTicks() MIJIN_NOEXCEPT
MIJIN_ASSERT(result == 0, "Error getting cpu time."); MIJIN_ASSERT(result == 0, "Error getting cpu time.");
return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution; return (static_cast<std::uint64_t>(time.tv_sec) * 1e9 + time.tv_nsec) / gCPUClockResolution;
#else #else
MIJIN_ERROR("TODO"); LARGE_INTEGER ticks;
return 0; [[maybe_unused]] const BOOL result = QueryPerformanceCounter(&ticks);
MIJIN_ASSERT(result, "Error getting cpu time.");
return static_cast<std::uint64_t>(ticks.QuadPart);
#endif #endif
} }
@ -206,8 +216,7 @@ std::uint64_t getCPUTicksPerSecond() MIJIN_NOEXCEPT
#if MIJIN_TARGET_OS == MIJIN_OS_LINUX #if MIJIN_TARGET_OS == MIJIN_OS_LINUX
return 1e9 / gCPUClockResolution; return 1e9 / gCPUClockResolution;
#else #else
MIJIN_ERROR("TODO"); return gCPUTickFrequency;
return 0;
#endif #endif
} }
} // namespace mijin } // namespace mijin