-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils_misc.hpp
115 lines (99 loc) · 3.68 KB
/
utils_misc.hpp
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
#ifndef UTILS_MISC_HPP
#define UTILS_MISC_HPP
#include "utils_compiler.hpp"
#include "utils_traits.hpp"
#include "utils_exceptions.hpp"
#include "utils_print.hpp"
#include <iomanip>
namespace utils::misc {
/** \brief
* Convert the given char* to a variable of type T.
* Use this method instead of the raw C functions: atoi, atof, atol, atoll.
*
* Also check for hex number.
*
* \tparam T
* The type of object to cast to.
* \param buffer
* The character buffer to convert.
* \return
* Returns a variable of type T with the value as given in buffer.
*/
template <class T> ATTR_MAYBE_UNUSED ATTR_NODISCARD
static T lexical_cast(const std::string_view buffer) {
T out;
if (HEDLEY_UNLIKELY(buffer.size() == 0))
throw utils::exceptions::CastingException(std::string(buffer), utils::print::type2name(out));
std::stringstream cast;
if (buffer.size() > 1 && buffer[0] == '0' && buffer[1] != '.') {
if (buffer[1] == 'x' || buffer[1] == 'X') {
// Hex literal
cast << std::hex << buffer;
} else if (buffer[1] == 'b' || buffer[1] == 'B') {
// Binary literal
try {
cast << std::stoll(std::string(buffer.substr(2)), nullptr, 2);
} catch (std::invalid_argument const&) {
throw utils::exceptions::CastingException(std::string(buffer), utils::print::type2name(out));
}
} else {
// Octal literal
cast << std::oct << buffer;
}
} else if (buffer[0] == '#') {
// Hex colour string
cast << std::hex << buffer.substr(1);
} else {
// Decimal/float/...
cast << buffer;
}
if (!(cast >> out))
throw utils::exceptions::CastingException(std::string(buffer), utils::print::type2name(out));
return out;
}
template <class T> ATTR_MAYBE_UNUSED ATTR_NODISCARD
static std::optional<T> try_lexical_cast(const std::string_view buffer, utils::exceptions::Exception *error = nullptr) {
std::optional<T> value;
try {
value = utils::misc::lexical_cast<T>(buffer);
} catch (const utils::exceptions::CastingException& e) {
if (error) *error = e;
value = std::nullopt;
}
return value;
}
/**
* Create a RAII (Resource Acquisition Is Initialization) scoped functor.
* This will call the given function or lambda when the Scoped object
* goes out of scope.
*
* Useful for delayed deletion of objects or restoring settings after the
* scope ends.
*
* e.g.
* {
* auto finally = utils::misc::MakeScoped([]{ std::cout << "done"; });
* // ...
* //~finally() calls the lambda at scope exit.
* }
*
* or use `UTILS_MISC_MAKE_SCOPED([]{})` for a generated unique name.
*/
template<
typename F,
typename = typename std::enable_if_t<utils::traits::is_invocable_v<F>>
> class Scoped {
private:
F callback;
public:
Scoped(F&& callback) : callback(std::move(callback)) {}
~Scoped() { std::invoke<F>(std::forward<F>(this->callback)); }
};
template<typename F> ATTR_MAYBE_UNUSED ATTR_NODISCARD
static inline auto MakeScoped(F&& callback) {
return Scoped<F>(std::forward<F>(callback));
}
#define UTILS_MISC_MAKE_SCOPED(callback) \
auto HEDLEY_CONCAT(scope_exit_, __LINE__) = utils::misc::MakeScoped(callback);
}
#endif // UTILS_MISC_HPP