Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ if(MSVC)
_iris_cxx_best_practices
INTERFACE
/W4 /analyze /analyze:external-
$<$<CONFIG:Release>:/Zi /Zo>
)
target_link_options(
_iris_cxx_best_practices
INTERFACE
$<$<CONFIG:Release>:/DEBUG:FULL>
)

else()
Expand Down
27 changes: 27 additions & 0 deletions include/iris/format.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef IRIS_FORMAT_HPP
#define IRIS_FORMAT_HPP

// SPDX-License-Identifier: MIT

#include <iris/format_traits.hpp>

#include <format>

namespace iris {

template<class CharT>
struct no_spec_formatter
{
static constexpr std::basic_format_parse_context<CharT>::const_iterator
parse(std::basic_format_parse_context<CharT>& ctx)
{
auto it = ctx.begin();
if (it == ctx.end()) return it;
if (*it == format_traits<CharT>::brace_close) return it;
throw std::format_error("unknown format specifier");
}
};

} // iris

#endif
8 changes: 7 additions & 1 deletion include/iris/format_traits.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#ifndef IRIS_ZZ_FORMAT_TRAITS_HPP
#ifndef IRIS_ZZ_FORMAT_TRAITS_HPP
#define IRIS_ZZ_FORMAT_TRAITS_HPP

// SPDX-License-Identifier: MIT
Expand All @@ -16,8 +16,11 @@ template<>
struct format_traits<char>
{
using char_type = char;
static constexpr char_type square_brace_open = '[';
static constexpr char_type paren_close = ')';
static constexpr char_type brace_open = '{';
static constexpr char_type brace_close = '}';
static constexpr char_type comma = ',';

template<class T>
static constexpr std::basic_format_string<char_type, std::type_identity_t<T>>
Expand All @@ -28,8 +31,11 @@ template<>
struct format_traits<wchar_t>
{
using char_type = wchar_t;
static constexpr char_type square_brace_open = L'[';
static constexpr char_type paren_close = L')';
static constexpr char_type brace_open = L'{';
static constexpr char_type brace_close = L'}';
static constexpr char_type comma = L',';

template<class T>
static constexpr std::basic_format_string<char_type, std::type_identity_t<T>>
Expand Down
129 changes: 129 additions & 0 deletions include/iris/interval.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#ifndef IRIS_INTERVAL_HPP
#define IRIS_INTERVAL_HPP

// SPDX-License-Identifier: MIT

#include <iris/config.hpp>
#include <iris/format_traits.hpp>

#include <iterator>
#include <format>
#include <concepts>
#include <compare>
#include <type_traits>
#include <utility>

namespace iris {

template<std::signed_integral T>
struct interval
{
using value_type = T;
T left, right;

[[nodiscard]]
constexpr bool operator==(interval const&) const noexcept = default;

[[nodiscard]]
constexpr std::strong_ordering operator<=>(interval const&) const noexcept = default;
};

template<std::size_t I, class T>
[[nodiscard]] constexpr T& get(interval<T>& iv) noexcept
{
static_assert(I == 0 || I == 1);
if constexpr (I == 0) { return iv.left; } else { return iv.right; }
}
template<std::size_t I, class T>
[[nodiscard]] constexpr T const& get(interval<T> const& iv) noexcept
{
static_assert(I == 0 || I == 1);
if constexpr (I == 0) { return iv.left; } else { return iv.right; }
}
template<std::size_t I, class T>
[[nodiscard]] constexpr T&& get(interval<T>&& iv) noexcept
{
static_assert(I == 0 || I == 1);
if constexpr (I == 0) { return std::move(iv).left; } else { return std::move(iv).right; }
}
template<std::size_t I, class T>
[[nodiscard]] constexpr T const&& get(interval<T> const&& iv) noexcept
{
static_assert(I == 0 || I == 1);
if constexpr (I == 0) { return std::move(iv).left; } else { return std::move(iv).right; }
}

} // iris

template<class T>
struct std::tuple_size<iris::interval<T>>
: std::integral_constant<std::size_t, 2>
{};

template<std::size_t I, class T>
struct std::tuple_element<I, iris::interval<T>>
{
using type = T;
};

template<class T, class CharT>
struct std::formatter<iris::interval<T>, CharT>
{
[[nodiscard]] constexpr std::basic_format_parse_context<CharT>::const_iterator
parse(std::basic_format_parse_context<CharT>& ctx)
{
auto const first = ctx.begin();
if (first == ctx.end()) return first;
if (*first == iris::format_traits<CharT>::brace_close) return first;

// Bound the search to this replacement field
auto const close_it = std::find(
first, ctx.end(),
iris::format_traits<CharT>::brace_close
);
if (close_it == ctx.end()) {
throw std::format_error("unterminated format specifier");
}

auto const comma_it = std::find(
first, close_it,
iris::format_traits<CharT>::comma
);
if (comma_it == close_it) {
throw std::format_error("expected ',' in format specifier");
}

{
std::basic_format_parse_context<CharT> left_ctx{
std::basic_string_view<CharT>{first, comma_it}
};
if (left_fmt_.parse(left_ctx) != left_ctx.end()) {
throw std::format_error("trailing characters in left format specifier");
}
}
{
std::basic_format_parse_context<CharT> right_ctx{
std::basic_string_view<CharT>{std::next(comma_it), close_it}
};
if (right_fmt_.parse(right_ctx) != right_ctx.end()) {
throw std::format_error("trailing characters in right format specifier");
}
}
return close_it;
}

template<class Ctx>
Ctx::iterator format(iris::interval<T> const& iv, Ctx& ctx) const
{
ctx.advance_to(std::format_to(ctx.out(), "{}", iris::format_traits<CharT>::square_brace_open));
left_fmt_.format(iv.left, ctx);
ctx.advance_to(std::format_to(ctx.out(), "{}", iris::format_traits<CharT>::comma));
right_fmt_.format(iv.right, ctx);
return std::format_to(ctx.out(), "{}", iris::format_traits<CharT>::paren_close);
}

private:
std::formatter<T, CharT> left_fmt_, right_fmt_;
};

#endif
Loading
Loading