Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
a37a0cc7
Commit
a37a0cc7
authored
Mar 24, 2017
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement overflow-guarded types.
parent
655ac0f2
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
936 additions
and
57 deletions
+936
-57
units-test.c++
c++/src/kj/units-test.c++
+251
-0
units.c++
c++/src/kj/units.c++
+5
-0
units.h
c++/src/kj/units.h
+680
-57
No files found.
c++/src/kj/units-test.c++
View file @
a37a0cc7
...
@@ -61,5 +61,256 @@ TEST(UnitMeasure, Basics) {
...
@@ -61,5 +61,256 @@ TEST(UnitMeasure, Basics) {
EXPECT_FALSE
(
8
*
KIB
<
4
*
KIB
);
EXPECT_FALSE
(
8
*
KIB
<
4
*
KIB
);
}
}
template
<
typename
T
,
typename
U
>
static
void
assertSameType
()
{
U
u
;
T
*
t
=
&
u
;
*
t
=
0
;
}
TEST
(
UnitMeasure
,
AtLeastUInt
)
{
assertSameType
<
uint8_t
,
AtLeastUInt
<
2
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
3
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
4
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
5
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
6
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
7
>>
();
assertSameType
<
uint8_t
,
AtLeastUInt
<
8
>>
();
assertSameType
<
uint16_t
,
AtLeastUInt
<
9
>>
();
assertSameType
<
uint16_t
,
AtLeastUInt
<
10
>>
();
assertSameType
<
uint16_t
,
AtLeastUInt
<
13
>>
();
assertSameType
<
uint16_t
,
AtLeastUInt
<
16
>>
();
assertSameType
<
uint32_t
,
AtLeastUInt
<
17
>>
();
assertSameType
<
uint32_t
,
AtLeastUInt
<
23
>>
();
assertSameType
<
uint32_t
,
AtLeastUInt
<
24
>>
();
assertSameType
<
uint32_t
,
AtLeastUInt
<
25
>>
();
assertSameType
<
uint32_t
,
AtLeastUInt
<
32
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
33
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
40
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
41
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
47
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
48
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
52
>>
();
assertSameType
<
uint64_t
,
AtLeastUInt
<
64
>>
();
// COMPILE ERROR: assertSameType<uint64_t, AtLeastUInt<65>>();
}
TEST
(
UnitMeasure
,
GuardedConst
)
{
// TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
// line restored to verify that they actually error out.
KJ_EXPECT
((
guarded
<
456
>
()
+
guarded
<
123
>
()).
unwrap
()
==
456
+
123
);
KJ_EXPECT
((
guarded
<
456
>
()
-
guarded
<
123
>
()).
unwrap
()
==
456
-
123
);
KJ_EXPECT
((
guarded
<
456
>
()
*
guarded
<
123
>
()).
unwrap
()
==
456
*
123
);
KJ_EXPECT
((
guarded
<
456
>
()
/
guarded
<
123
>
()).
unwrap
()
==
456
/
123
);
KJ_EXPECT
((
guarded
<
456
>
()
%
guarded
<
123
>
()).
unwrap
()
==
456
%
123
);
KJ_EXPECT
((
guarded
<
456
>
()
<<
guarded
<
5
>
()).
unwrap
()
==
456
<<
5
);
KJ_EXPECT
((
guarded
<
456
>
()
>>
guarded
<
2
>
()).
unwrap
()
==
456
>>
2
);
KJ_EXPECT
(
guarded
<
123
>
()
==
guarded
<
123
>
());
KJ_EXPECT
(
guarded
<
123
>
()
!=
guarded
<
456
>
());
KJ_EXPECT
(
guarded
<
123
>
()
<
guarded
<
456
>
());
KJ_EXPECT
(
guarded
<
456
>
()
>
guarded
<
123
>
());
KJ_EXPECT
(
guarded
<
123
>
()
<=
guarded
<
456
>
());
KJ_EXPECT
(
guarded
<
456
>
()
>=
guarded
<
123
>
());
KJ_EXPECT
(
!
(
guarded
<
123
>
()
==
guarded
<
456
>
()));
KJ_EXPECT
(
!
(
guarded
<
123
>
()
!=
guarded
<
123
>
()));
KJ_EXPECT
(
!
(
guarded
<
456
>
()
<
guarded
<
123
>
()));
KJ_EXPECT
(
!
(
guarded
<
123
>
()
>
guarded
<
456
>
()));
KJ_EXPECT
(
!
(
guarded
<
456
>
()
<=
guarded
<
123
>
()));
KJ_EXPECT
(
!
(
guarded
<
123
>
()
>=
guarded
<
456
>
()));
{
uint16_t
succ
=
unguard
(
guarded
<
12345
>
());
KJ_EXPECT
(
succ
==
12345
);
// COMPILE ERROR: uint8_t err KJ_UNUSED = unguard(guarded<12345>());
}
// COMPILE ERROR: auto err1 KJ_UNUSED = guarded<(0xffffffffffffffffull)>() + guarded<1>();
// COMPILE ERROR: auto err2 KJ_UNUSED = guarded<1>() - guarded<2>();
// COMPILE ERROR: auto err3 KJ_UNUSED = guarded<(1ull << 60)>() * guarded<(1ull << 60)>();
// COMPILE ERROR: auto err4 KJ_UNUSED = guarded<1>() / guarded<0>();
// COMPILE ERROR: auto err5 KJ_UNUSED = guarded<1>() % guarded<0>();
// COMPILE ERROR: auto err6 KJ_UNUSED = guarded<1>() << guarded<64>();
// COMPILE ERROR: auto err7 KJ_UNUSED = guarded<(1ull << 60)>() << guarded<4>();
// COMPILE ERROR: auto err8 KJ_UNUSED = guarded<1>() >> guarded<64>();
// COMPILE ERROR: guardedAdd<0xffffffffffffffffull, 1>();
// COMPILE ERROR: guardedSub<1, 2>();
// COMPILE ERROR: guardedMul<0x100000000, 0x100000000>();
// COMPILE ERROR: guardedLShift<0x10, 60>();
}
template
<
uint
value
>
constexpr
Guarded
<
value
,
uint
>
guardedValue
()
{
return
Guarded
<
value
,
uint
>
(
value
,
unsafe
);
}
TEST
(
UnitMeasure
,
Guarded
)
{
// TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
// line restored to verify that they actually error out.
KJ_EXPECT
((
guardedValue
<
456
>
()
+
guardedValue
<
123
>
()).
unwrap
()
==
456
+
123
);
KJ_EXPECT
(
guardedValue
<
456
>
().
subtractChecked
(
guardedValue
<
123
>
(),
[](){}).
unwrap
()
==
456
-
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
*
guardedValue
<
123
>
()).
unwrap
()
==
456
*
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
/
guardedValue
<
123
>
()).
unwrap
()
==
456
/
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
%
guardedValue
<
123
>
()).
unwrap
()
==
456
%
123
);
{
Guarded
<
123
,
uint8_t
>
succ
KJ_UNUSED
;
// COMPILE ERROR: Guarded<1234, uint8_t> err KJ_UNUSED;
// COMPILE ERROR: auto err KJ_UNUSED = guardedValue<0xffffffffull>() + guardedValue<1>();
}
{
Guarded
<
123
,
uint8_t
>
succ1
KJ_UNUSED
=
guardedValue
<
123
>
();
Guarded
<
123
,
uint8_t
>
succ2
KJ_UNUSED
=
guardedValue
<
122
>
();
Guarded
<
123
,
uint8_t
>
succ3
KJ_UNUSED
=
guardedValue
<
0
>
();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guardedValue<124>();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guardedValue<125>();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guardedValue<123456>();
}
Guarded
<
123
,
uint8_t
>
foo
;
foo
=
guardedValue
<
123
>
();
foo
=
guardedValue
<
122
>
();
foo
=
guardedValue
<
0
>
();
// COMPILE ERROR: foo = guardedValue<124>();
// COMPILE ERROR: foo = guardedValue<125>();
// COMPILE ERROR: foo = guardedValue<123456>();
assertMax
<
122
>
(
foo
,
[]()
{});
// COMPILE ERROR: assertMax<123>(foo, []() {});
// COMPILE ERROR: assertMax<124>(foo, []() {});
assertMaxBits
<
6
>
(
foo
,
[]()
{});
// COMPILE ERROR: assertMaxBits<7>(foo, []() {});
// COMPILE ERROR: assertMaxBits<8>(foo, []() {});
Guarded
<
12
,
uint8_t
>
bar
;
// COMPILE ERROR: bar = foo;
// COMPILE ERROR: bar = foo.assertMax<13>([]() {});
bool
caught
=
false
;
foo
=
guardedValue
<
13
>
();
bar
=
foo
.
assertMax
<
12
>
([
&
]()
{
caught
=
true
;
});
KJ_EXPECT
(
caught
);
foo
=
guardedValue
<
100
>
()
+
guardedValue
<
23
>
();
// COMPILE ERROR: foo = guardedValue<100>() + guardedValue<24>();
bar
=
guardedValue
<
3
>
()
*
guardedValue
<
4
>
();
// COMPILE ERROR: bar = guardedValue<2>() * guardedValue<7>();
foo
.
subtractChecked
(
guardedValue
<
122
>
(),
[]()
{
KJ_FAIL_EXPECT
();
});
foo
.
subtractChecked
(
guardedValue
<
123
>
(),
[]()
{
KJ_FAIL_EXPECT
();
});
caught
=
false
;
foo
.
subtractChecked
(
guardedValue
<
124
>
(),
[
&
]()
{
caught
=
true
;
});
KJ_EXPECT
(
caught
);
{
Guarded
<
65535
,
uint16_t
>
succ1
KJ_UNUSED
=
guarded
((
uint16_t
)
123
);
// COMPILE ERROR: Guarded<65534, uint16_t> err KJ_UNUSED = guarded((uint16_t)123);
}
uint
old
=
foo
.
unwrap
();
foo
=
foo
*
unit
<
decltype
(
foo
)
>
();
KJ_EXPECT
(
foo
.
unwrap
()
==
old
);
{
Guarded
<
1234
,
uint16_t
>
x
=
guarded
<
123
>
();
uint16_t
succ
=
unguard
(
x
);
KJ_EXPECT
(
succ
==
123
);
// COMPILE ERROR: uint8_t err KJ_UNUSED = unguard(x);
}
}
TEST
(
UnitMeasure
,
GuardedVsGuardedConst
)
{
// TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
// line restored to verify that they actually error out.
KJ_EXPECT
((
guardedValue
<
456
>
()
+
guarded
<
123
>
()).
unwrap
()
==
456
+
123
);
KJ_EXPECT
(
guardedValue
<
456
>
().
subtractChecked
(
guarded
<
123
>
(),
[](){}).
unwrap
()
==
456
-
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
*
guarded
<
123
>
()).
unwrap
()
==
456
*
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
/
guarded
<
123
>
()).
unwrap
()
==
456
/
123
);
KJ_EXPECT
((
guardedValue
<
456
>
()
%
guarded
<
123
>
()).
unwrap
()
==
456
%
123
);
{
Guarded
<
123
,
uint8_t
>
succ1
KJ_UNUSED
=
guarded
<
123
>
();
Guarded
<
123
,
uint8_t
>
succ2
KJ_UNUSED
=
guarded
<
122
>
();
Guarded
<
123
,
uint8_t
>
succ3
KJ_UNUSED
=
guarded
<
0
>
();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guarded<124>();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guarded<125>();
// COMPILE ERROR: Guarded<123, uint8_t> err KJ_UNUSED = guarded<123456>();
}
Guarded
<
123
,
uint8_t
>
foo
;
foo
=
guarded
<
123
>
();
foo
=
guarded
<
122
>
();
foo
=
guarded
<
0
>
();
// COMPILE ERROR: foo = guarded<124>();
// COMPILE ERROR: foo = guarded<125>();
// COMPILE ERROR: foo = guarded<123456>();
Guarded
<
16
,
uint8_t
>
bar
;
// COMPILE ERROR: bar = foo >> guarded<2>();
bar
=
foo
>>
guarded
<
3
>
();
// COMPILE ERROR: foo = bar << guarded<3>();
foo
=
bar
<<
guarded
<
2
>
();
}
TEST
(
UnitMeasure
,
GuardedRange
)
{
uint
expected
=
0
;
for
(
auto
i
:
zeroTo
(
guarded
<
10
>
()))
{
Guarded
<
10
,
uint8_t
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
expected
=
0
;
for
(
auto
i
:
zeroTo
(
guarded
((
uint8_t
)
10
)))
{
Guarded
<
255
,
uint8_t
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
expected
=
3
;
for
(
auto
i
:
range
(
guarded
((
uint8_t
)
3
),
guarded
((
uint8_t
)
10
)))
{
Guarded
<
255
,
uint8_t
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
}
TEST
(
UnitMeasure
,
GuardedQuantity
)
{
auto
BYTES
=
unit
<
Quantity
<
Guarded
<
12345
,
uint16_t
>
,
byte
>>
();
uint
expected
=
0
;
for
(
auto
i
:
zeroTo
(
guarded
<
10
>
()
*
BYTES
))
{
Quantity
<
Guarded
<
10
,
uint8_t
>
,
byte
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
/
BYTES
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
expected
=
0
;
for
(
auto
i
:
zeroTo
(
guarded
((
uint8_t
)
10
)
*
BYTES
))
{
Quantity
<
Guarded
<
255
,
uint8_t
>
,
byte
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
/
BYTES
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
expected
=
3
;
for
(
auto
i
:
range
(
guarded
((
uint8_t
)
3
)
*
BYTES
,
guarded
((
uint8_t
)
10
)
*
BYTES
))
{
Quantity
<
Guarded
<
255
,
uint8_t
>
,
byte
>
value
=
i
;
KJ_EXPECT
(
unguard
(
value
/
BYTES
)
==
expected
++
);
}
KJ_EXPECT
(
expected
==
10
);
}
}
// namespace
}
// namespace
}
// namespace kj
}
// namespace kj
c++/src/kj/units.c++
View file @
a37a0cc7
...
@@ -20,7 +20,12 @@
...
@@ -20,7 +20,12 @@
// THE SOFTWARE.
// THE SOFTWARE.
#include "units.h"
#include "units.h"
#include "debug.h"
namespace
kj
{
namespace
kj
{
void
ThrowOverflow
::
operator
()()
const
{
KJ_FAIL_REQUIRE
(
"integer overflow"
);
}
}
// namespace kj
}
// namespace kj
c++/src/kj/units.h
View file @
a37a0cc7
...
@@ -31,6 +31,7 @@
...
@@ -31,6 +31,7 @@
#endif
#endif
#include "common.h"
#include "common.h"
#include <inttypes.h>
namespace
kj
{
namespace
kj
{
...
@@ -67,6 +68,15 @@ struct Id {
...
@@ -67,6 +68,15 @@ struct Id {
// =======================================================================================
// =======================================================================================
// Quantity and UnitRatio -- implement unit analysis via the type system
// Quantity and UnitRatio -- implement unit analysis via the type system
struct
Unsafe_
{};
constexpr
Unsafe_
unsafe
=
Unsafe_
();
// Use as a parameter to constructors that are unsafe to indicate that you really do mean it.
template
<
uint64_t
maxN
,
typename
T
>
class
Guarded
;
template
<
uint
value
>
class
GuardedConst
;
template
<
typename
T
>
constexpr
bool
isIntegral
()
{
return
false
;
}
template
<
typename
T
>
constexpr
bool
isIntegral
()
{
return
false
;
}
template
<>
constexpr
bool
isIntegral
<
char
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
char
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
signed
char
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
signed
char
>
()
{
return
true
;
}
...
@@ -80,6 +90,16 @@ template <> constexpr bool isIntegral<unsigned int>() { return true; }
...
@@ -80,6 +90,16 @@ template <> constexpr bool isIntegral<unsigned int>() { return true; }
template
<>
constexpr
bool
isIntegral
<
unsigned
long
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
unsigned
long
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
unsigned
long
long
>
()
{
return
true
;
}
template
<>
constexpr
bool
isIntegral
<
unsigned
long
long
>
()
{
return
true
;
}
template
<
typename
T
>
struct
IsIntegralOrGuarded_
{
static
constexpr
bool
value
=
isIntegral
<
T
>
();
};
template
<
uint64_t
m
,
typename
T
>
struct
IsIntegralOrGuarded_
<
Guarded
<
m
,
T
>>
{
static
constexpr
bool
value
=
true
;
};
template
<
uint
v
>
struct
IsIntegralOrGuarded_
<
GuardedConst
<
v
>>
{
static
constexpr
bool
value
=
true
;
};
template
<
typename
T
>
inline
constexpr
bool
isIntegralOrGuarded
()
{
return
IsIntegralOrGuarded_
<
T
>::
value
;
}
template
<
typename
Number
,
typename
Unit1
,
typename
Unit2
>
template
<
typename
Number
,
typename
Unit1
,
typename
Unit2
>
class
UnitRatio
{
class
UnitRatio
{
// A multiplier used to convert Quantities of one unit to Quantities of another unit. See
// A multiplier used to convert Quantities of one unit to Quantities of another unit. See
...
@@ -88,12 +108,13 @@ class UnitRatio {
...
@@ -88,12 +108,13 @@ class UnitRatio {
// Construct this type by dividing one Quantity by another of a different unit. Use this type
// Construct this type by dividing one Quantity by another of a different unit. Use this type
// by multiplying it by a Quantity, or dividing a Quantity by it.
// by multiplying it by a Quantity, or dividing a Quantity by it.
static_assert
(
isIntegral
<
Number
>
(),
"Underlying type for UnitRatio must be integer."
);
static_assert
(
isIntegralOrGuarded
<
Number
>
(),
"Underlying type for UnitRatio must be integer."
);
public
:
public
:
inline
UnitRatio
()
{}
inline
UnitRatio
()
{}
constexpr
explicit
UnitRatio
(
Number
unit1PerUnit2
)
:
unit1PerUnit2
(
unit1PerUnit2
)
{}
constexpr
UnitRatio
(
Number
unit1PerUnit2
,
decltype
(
unsafe
)
)
:
unit1PerUnit2
(
unit1PerUnit2
)
{}
// This constructor was intended to be private, but GCC complains about it being private in a
// This constructor was intended to be private, but GCC complains about it being private in a
// bunch of places that don't appear to even call it, so I made it public. Oh well.
// bunch of places that don't appear to even call it, so I made it public. Oh well.
...
@@ -102,50 +123,50 @@ public:
...
@@ -102,50 +123,50 @@ public:
:
unit1PerUnit2
(
other
.
unit1PerUnit2
)
{}
:
unit1PerUnit2
(
other
.
unit1PerUnit2
)
{}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
+
OtherNumber
(
1
)),
Unit1
,
Unit2
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
+
OtherNumber
(
)),
Unit1
,
Unit2
>
operator
+
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
operator
+
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
return
UnitRatio
<
decltype
(
Number
(
1
)
+
OtherNumber
(
1
)),
Unit1
,
Unit2
>
(
return
UnitRatio
<
decltype
(
Number
(
)
+
OtherNumber
(
)),
Unit1
,
Unit2
>
(
unit1PerUnit2
+
other
.
unit1PerUnit2
);
unit1PerUnit2
+
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
-
OtherNumber
(
1
)),
Unit1
,
Unit2
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
-
OtherNumber
(
)),
Unit1
,
Unit2
>
operator
-
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
operator
-
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
return
UnitRatio
<
decltype
(
Number
(
1
)
-
OtherNumber
(
1
)),
Unit1
,
Unit2
>
(
return
UnitRatio
<
decltype
(
Number
(
)
-
OtherNumber
(
)),
Unit1
,
Unit2
>
(
unit1PerUnit2
-
other
.
unit1PerUnit2
);
unit1PerUnit2
-
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
Unit3
>
template
<
typename
OtherNumber
,
typename
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit3
,
Unit2
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit3
,
Unit2
>
operator
*
(
UnitRatio
<
OtherNumber
,
Unit3
,
Unit1
>
other
)
const
{
operator
*
(
UnitRatio
<
OtherNumber
,
Unit3
,
Unit1
>
other
)
const
{
// U1 / U2 * U3 / U1 = U3 / U2
// U1 / U2 * U3 / U1 = U3 / U2
return
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit3
,
Unit2
>
(
return
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit3
,
Unit2
>
(
unit1PerUnit2
*
other
.
unit1PerUnit2
);
unit1PerUnit2
*
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
Unit3
>
template
<
typename
OtherNumber
,
typename
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit1
,
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit1
,
Unit3
>
operator
*
(
UnitRatio
<
OtherNumber
,
Unit2
,
Unit3
>
other
)
const
{
operator
*
(
UnitRatio
<
OtherNumber
,
Unit2
,
Unit3
>
other
)
const
{
// U1 / U2 * U2 / U3 = U1 / U3
// U1 / U2 * U2 / U3 = U1 / U3
return
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit1
,
Unit3
>
(
return
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit1
,
Unit3
>
(
unit1PerUnit2
*
other
.
unit1PerUnit2
);
unit1PerUnit2
*
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
Unit3
>
template
<
typename
OtherNumber
,
typename
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit3
,
Unit2
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit3
,
Unit2
>
operator
/
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit3
>
other
)
const
{
operator
/
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit3
>
other
)
const
{
// (U1 / U2) / (U1 / U3) = U3 / U2
// (U1 / U2) / (U1 / U3) = U3 / U2
return
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit3
,
Unit2
>
(
return
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit3
,
Unit2
>
(
unit1PerUnit2
/
other
.
unit1PerUnit2
);
unit1PerUnit2
/
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
Unit3
>
template
<
typename
OtherNumber
,
typename
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit1
,
Unit3
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit1
,
Unit3
>
operator
/
(
UnitRatio
<
OtherNumber
,
Unit3
,
Unit2
>
other
)
const
{
operator
/
(
UnitRatio
<
OtherNumber
,
Unit3
,
Unit2
>
other
)
const
{
// (U1 / U2) / (U3 / U2) = U1 / U3
// (U1 / U2) / (U3 / U2) = U1 / U3
return
UnitRatio
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit1
,
Unit3
>
(
return
UnitRatio
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit1
,
Unit3
>
(
unit1PerUnit2
/
other
.
unit1PerUnit2
);
unit1PerUnit2
/
other
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
decltype
(
Number
(
1
)
/
OtherNumber
(
1
))
inline
decltype
(
Number
(
)
/
OtherNumber
(
))
operator
/
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
operator
/
(
UnitRatio
<
OtherNumber
,
Unit1
,
Unit2
>
other
)
const
{
return
unit1PerUnit2
/
other
.
unit1PerUnit2
;
return
unit1PerUnit2
/
other
.
unit1PerUnit2
;
}
}
...
@@ -162,14 +183,14 @@ private:
...
@@ -162,14 +183,14 @@ private:
friend
class
UnitRatio
;
friend
class
UnitRatio
;
template
<
typename
N1
,
typename
N2
,
typename
U1
,
typename
U2
>
template
<
typename
N1
,
typename
N2
,
typename
U1
,
typename
U2
>
friend
inline
constexpr
UnitRatio
<
decltype
(
N1
(
1
)
*
N2
(
1
)),
U1
,
U2
>
friend
inline
constexpr
UnitRatio
<
decltype
(
N1
(
)
*
N2
(
)),
U1
,
U2
>
operator
*
(
N1
,
UnitRatio
<
N2
,
U1
,
U2
>
);
operator
*
(
N1
,
UnitRatio
<
N2
,
U1
,
U2
>
);
};
};
template
<
typename
N1
,
typename
N2
,
typename
U1
,
typename
U2
>
template
<
typename
N1
,
typename
N2
,
typename
U1
,
typename
U2
>
inline
constexpr
UnitRatio
<
decltype
(
N1
(
1
)
*
N2
(
1
)),
U1
,
U2
>
inline
constexpr
UnitRatio
<
decltype
(
N1
(
)
*
N2
(
)),
U1
,
U2
>
operator
*
(
N1
n
,
UnitRatio
<
N2
,
U1
,
U2
>
r
)
{
operator
*
(
N1
n
,
UnitRatio
<
N2
,
U1
,
U2
>
r
)
{
return
UnitRatio
<
decltype
(
N1
(
1
)
*
N2
(
1
)),
U1
,
U2
>
(
n
*
r
.
unit1PerUnit2
);
return
UnitRatio
<
decltype
(
N1
(
)
*
N2
()),
U1
,
U2
>
(
n
*
r
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
Number
,
typename
Unit
>
template
<
typename
Number
,
typename
Unit
>
...
@@ -216,10 +237,11 @@ class Quantity {
...
@@ -216,10 +237,11 @@ class Quantity {
// waitFor(3 * MINUTES);
// waitFor(3 * MINUTES);
// }
// }
static_assert
(
isIntegral
<
Number
>
(),
"Underlying type for Quantity must be integer."
);
static_assert
(
isIntegralOrGuarded
<
Number
>
(),
"Underlying type for Quantity must be integer."
);
public
:
public
:
inline
constexpr
Quantity
()
{}
inline
constexpr
Quantity
()
=
default
;
inline
constexpr
Quantity
(
MaxValue_
)
:
value
(
maxValue
)
{}
inline
constexpr
Quantity
(
MaxValue_
)
:
value
(
maxValue
)
{}
inline
constexpr
Quantity
(
MinValue_
)
:
value
(
minValue
)
{}
inline
constexpr
Quantity
(
MinValue_
)
:
value
(
minValue
)
{}
...
@@ -228,7 +250,7 @@ public:
...
@@ -228,7 +250,7 @@ public:
// parameters, causing the compiler to complain of a duplicate constructor definition, so we
// parameters, causing the compiler to complain of a duplicate constructor definition, so we
// specify MaxValue_ and MinValue_ types explicitly.
// specify MaxValue_ and MinValue_ types explicitly.
inline
explicit
constexpr
Quantity
(
Number
value
)
:
value
(
value
)
{}
inline
constexpr
Quantity
(
Number
value
,
decltype
(
unsafe
)
)
:
value
(
value
)
{}
// This constructor was intended to be private, but GCC complains about it being private in a
// This constructor was intended to be private, but GCC complains about it being private in a
// bunch of places that don't appear to even call it, so I made it public. Oh well.
// bunch of places that don't appear to even call it, so I made it public. Oh well.
...
@@ -237,60 +259,65 @@ public:
...
@@ -237,60 +259,65 @@ public:
:
value
(
other
.
value
)
{}
:
value
(
other
.
value
)
{}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
+
OtherNumber
(
1
)),
Unit
>
inline
Quantity
&
operator
=
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
{
value
=
other
.
value
;
return
*
this
;
}
template
<
typename
OtherNumber
>
inline
constexpr
Quantity
<
decltype
(
Number
()
+
OtherNumber
()),
Unit
>
operator
+
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
operator
+
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
+
OtherNumber
(
1
)),
Unit
>
(
value
+
other
.
valu
e
);
return
Quantity
<
decltype
(
Number
(
)
+
OtherNumber
()),
Unit
>
(
value
+
other
.
value
,
unsaf
e
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
-
OtherNumber
(
1
)),
Unit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
-
OtherNumber
(
)),
Unit
>
operator
-
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
operator
-
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
-
OtherNumber
(
1
)),
Unit
>
(
value
-
other
.
valu
e
);
return
Quantity
<
decltype
(
Number
(
)
-
OtherNumber
()),
Unit
>
(
value
-
other
.
value
,
unsaf
e
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
,
typename
=
EnableIf
<
isIntegralOrGuarded
<
OtherNumber
>
()
>
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
Unit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
Unit
>
operator
*
(
OtherNumber
other
)
const
{
operator
*
(
OtherNumber
other
)
const
{
static_assert
(
isIntegral
<
OtherNumber
>
(),
"Multiplied Quantity by non-integer."
);
return
Quantity
<
decltype
(
Number
()
*
other
),
Unit
>
(
value
*
other
,
unsafe
);
return
Quantity
<
decltype
(
Number
(
1
)
*
other
),
Unit
>
(
value
*
other
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
,
typename
=
EnableIf
<
isIntegralOrGuarded
<
OtherNumber
>
()
>
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
/
OtherNumber
(
1
)),
Unit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
/
OtherNumber
(
)),
Unit
>
operator
/
(
OtherNumber
other
)
const
{
operator
/
(
OtherNumber
other
)
const
{
static_assert
(
isIntegral
<
OtherNumber
>
(),
"Divided Quantity by non-integer."
);
return
Quantity
<
decltype
(
Number
()
/
other
),
Unit
>
(
value
/
other
,
unsafe
);
return
Quantity
<
decltype
(
Number
(
1
)
/
other
),
Unit
>
(
value
/
other
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
decltype
(
Number
(
1
)
/
OtherNumber
(
1
))
inline
constexpr
decltype
(
Number
(
)
/
OtherNumber
(
))
operator
/
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
operator
/
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
return
value
/
other
.
value
;
return
value
/
other
.
value
;
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
%
OtherNumber
(
1
)),
Unit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
%
OtherNumber
(
)),
Unit
>
operator
%
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
operator
%
(
const
Quantity
<
OtherNumber
,
Unit
>&
other
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
%
OtherNumber
(
1
)),
Unit
>
(
value
%
other
.
valu
e
);
return
Quantity
<
decltype
(
Number
(
)
%
OtherNumber
()),
Unit
>
(
value
%
other
.
value
,
unsaf
e
);
}
}
template
<
typename
OtherNumber
,
typename
OtherUnit
>
template
<
typename
OtherNumber
,
typename
OtherUnit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
OtherUnit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
OtherUnit
>
operator
*
(
const
UnitRatio
<
OtherNumber
,
OtherUnit
,
Unit
>&
ratio
)
const
{
operator
*
(
const
UnitRatio
<
OtherNumber
,
OtherUnit
,
Unit
>&
ratio
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
*
OtherNumber
(
1
)),
OtherUnit
>
(
return
Quantity
<
decltype
(
Number
(
)
*
OtherNumber
(
)),
OtherUnit
>
(
value
*
ratio
.
unit1PerUnit2
);
value
*
ratio
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
OtherUnit
>
template
<
typename
OtherNumber
,
typename
OtherUnit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
/
OtherNumber
(
1
)),
OtherUnit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
/
OtherNumber
(
)),
OtherUnit
>
operator
/
(
const
UnitRatio
<
OtherNumber
,
Unit
,
OtherUnit
>&
ratio
)
const
{
operator
/
(
const
UnitRatio
<
OtherNumber
,
Unit
,
OtherUnit
>&
ratio
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
/
OtherNumber
(
1
)),
OtherUnit
>
(
return
Quantity
<
decltype
(
Number
(
)
/
OtherNumber
(
)),
OtherUnit
>
(
value
/
ratio
.
unit1PerUnit2
);
value
/
ratio
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
OtherUnit
>
template
<
typename
OtherNumber
,
typename
OtherUnit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
1
)
%
OtherNumber
(
1
)),
Unit
>
inline
constexpr
Quantity
<
decltype
(
Number
(
)
%
OtherNumber
(
)),
Unit
>
operator
%
(
const
UnitRatio
<
OtherNumber
,
Unit
,
OtherUnit
>&
ratio
)
const
{
operator
%
(
const
UnitRatio
<
OtherNumber
,
Unit
,
OtherUnit
>&
ratio
)
const
{
return
Quantity
<
decltype
(
Number
(
1
)
%
OtherNumber
(
1
)),
Unit
>
(
return
Quantity
<
decltype
(
Number
(
)
%
OtherNumber
(
)),
Unit
>
(
value
%
ratio
.
unit1PerUnit2
);
value
%
ratio
.
unit1PerUnit2
,
unsafe
);
}
}
template
<
typename
OtherNumber
,
typename
OtherUnit
>
template
<
typename
OtherNumber
,
typename
OtherUnit
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
1
)
/
OtherNumber
(
1
)),
Unit
,
OtherUnit
>
inline
constexpr
UnitRatio
<
decltype
(
Number
(
)
/
OtherNumber
(
)),
Unit
,
OtherUnit
>
operator
/
(
const
Quantity
<
OtherNumber
,
OtherUnit
>&
other
)
const
{
operator
/
(
const
Quantity
<
OtherNumber
,
OtherUnit
>&
other
)
const
{
return
UnitRatio
<
decltype
(
Number
(
1
)
/
OtherNumber
(
1
)),
Unit
,
OtherUnit
>
(
value
/
other
.
value
);
return
UnitRatio
<
decltype
(
Number
()
/
OtherNumber
()),
Unit
,
OtherUnit
>
(
value
/
other
.
value
,
unsafe
);
}
}
template
<
typename
OtherNumber
>
template
<
typename
OtherNumber
>
...
@@ -347,21 +374,31 @@ private:
...
@@ -347,21 +374,31 @@ private:
template
<
typename
Number1
,
typename
Number2
,
typename
Unit2
>
template
<
typename
Number1
,
typename
Number2
,
typename
Unit2
>
friend
inline
constexpr
auto
operator
*
(
Number1
a
,
Quantity
<
Number2
,
Unit2
>
b
)
friend
inline
constexpr
auto
operator
*
(
Number1
a
,
Quantity
<
Number2
,
Unit2
>
b
)
->
Quantity
<
decltype
(
Number1
(
1
)
*
Number2
(
1
)),
Unit2
>
;
->
Quantity
<
decltype
(
Number1
(
)
*
Number2
(
)),
Unit2
>
;
template
<
typename
T
>
template
<
typename
T
>
friend
inline
constexpr
T
unit
();
friend
inline
constexpr
T
unit
();
};
};
template
<
typename
T
>
struct
Unit_
{
static
inline
constexpr
T
get
()
{
return
T
(
1
);
}
};
template
<
typename
T
,
typename
U
>
struct
Unit_
<
Quantity
<
T
,
U
>>
{
static
inline
constexpr
Quantity
<
decltype
(
Unit_
<
T
>::
get
()),
U
>
get
()
{
return
Quantity
<
decltype
(
Unit_
<
T
>::
get
()),
U
>
(
Unit_
<
T
>::
get
(),
unsafe
);
}
};
template
<
typename
T
>
template
<
typename
T
>
inline
constexpr
T
unit
()
{
return
T
(
1
);
}
inline
constexpr
auto
unit
()
->
decltype
(
Unit_
<
T
>::
get
())
{
return
Unit_
<
T
>::
get
(
);
}
// unit<Quantity<T, U>>() returns a Quantity of value 1. It also, intentionally, works on basic
// unit<Quantity<T, U>>() returns a Quantity of value 1. It also, intentionally, works on basic
// numeric types.
// numeric types.
template
<
typename
Number1
,
typename
Number2
,
typename
Unit
>
template
<
typename
Number1
,
typename
Number2
,
typename
Unit
>
inline
constexpr
auto
operator
*
(
Number1
a
,
Quantity
<
Number2
,
Unit
>
b
)
inline
constexpr
auto
operator
*
(
Number1
a
,
Quantity
<
Number2
,
Unit
>
b
)
->
Quantity
<
decltype
(
Number1
(
1
)
*
Number2
(
1
)),
Unit
>
{
->
Quantity
<
decltype
(
Number1
(
)
*
Number2
(
)),
Unit
>
{
return
Quantity
<
decltype
(
Number1
(
1
)
*
Number2
(
1
)),
Unit
>
(
a
*
b
.
valu
e
);
return
Quantity
<
decltype
(
Number1
(
)
*
Number2
()),
Unit
>
(
a
*
b
.
value
,
unsaf
e
);
}
}
template
<
typename
Number1
,
typename
Number2
,
typename
Unit
,
typename
Unit2
>
template
<
typename
Number1
,
typename
Number2
,
typename
Unit
,
typename
Unit2
>
...
@@ -428,6 +465,592 @@ inline constexpr T origin() { return T(0 * unit<UnitOf<T>>()); }
...
@@ -428,6 +465,592 @@ inline constexpr T origin() { return T(0 * unit<UnitOf<T>>()); }
// origin<Absolute<T, U>>() returns an Absolute of value 0. It also, intentionally, works on basic
// origin<Absolute<T, U>>() returns an Absolute of value 0. It also, intentionally, works on basic
// numeric types.
// numeric types.
// =======================================================================================
// Overflow avoidance
template
<
uint64_t
n
,
uint
accum
=
0
>
struct
BitCount_
{
static
constexpr
uint
value
=
BitCount_
<
(
n
>>
1
),
accum
+
1
>::
value
;
};
template
<
uint
accum
>
struct
BitCount_
<
0
,
accum
>
{
static
constexpr
uint
value
=
accum
;
};
template
<
uint64_t
n
>
inline
constexpr
uint
bitCount
()
{
return
BitCount_
<
n
>::
value
;
}
// Number of bits required to represent the number `n`.
template
<
uint
bitCountBitCount
>
struct
AtLeastUInt_
{
static_assert
(
bitCountBitCount
<
7
,
"don't know how to represent integers over 64 bits"
);
};
template
<>
struct
AtLeastUInt_
<
0
>
{
typedef
uint8_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
1
>
{
typedef
uint8_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
2
>
{
typedef
uint8_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
3
>
{
typedef
uint8_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
4
>
{
typedef
uint16_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
5
>
{
typedef
uint32_t
Type
;
};
template
<>
struct
AtLeastUInt_
<
6
>
{
typedef
uint64_t
Type
;
};
template
<
uint
bits
>
using
AtLeastUInt
=
typename
AtLeastUInt_
<
bitCount
<
max
(
bits
,
1
)
-
1
>
()
>::
Type
;
// AtLeastUInt<n> is an unsigned integer of at least n bits. E.g. AtLeastUInt<12> is uint16_t.
template
<
uint
bits
>
inline
constexpr
uint64_t
maxValueForBits
()
{
// Get the maximum integer representable in the given number of bits.
// 1ull << 64 is unfortunately undefined.
return
(
bits
==
64
?
0
:
(
1ull
<<
bits
))
-
1
;
}
// -------------------------------------------------------------------
template
<
uint
value
>
class
GuardedConst
{
// A constant integer value on which we can do bit size analysis.
public
:
inline
constexpr
uint
unwrap
()
const
{
return
value
;
}
#define OP(op, check) \
template <uint other> \
inline constexpr GuardedConst<(value op other)> \
operator op(GuardedConst<other>) const { \
static_assert(check, "overflow in GuardedConst arithmetic"); \
return GuardedConst<(value op other)>(); \
}
#define COMPARE_OP(op) \
template <uint other> \
inline constexpr bool operator op(GuardedConst<other>) const { \
return value op other; \
}
OP
(
+
,
value
+
other
>=
value
)
OP
(
-
,
value
-
other
<=
value
)
OP
(
*
,
value
*
other
/
other
==
value
)
OP
(
/
,
true
)
// div by zero already errors out; no other division ever overflows
OP
(
%
,
true
)
// mod by zero already errors out; no other modulus ever overflows
OP
(
<<
,
value
<<
other
>=
value
)
OP
(
>>
,
true
)
// right shift can't overflow
OP
(
&
,
true
)
// bitwise ops can't overflow
OP
(
|
,
true
)
// bitwise ops can't overflow
COMPARE_OP
(
==
)
COMPARE_OP
(
!=
)
COMPARE_OP
(
<
)
COMPARE_OP
(
>
)
COMPARE_OP
(
<=
)
COMPARE_OP
(
>=
)
#undef OP
#undef COMPARE_OP
};
template
<
uint64_t
m
,
typename
T
>
struct
Unit_
<
Guarded
<
m
,
T
>>
{
static
inline
constexpr
GuardedConst
<
1
>
get
()
{
return
GuardedConst
<
1
>
();
}
};
template
<
uint
value
>
struct
Unit_
<
GuardedConst
<
value
>>
{
static
inline
constexpr
GuardedConst
<
1
>
get
()
{
return
GuardedConst
<
1
>
();
}
};
template
<
uint
value
>
inline
constexpr
GuardedConst
<
value
>
guarded
()
{
return
GuardedConst
<
value
>
();
}
template
<
uint64_t
a
,
uint64_t
b
>
static
constexpr
uint64_t
guardedAdd
()
{
static_assert
(
a
+
b
>=
a
,
"possible overflow detected"
);
return
a
+
b
;
}
template
<
uint64_t
a
,
uint64_t
b
>
static
constexpr
uint64_t
guardedSub
()
{
static_assert
(
a
-
b
<=
a
,
"possible underflow detected"
);
return
a
-
b
;
}
template
<
uint64_t
a
,
uint64_t
b
>
static
constexpr
uint64_t
guardedMul
()
{
static_assert
(
a
*
b
/
b
==
a
,
"possible overflow detected"
);
return
a
*
b
;
}
template
<
uint64_t
a
,
uint64_t
b
>
static
constexpr
uint64_t
guardedLShift
()
{
static_assert
(
a
<<
b
>=
a
,
"possible overflow detected"
);
return
a
<<
b
;
}
// -------------------------------------------------------------------
template
<
uint64_t
maxN
,
typename
T
>
class
Guarded
{
public
:
static_assert
(
maxN
<=
T
(
kj
::
maxValue
),
"possible overflow detected"
);
Guarded
()
=
default
;
inline
constexpr
Guarded
(
decltype
(
kj
::
maxValue
))
:
value
(
maxN
)
{}
inline
constexpr
Guarded
(
decltype
(
kj
::
minValue
))
:
value
(
0
)
{}
Guarded
(
const
Guarded
&
other
)
=
default
;
template
<
typename
OtherInt
,
typename
=
EnableIf
<
isIntegral
<
OtherInt
>
()
>>
inline
constexpr
Guarded
(
OtherInt
value
)
:
value
(
value
)
{
static_assert
(
OtherInt
(
maxValue
)
<=
maxN
,
"possible overflow detected"
);
}
template
<
uint64_t
otherMax
,
typename
OtherT
>
inline
constexpr
Guarded
(
const
Guarded
<
otherMax
,
OtherT
>&
other
)
:
value
(
other
.
value
)
{
static_assert
(
otherMax
<=
maxN
,
"possible overflow detected"
);
}
template
<
uint
otherValue
>
inline
constexpr
Guarded
(
GuardedConst
<
otherValue
>
)
:
value
(
otherValue
)
{
static_assert
(
otherValue
<=
maxN
,
"overflow detected"
);
}
Guarded
&
operator
=
(
const
Guarded
&
other
)
=
default
;
template
<
typename
OtherInt
,
typename
=
EnableIf
<
isIntegral
<
OtherInt
>
()
>>
Guarded
&
operator
=
(
OtherInt
other
)
{
static_assert
(
OtherInt
(
maxValue
)
<=
maxN
,
"possible overflow detected"
);
value
=
other
;
return
*
this
;
}
template
<
uint64_t
otherMax
,
typename
OtherT
>
inline
Guarded
&
operator
=
(
const
Guarded
<
otherMax
,
OtherT
>&
other
)
{
static_assert
(
otherMax
<=
maxN
,
"possible overflow detected"
);
value
=
other
.
value
;
return
*
this
;
}
template
<
uint
otherValue
>
inline
Guarded
&
operator
=
(
GuardedConst
<
otherValue
>
)
{
static_assert
(
otherValue
<=
maxN
,
"overflow detected"
);
value
=
otherValue
;
return
*
this
;
}
inline
constexpr
T
unwrap
()
const
{
return
value
;
}
#define OP(op, newMax) \
template <uint64_t otherMax, typename otherT> \
inline constexpr Guarded<newMax, decltype(T() op otherT())> \
operator op(const Guarded<otherMax, otherT>& other) const { \
return Guarded<newMax, decltype(T() op otherT())>(value op other.value, unsafe); \
}
#define COMPARE_OP(op) \
template <uint64_t otherMax, typename OtherT> \
inline constexpr bool operator op(const Guarded<otherMax, OtherT>& other) const { \
return value op other.value; \
}
OP
(
+
,
(
guardedAdd
<
maxN
,
otherMax
>
()))
OP
(
*
,
(
guardedMul
<
maxN
,
otherMax
>
()))
OP
(
/
,
maxN
)
OP
(
%
,
otherMax
-
1
)
// operator- is intentionally omitted because we mostly use this with unsigned types, and
// subtraction requires proof that subtrahend is not greater than the minuend.
COMPARE_OP
(
==
)
COMPARE_OP
(
!=
)
COMPARE_OP
(
<
)
COMPARE_OP
(
>
)
COMPARE_OP
(
<=
)
COMPARE_OP
(
>=
)
#undef OP
#undef COMPARE_OP
template
<
uint64_t
newMax
,
typename
ErrorFunc
>
inline
Guarded
<
newMax
,
T
>
assertMax
(
ErrorFunc
&&
func
)
const
{
// Assert that the number is no more than `newMax`. Otherwise, call `func`.
static_assert
(
newMax
<
maxN
,
"this guarded size assertion is redundant"
);
if
(
KJ_UNLIKELY
(
value
>
newMax
))
func
();
return
Guarded
<
newMax
,
T
>
(
value
,
unsafe
);
}
template
<
uint64_t
otherMax
,
typename
OtherT
,
typename
ErrorFunc
>
inline
Guarded
<
maxN
,
decltype
(
T
()
-
OtherT
())
>
subtractChecked
(
const
Guarded
<
otherMax
,
OtherT
>&
other
,
ErrorFunc
&&
func
)
const
{
// Subtract a number, calling func() if the result would underflow.
if
(
KJ_UNLIKELY
(
value
<
other
.
value
))
func
();
return
Guarded
<
maxN
,
decltype
(
T
()
-
OtherT
())
>
(
value
-
other
.
value
,
unsafe
);
}
template
<
uint
otherValue
,
typename
ErrorFunc
>
inline
Guarded
<
maxN
-
otherValue
,
T
>
subtractChecked
(
GuardedConst
<
otherValue
>
,
ErrorFunc
&&
func
)
const
{
// Subtract a number, calling func() if the result would underflow.
static_assert
(
otherValue
<=
maxN
,
"underflow detected"
);
if
(
KJ_UNLIKELY
(
value
<
otherValue
))
func
();
return
Guarded
<
maxN
-
otherValue
,
T
>
(
value
-
otherValue
,
unsafe
);
}
inline
constexpr
Guarded
(
T
value
,
decltype
(
unsafe
))
:
value
(
value
)
{}
template
<
uint64_t
otherMax
,
typename
OtherT
>
inline
constexpr
Guarded
(
Guarded
<
otherMax
,
OtherT
>
value
,
decltype
(
unsafe
))
:
value
(
value
.
value
)
{}
// Mainly for internal use.
//
// Only use these as a last resort, with ample commentary on why you think it's safe.
private
:
T
value
;
template
<
uint64_t
,
typename
>
friend
class
Guarded
;
};
template
<
typename
Number
>
inline
constexpr
Guarded
<
Number
(
kj
::
maxValue
),
Number
>
guarded
(
Number
value
)
{
return
Guarded
<
Number
(
kj
::
maxValue
),
Number
>
(
value
,
unsafe
);
}
inline
constexpr
Guarded
<
1
,
uint8_t
>
guarded
(
bool
value
)
{
return
Guarded
<
1
,
uint8_t
>
(
value
,
unsafe
);
}
template
<
uint
bits
,
typename
Number
>
inline
constexpr
Guarded
<
maxValueForBits
<
bits
>
(),
Number
>
assumeBits
(
Number
value
)
{
return
Guarded
<
maxValueForBits
<
bits
>
(),
Number
>
(
value
,
unsafe
);
}
template
<
uint
bits
,
uint64_t
maxN
,
typename
T
>
inline
constexpr
Guarded
<
maxValueForBits
<
bits
>
(),
T
>
assumeBits
(
Guarded
<
maxN
,
T
>
value
)
{
return
Guarded
<
maxValueForBits
<
bits
>
(),
T
>
(
value
,
unsafe
);
}
template
<
uint
bits
,
typename
Number
,
typename
Unit
>
inline
constexpr
auto
assumeBits
(
Quantity
<
Number
,
Unit
>
value
)
->
Quantity
<
decltype
(
assumeBits
<
bits
>
(
value
/
unit
<
Quantity
<
Number
,
Unit
>>
())),
Unit
>
{
return
Quantity
<
decltype
(
assumeBits
<
bits
>
(
value
/
unit
<
Quantity
<
Number
,
Unit
>>
())),
Unit
>
(
assumeBits
<
bits
>
(
value
/
unit
<
Quantity
<
Number
,
Unit
>>
()),
unsafe
);
}
struct
ThrowOverflow
{
void
operator
()()
const
;
};
template
<
uint64_t
newMax
,
uint64_t
maxN
,
typename
T
,
typename
ErrorFunc
>
inline
constexpr
Guarded
<
newMax
,
T
>
assertMax
(
Guarded
<
maxN
,
T
>
value
,
ErrorFunc
&&
errorFunc
)
{
// Assert that the guarded value is less than or equal to the given maximum, calling errorFunc()
// if not.
static_assert
(
newMax
<
maxN
,
"this guarded size assertion is redundant"
);
return
value
.
template
assertMax
<
newMax
>
(
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
));
}
template
<
uint64_t
newMax
,
uint64_t
maxN
,
typename
T
,
typename
Unit
,
typename
ErrorFunc
>
inline
constexpr
Quantity
<
Guarded
<
newMax
,
T
>
,
Unit
>
assertMax
(
Quantity
<
Guarded
<
maxN
,
T
>
,
Unit
>
value
,
ErrorFunc
&&
errorFunc
)
{
// Assert that the guarded value is less than or equal to the given maximum, calling errorFunc()
// if not.
static_assert
(
newMax
<
maxN
,
"this guarded size assertion is redundant"
);
return
(
value
/
unit
<
decltype
(
value
)
>
()).
template
assertMax
<
newMax
>
(
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
))
*
unit
<
decltype
(
value
)
>
();
}
template
<
uint64_t
newBits
,
uint64_t
maxN
,
typename
T
,
typename
ErrorFunc
=
ThrowOverflow
>
inline
constexpr
Guarded
<
maxValueForBits
<
newBits
>
(),
T
>
assertMaxBits
(
Guarded
<
maxN
,
T
>
value
,
ErrorFunc
&&
errorFunc
=
ErrorFunc
())
{
// Assert that the guarded value requires no more than the given number of bits, calling
// errorFunc() if not.
return
assertMax
<
maxValueForBits
<
newBits
>
()
>
(
value
,
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
));
}
template
<
uint64_t
newBits
,
uint64_t
maxN
,
typename
T
,
typename
Unit
,
typename
ErrorFunc
=
ThrowOverflow
>
inline
constexpr
Quantity
<
Guarded
<
maxValueForBits
<
newBits
>
(),
T
>
,
Unit
>
assertMaxBits
(
Quantity
<
Guarded
<
maxN
,
T
>
,
Unit
>
value
,
ErrorFunc
&&
errorFunc
=
ErrorFunc
())
{
// Assert that the guarded value requires no more than the given number of bits, calling
// errorFunc() if not.
return
assertMax
<
maxValueForBits
<
newBits
>
()
>
(
value
,
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
));
}
template
<
typename
newT
,
uint64_t
maxN
,
typename
T
>
inline
constexpr
Guarded
<
maxN
,
newT
>
upgradeGuard
(
Guarded
<
maxN
,
T
>
value
)
{
return
value
;
}
template
<
typename
newT
,
uint64_t
maxN
,
typename
T
,
typename
Unit
>
inline
constexpr
Quantity
<
Guarded
<
maxN
,
newT
>
,
Unit
>
upgradeGuard
(
Quantity
<
Guarded
<
maxN
,
T
>
,
Unit
>
value
)
{
return
value
;
}
template
<
uint64_t
maxN
,
typename
T
,
typename
Other
,
typename
ErrorFunc
>
inline
auto
subtractChecked
(
Guarded
<
maxN
,
T
>
value
,
Other
other
,
ErrorFunc
&&
errorFunc
)
->
decltype
(
value
.
subtractChecked
(
other
,
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
)))
{
return
value
.
subtractChecked
(
other
,
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
));
}
template
<
typename
T
,
typename
U
,
typename
Unit
,
typename
ErrorFunc
>
inline
auto
subtractChecked
(
Quantity
<
T
,
Unit
>
value
,
Quantity
<
U
,
Unit
>
other
,
ErrorFunc
&&
errorFunc
)
->
Quantity
<
decltype
(
subtractChecked
(
T
(),
U
(),
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
))),
Unit
>
{
return
subtractChecked
(
value
/
unit
<
Quantity
<
T
,
Unit
>>
(),
other
/
unit
<
Quantity
<
U
,
Unit
>>
(),
kj
::
fwd
<
ErrorFunc
>
(
errorFunc
))
*
unit
<
Quantity
<
T
,
Unit
>>
();
}
// -------------------------------------------------------------------
// Operators between Guarded and GuardedConst
#define OP(op, newMax) \
template <uint64_t maxN, uint cvalue, typename T> \
inline constexpr Guarded<(newMax), decltype(T() op uint())> operator op( \
Guarded<maxN, T> value, GuardedConst<cvalue>) { \
return Guarded<(newMax), decltype(T() op uint())>(value.unwrap() op cvalue, unsafe); \
}
#define REVERSE_OP(op, newMax) \
template <uint64_t maxN, uint cvalue, typename T> \
inline constexpr Guarded<(newMax), decltype(uint() op T())> operator op( \
GuardedConst<cvalue>, Guarded<maxN, T> value) { \
return Guarded<(newMax), decltype(uint() op T())>(cvalue op value.unwrap(), unsafe); \
}
#define COMPARE_OP(op) \
template <uint64_t maxN, uint cvalue, typename T> \
inline constexpr bool operator op(Guarded<maxN, T> value, GuardedConst<cvalue>) { \
return value.unwrap() op cvalue; \
} \
template <uint64_t maxN, uint cvalue, typename T> \
inline constexpr bool operator op(GuardedConst<cvalue>, Guarded<maxN, T> value) { \
return cvalue op value.unwrap(); \
}
OP
(
+
,
(
guardedAdd
<
maxN
,
cvalue
>
()))
REVERSE_OP
(
+
,
(
guardedAdd
<
maxN
,
cvalue
>
()))
OP
(
*
,
(
guardedMul
<
maxN
,
cvalue
>
()))
REVERSE_OP
(
*
,
(
guardedAdd
<
maxN
,
cvalue
>
()))
OP
(
/
,
maxN
/
cvalue
)
REVERSE_OP
(
/
,
cvalue
)
// denominator could be 1
OP
(
%
,
cvalue
-
1
)
REVERSE_OP
(
%
,
maxN
-
1
)
OP
(
<<
,
(
guardedLShift
<
maxN
,
cvalue
>
()))
REVERSE_OP
(
<<
,
(
guardedLShift
<
cvalue
,
maxN
>
()))
OP
(
>>
,
maxN
>>
cvalue
)
REVERSE_OP
(
>>
,
cvalue
>>
maxN
)
OP
(
&
,
maxValueForBits
<
bitCount
<
maxN
>
()
>
()
&
cvalue
)
REVERSE_OP
(
&
,
maxValueForBits
<
bitCount
<
maxN
>
()
>
()
&
cvalue
)
OP
(
|
,
maxN
|
cvalue
)
REVERSE_OP
(
|
,
maxN
|
cvalue
)
COMPARE_OP
(
==
)
COMPARE_OP
(
!=
)
COMPARE_OP
(
<
)
COMPARE_OP
(
>
)
COMPARE_OP
(
<=
)
COMPARE_OP
(
>=
)
#undef OP
#undef REVERSE_OP
#undef COMPARE_OP
template
<
uint64_t
maxN
,
uint
cvalue
,
typename
T
>
inline
constexpr
Guarded
<
cvalue
,
decltype
(
uint
()
-
T
())
>
operator
-
(
GuardedConst
<
cvalue
>
,
Guarded
<
maxN
,
T
>
value
)
{
// We allow subtraction of a variable from a constant only if the constant is greater than or
// equal to the maximum possible value of the variable. Since the variable could be zero, the
// result can be as large as the constant.
//
// We do not allow subtraction of a constant from a variable because there's never a guarantee it
// won't underflow (unless the constant is zero, which is silly).
static_assert
(
cvalue
>=
maxN
,
"possible underflow detected"
);
return
Guarded
<
cvalue
,
decltype
(
uint
()
-
T
())
>
(
cvalue
-
value
.
unwrap
(),
unsafe
);
}
// -------------------------------------------------------------------
template
<
uint64_t
maxN
,
typename
T
>
class
SafeUnwrapper
{
public
:
inline
explicit
constexpr
SafeUnwrapper
(
Guarded
<
maxN
,
T
>
value
)
:
value
(
value
.
unwrap
())
{}
template
<
typename
U
,
typename
=
EnableIf
<
isIntegral
<
U
>
()
>>
inline
constexpr
operator
U
()
{
static_assert
(
maxN
<=
U
(
maxValue
),
"possible truncation detected"
);
return
value
;
}
inline
constexpr
operator
bool
()
{
static_assert
(
maxN
<=
1
,
"possible truncation detected"
);
return
value
;
}
private
:
T
value
;
};
template
<
uint64_t
maxN
,
typename
T
>
inline
constexpr
SafeUnwrapper
<
maxN
,
T
>
unguard
(
Guarded
<
maxN
,
T
>
guarded
)
{
// Unwraps the guarded value, returning a value that can be implicitly cast to any integer type.
// If this implicit cast could truncate, a compile-time error will be raised.
return
SafeUnwrapper
<
maxN
,
T
>
(
guarded
);
}
template
<
uint64_t
value
>
class
SafeConstUnwrapper
{
public
:
template
<
typename
T
,
typename
=
EnableIf
<
isIntegral
<
T
>
()
>>
inline
constexpr
operator
T
()
{
static_assert
(
value
<=
T
(
maxValue
),
"this operation will truncate"
);
return
value
;
}
inline
constexpr
operator
bool
()
{
static_assert
(
value
<=
1
,
"this operation will truncate"
);
return
value
;
}
};
template
<
uint
value
>
inline
constexpr
SafeConstUnwrapper
<
value
>
unguard
(
GuardedConst
<
value
>
)
{
return
SafeConstUnwrapper
<
value
>
();
}
template
<
typename
T
,
typename
U
>
inline
constexpr
T
unguardAs
(
U
value
)
{
return
unguard
(
value
);
}
template
<
uint64_t
requestedMax
,
uint64_t
maxN
,
typename
T
>
inline
constexpr
T
unguardMax
(
Guarded
<
maxN
,
T
>
value
)
{
// Explicitly ungaurd expecting a value that is at most `maxN`.
static_assert
(
maxN
<=
requestedMax
,
"possible overflow detected"
);
return
value
.
unwrap
();
}
template
<
uint64_t
requestedMax
,
uint
value
>
inline
constexpr
uint
unguardMax
(
GuardedConst
<
value
>
)
{
// Explicitly ungaurd expecting a value that is at most `maxN`.
static_assert
(
value
<=
requestedMax
,
"overflow detected"
);
return
value
;
}
template
<
uint
bits
,
typename
T
>
inline
constexpr
auto
unguardMaxBits
(
T
value
)
->
decltype
(
unguardMax
<
maxValueForBits
<
bits
>
()
>
(
value
))
{
// Explicitly ungaurd expecting a value that fits into `bits` bits.
return
unguardMax
<
maxValueForBits
<
bits
>
()
>
(
value
);
}
#define OP(op) \
template <uint64_t maxN, typename T, typename U> \
inline constexpr auto operator op(T a, SafeUnwrapper<maxN, U> b) -> decltype(a op (T)b) { \
return a op (AtLeastUInt<sizeof(T)*8>)b; \
} \
template <uint64_t maxN, typename T, typename U> \
inline constexpr auto operator op(SafeUnwrapper<maxN, U> b, T a) -> decltype((T)b op a) { \
return (AtLeastUInt<sizeof(T)*8>)b op a; \
} \
template <uint64_t value, typename T> \
inline constexpr auto operator op(T a, SafeConstUnwrapper<value> b) -> decltype(a op (T)b) { \
return a op (AtLeastUInt<sizeof(T)*8>)b; \
} \
template <uint64_t value, typename T> \
inline constexpr auto operator op(SafeConstUnwrapper<value> b, T a) -> decltype((T)b op a) { \
return (AtLeastUInt<sizeof(T)*8>)b op a; \
}
OP
(
+
)
OP
(
-
)
OP
(
*
)
OP
(
/
)
OP
(
%
)
OP
(
<<
)
OP
(
>>
)
OP
(
&
)
OP
(
|
)
OP
(
==
)
OP
(
!=
)
OP
(
<=
)
OP
(
>=
)
OP
(
<
)
OP
(
>
)
#undef OP
// -------------------------------------------------------------------
template
<
uint64_t
maxN
,
typename
T
>
class
Range
<
Guarded
<
maxN
,
T
>>
{
public
:
inline
constexpr
Range
(
Guarded
<
maxN
,
T
>
begin
,
Guarded
<
maxN
,
T
>
end
)
:
inner
(
unguard
(
begin
),
unguard
(
end
))
{}
inline
explicit
constexpr
Range
(
Guarded
<
maxN
,
T
>
end
)
:
inner
(
unguard
(
end
))
{}
class
Iterator
{
public
:
Iterator
()
=
default
;
inline
explicit
Iterator
(
typename
Range
<
T
>::
Iterator
inner
)
:
inner
(
inner
)
{}
inline
Guarded
<
maxN
,
T
>
operator
*
()
const
{
return
Guarded
<
maxN
,
T
>
(
*
inner
,
unsafe
);
}
inline
Iterator
&
operator
++
()
{
++
inner
;
return
*
this
;
}
inline
bool
operator
==
(
const
Iterator
&
other
)
const
{
return
inner
==
other
.
inner
;
}
inline
bool
operator
!=
(
const
Iterator
&
other
)
const
{
return
inner
!=
other
.
inner
;
}
private
:
typename
Range
<
T
>::
Iterator
inner
;
};
inline
Iterator
begin
()
const
{
return
Iterator
(
inner
.
begin
());
}
inline
Iterator
end
()
const
{
return
Iterator
(
inner
.
end
());
}
private
:
Range
<
T
>
inner
;
};
template
<
typename
T
,
typename
U
>
class
Range
<
Quantity
<
T
,
U
>>
{
public
:
inline
constexpr
Range
(
Quantity
<
T
,
U
>
begin
,
Quantity
<
T
,
U
>
end
)
:
inner
(
begin
/
unit
<
Quantity
<
T
,
U
>>
(),
end
/
unit
<
Quantity
<
T
,
U
>>
())
{}
inline
explicit
constexpr
Range
(
Quantity
<
T
,
U
>
end
)
:
inner
(
end
/
unit
<
Quantity
<
T
,
U
>>
())
{}
class
Iterator
{
public
:
Iterator
()
=
default
;
inline
explicit
Iterator
(
typename
Range
<
T
>::
Iterator
inner
)
:
inner
(
inner
)
{}
inline
Quantity
<
T
,
U
>
operator
*
()
const
{
return
*
inner
*
unit
<
Quantity
<
T
,
U
>>
();
}
inline
Iterator
&
operator
++
()
{
++
inner
;
return
*
this
;
}
inline
bool
operator
==
(
const
Iterator
&
other
)
const
{
return
inner
==
other
.
inner
;
}
inline
bool
operator
!=
(
const
Iterator
&
other
)
const
{
return
inner
!=
other
.
inner
;
}
private
:
typename
Range
<
T
>::
Iterator
inner
;
};
inline
Iterator
begin
()
const
{
return
Iterator
(
inner
.
begin
());
}
inline
Iterator
end
()
const
{
return
Iterator
(
inner
.
end
());
}
private
:
Range
<
T
>
inner
;
};
template
<
uint
value
>
inline
constexpr
Range
<
Guarded
<
value
,
uint
>>
zeroTo
(
GuardedConst
<
value
>
end
)
{
return
Range
<
Guarded
<
value
,
uint
>>
(
end
);
}
template
<
uint
value
,
typename
Unit
>
inline
constexpr
Range
<
Quantity
<
Guarded
<
value
,
uint
>
,
Unit
>>
zeroTo
(
Quantity
<
GuardedConst
<
value
>
,
Unit
>
end
)
{
return
Range
<
Quantity
<
Guarded
<
value
,
uint
>
,
Unit
>>
(
end
);
}
}
// namespace kj
}
// namespace kj
#endif // KJ_UNITS_H_
#endif // KJ_UNITS_H_
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment