ampsci
High-precision calculations for one- and two-valence atomic systems
StrongType.hpp
1#pragma once
2#include <iostream>
3#include <type_traits>
4
5//! Collection of handy tools
6namespace qip {
7
8/*!
9@brief A light-weight easy-to-use single-file header-only template class for
10strong typing.
11
12@details
13It works using scoped enums; the enum value (+scope) are used to ensure
14uniqueness for a given user-defined strong type. The class template, defined in
15the qip namespace, takes an enum class value, and a base type. The base type is
16confined to be an arithmetic type (float, double, int, long, etc.) to allow for
17operator overloading. The 'using' declaration is optional, but makes things much
18easier.
19
20```cpp
21using MyType = qip::StrongType<EnumClass::value, BaseType>;
22```
23
24The newly-defined Strong Type will, for the most part, behave just like an
25instance of the underlying base type would. In particular, the usual arithmetic
26operations (+, -, \*, /, +=, \*= etc.) are all defined, and they work with
27iostreams. The main difference is that all implicit conversions are banned.
28
29## Examples
30
31Usage is best shown with examples. Consider this dummy problem, where we define
32strong types for Energy, Mass, and Velocity.
33
34```cpp
35#include "StrongType.hpp"
36enum class MechanicsTypes { energy, mass, velocity };
37// Use of enum class ensures each StrongType is unique
38
39using Energy = qip::StrongType<MechanicsTypes::energy, double>;
40using Mass = qip::StrongType<MechanicsTypes::mass, double>;
41using Velocity = qip::StrongType<MechanicsTypes::velocity, double>;
42```
43*/
44template <auto enumV, typename BaseT>
45struct StrongType {
46private:
47 static_assert(std::is_arithmetic_v<BaseT>,
48 "StrongType only available for arithmetic types");
49 static_assert(
50 std::is_enum_v<decltype(enumV)>,
51 "StrongType must be instantiated with scoped enum (enum class)");
52 using StrongT = StrongType<enumV, BaseT>; // type alias
53
54public:
55 BaseT v;
56
57 explicit constexpr StrongType(BaseT tv) : v(tv) {}
58 explicit constexpr operator BaseT() const { return v; }
59 constexpr BaseT &as_base() { return v; }
60 [[nodiscard]] constexpr BaseT as_base() const { return v; }
61
62 //! makes 'BaseType' publicly accessible
63 using BaseType = BaseT;
64
65 //! Provides operators for regular arithmetic operations
66 constexpr StrongT &operator*=(const StrongT &rhs) {
67 this->v *= rhs.v;
68 return *this;
69 }
70 friend constexpr StrongT operator*(StrongT lhs, const StrongT &rhs) {
71 return lhs *= rhs;
72 }
73 constexpr StrongT &operator/=(const StrongT &rhs) {
74 this->v /= rhs.v;
75 return *this;
76 }
77 friend constexpr StrongT operator/(StrongT lhs, const StrongT &rhs) {
78 return lhs /= rhs;
79 }
80 constexpr StrongT &operator+=(const StrongT &rhs) {
81 this->v += rhs.v;
82 return *this;
83 }
84 friend constexpr StrongT operator+(StrongT lhs, const StrongT &rhs) {
85 return lhs += rhs;
86 }
87 constexpr StrongT &operator-=(const StrongT &rhs) {
88 this->v -= rhs.v;
89 return *this;
90 }
91 friend constexpr StrongT operator-(StrongT lhs, const StrongT &rhs) {
92 return lhs -= rhs;
93 }
94
95 //! Provide Base*Strong, Strong*Base oprators - allow scalar multiplication
96 constexpr StrongT &operator*=(const BaseT &rhs) {
97 this->v *= rhs;
98 return *this;
99 }
100 friend constexpr StrongT operator*(StrongT lhs, const BaseT &rhs) {
101 return lhs *= rhs;
102 }
103 friend constexpr StrongT operator*(const BaseT &lhs, StrongT rhs) {
104 return rhs *= lhs;
105 }
106 //! Provide Strong/Base, but NOT Base/Strong (still scalar multiplication).
107 // If StrongT is used for physical units, this will likely not be what you
108 // want. In this case, just be explicit. Base/Strong is not scalar
109 // multiplication.
110 constexpr StrongT &operator/=(const BaseT &rhs) {
111 this->v /= rhs;
112 return *this;
113 }
114 friend constexpr StrongT operator/(StrongT lhs, const BaseT &rhs) {
115 return lhs /= rhs;
116 }
117
118 //! Provides pre/post increment/decrement (++, --) operators
119 constexpr StrongT &operator++() {
120 ++v;
121 return *this;
122 }
123 constexpr StrongT operator++(int) {
124 StrongT result(*this);
125 ++(*this);
126 return result;
127 }
128 constexpr StrongT &operator--() {
129 --v;
130 return *this;
131 }
132 constexpr StrongT operator--(int) {
133 StrongT result(*this);
134 --(*this);
135 return result;
136 }
137
138 //! Provides comparison operators
139 friend constexpr bool operator==(const StrongT &lhs, const StrongT &rhs) {
140 return lhs.v == rhs.v;
141 }
142 friend constexpr bool operator!=(const StrongT &lhs, const StrongT &rhs) {
143 return !(lhs == rhs);
144 }
145 friend constexpr bool operator<(const StrongT &lhs, const StrongT &rhs) {
146 return lhs.v < rhs.v;
147 }
148 friend constexpr bool operator>(const StrongT &lhs, const StrongT &rhs) {
149 return rhs < lhs;
150 }
151 friend constexpr bool operator<=(const StrongT &lhs, const StrongT &rhs) {
152 return !(rhs < lhs);
153 }
154 friend constexpr bool operator>=(const StrongT &lhs, const StrongT &rhs) {
155 return !(lhs < rhs);
156 }
157
158 //! Provides operators for direct comparison w/ BaseT literal (rvalue).
159 //! Note: Does not allow comparison with BaseT lvalue
160 friend constexpr bool operator==(const StrongT &lhs, const BaseT &&rhs) {
161 return lhs.v == rhs;
162 }
163 friend constexpr bool operator!=(const StrongT &lhs, const BaseT &&rhs) {
164 return lhs.v != rhs;
165 }
166 friend constexpr bool operator<(const StrongT &lhs, const BaseT &&rhs) {
167 return lhs.v < rhs;
168 }
169 friend constexpr bool operator>(const StrongT &lhs, const BaseT &&rhs) {
170 return lhs.v > rhs;
171 }
172 friend constexpr bool operator<=(const StrongT &lhs, const BaseT &&rhs) {
173 return lhs.v <= rhs;
174 }
175 friend constexpr bool operator>=(const StrongT &lhs, const BaseT &&rhs) {
176 return lhs.v >= rhs;
177 }
178 friend constexpr bool operator==(const BaseT &&lhs, const StrongT &rhs) {
179 return lhs == rhs.v;
180 }
181 friend constexpr bool operator!=(const BaseT &&lhs, const StrongT &rhs) {
182 return lhs != rhs.v;
183 }
184 friend constexpr bool operator<(const BaseT &&lhs, const StrongT &rhs) {
185 return lhs < rhs.v;
186 }
187 friend constexpr bool operator>(const BaseT &&lhs, const StrongT &rhs) {
188 return lhs > rhs.v;
189 }
190 friend constexpr bool operator<=(const BaseT &&lhs, const StrongT &rhs) {
191 return lhs <= rhs.v;
192 }
193 friend constexpr bool operator>=(const BaseT &&lhs, const StrongT &rhs) {
194 return lhs >= rhs.v;
195 }
196
197 //! Provides iostream interface, works as it would for BaseT
198 friend std::ostream &operator<<(std::ostream &os, const StrongT &rhs) {
199 return os << rhs.v;
200 }
201 friend std::istream &operator>>(std::istream &is, StrongT &rhs) {
202 return is >> rhs.v;
203 }
204};
205
206} // namespace qip
qip library: A collection of useful functions
Definition Array.hpp:9
A light-weight easy-to-use single-file header-only template class for strong typing.
Definition StrongType.hpp:45
constexpr StrongT & operator*=(const BaseT &rhs)
Provide Base*Strong, Strong*Base oprators - allow scalar multiplication.
Definition StrongType.hpp:96
constexpr StrongT & operator/=(const BaseT &rhs)
Provide Strong/Base, but NOT Base/Strong (still scalar multiplication).
Definition StrongType.hpp:110
constexpr StrongT & operator++()
Provides pre/post increment/decrement (++, –) operators.
Definition StrongType.hpp:119
constexpr StrongT & operator*=(const StrongT &rhs)
Provides operators for regular arithmetic operations.
Definition StrongType.hpp:66
friend std::ostream & operator<<(std::ostream &os, const StrongT &rhs)
Provides iostream interface, works as it would for BaseT.
Definition StrongType.hpp:198
friend constexpr bool operator==(const StrongT &lhs, const BaseT &&rhs)
Provides operators for direct comparison w/ BaseT literal (rvalue). Note: Does not allow comparison w...
Definition StrongType.hpp:160
friend constexpr bool operator==(const StrongT &lhs, const StrongT &rhs)
Provides comparison operators.
Definition StrongType.hpp:139
BaseT BaseType
makes 'BaseType' publicly accessible
Definition StrongType.hpp:63