1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
// This source code is released into the public domain.
module;
#include <ucl.h>
export module nihil.ucl:real;
import nihil.std;
import nihil.core;
import :object;
import :type;
namespace nihil::ucl {
export struct real final : object
{
using contained_type = double;
static constexpr object_type ucl_type = object_type::real;
// Create a real holding the value 0. Throws std::system_error
// on failure.
real()
: real(0)
{
}
// Create a real holding a specific value. Throws std::system_error
// on failure.
explicit real(contained_type value)
: real(noref, [&] {
auto *uobj = ::ucl_object_fromdouble(value);
if (uobj == nullptr)
throw std::system_error(std::make_error_code(sys_error()));
return uobj;
}())
{
}
// Create a new real from a UCL object. Throws type_mismatch
// on failure.
real(ref_t, ::ucl_object_t const *uobj)
: object(nihil::ucl::ref, ensure_ucl_type(uobj, real::ucl_type))
{
}
real(noref_t, ::ucl_object_t *uobj)
: object(nihil::ucl::noref, ensure_ucl_type(uobj, real::ucl_type))
{
}
// Return the value of this real.
[[nodiscard]] auto value(this real const &self) -> contained_type
{
auto v = contained_type{};
auto const *uobj = self.get_ucl_object();
if (::ucl_object_todouble_safe(uobj, &v))
return v;
throw std::runtime_error("ucl_object_todouble_safe failed");
}
private:
//
// Comparison operators.
//
[[nodiscard]] friend auto operator==(real const &a, real const &b) -> bool
{
return a.value() == b.value();
}
[[nodiscard]] friend auto operator<=>(real const &a, real const &b) -> std::partial_ordering
{
return a.value() <=> b.value();
}
[[nodiscard]] friend auto operator==(real const &a, real::contained_type b) -> bool
{
return a.value() == b;
}
[[nodiscard]] friend auto
operator<=>(real const &a, real::contained_type b) -> std::partial_ordering
{
return a.value() <=> b;
}
};
// Real constructor. This returns an error instead of throwing.
export [[nodiscard]] auto make_real(real::contained_type value = 0) -> std::expected<real, error>
{
auto *uobj = ::ucl_object_fromdouble(value);
if (uobj == nullptr)
return std::unexpected(error(errc::failed_to_create_object, error(sys_error())));
return real(noref, uobj);
}
// Literal operator.
inline namespace literals {
export constexpr auto operator""_ucl(long double d) -> real
{
if (d > static_cast<long double>(std::numeric_limits<double>::max()) ||
d < static_cast<long double>(std::numeric_limits<double>::min()))
throw std::out_of_range("literal out of range");
return real(static_cast<double>(d));
}
} // namespace literals
} // namespace nihil::ucl
namespace nihil {
inline namespace literals {
export using namespace ::nihil::ucl::literals;
}
} // namespace nihil
// std::formatter for a real. This provides the same format operations
// as std::formatter<double>;
export template <>
struct std::formatter<nihil::ucl::real, char>
{
std::formatter<double> base_formatter;
template <class ParseContext>
constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return base_formatter.parse(ctx);
}
template <class FmtContext>
auto format(nihil::ucl::real const &o, FmtContext &ctx) const -> FmtContext::iterator
{
return base_formatter.format(o.value(), ctx);
}
};
|