Compare commits
4 Commits
97cb57d521
...
2bc6398d3e
Author | SHA1 | Date | |
---|---|---|---|
2bc6398d3e | |||
34c738116c | |||
90bd4a83c0 | |||
ee6387e7b5 |
@ -1,7 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
|
// Parse an unsigned integer and advance *str to point to the first non-digit character after the number.
|
||||||
usize scan_unsigned_integer(const char** str);
|
usize scan_unsigned_integer(const char** str);
|
||||||
|
|
||||||
|
// Parse a signed integer and advance *str to point to the first non-digit character after the number.
|
||||||
isize scan_signed_integer(const char** str);
|
isize scan_signed_integer(const char** str);
|
||||||
|
|
||||||
|
// Parse an unsigned integer, similar to strtoull().
|
||||||
usize parse_unsigned_integer(const char* str, const char** endptr, int base);
|
usize parse_unsigned_integer(const char* str, const char** endptr, int base);
|
||||||
|
|
||||||
|
// Parse a signed integer, similar to strtoll().
|
||||||
isize parse_signed_integer(const char* str, const char** endptr, int base);
|
isize parse_signed_integer(const char* str, const char** endptr, int base);
|
@ -6,34 +6,29 @@
|
|||||||
template <typename T> class Option
|
template <typename T> class Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Option(const T& value)
|
Option(const T& value) : m_has_value(true)
|
||||||
{
|
{
|
||||||
m_storage.store_reference(value);
|
m_storage.store_reference(value);
|
||||||
m_has_value = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Option(T&& value)
|
Option(T&& value) : m_has_value(true)
|
||||||
{
|
{
|
||||||
m_storage.store_moved_reference(move(value));
|
m_storage.store_moved_reference(move(value));
|
||||||
m_has_value = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Option(const Option<T>& other)
|
Option(const Option<T>& other) : m_has_value(other.m_has_value)
|
||||||
{
|
{
|
||||||
m_has_value = other.has_value();
|
|
||||||
if (m_has_value) { m_storage.store_reference(other.m_storage.fetch_reference()); }
|
if (m_has_value) { m_storage.store_reference(other.m_storage.fetch_reference()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Option(Option<T>&& other)
|
Option(Option<T>&& other) : m_has_value(other.m_has_value)
|
||||||
{
|
{
|
||||||
m_has_value = other.has_value();
|
|
||||||
other.m_has_value = false;
|
other.m_has_value = false;
|
||||||
if (m_has_value) { m_storage.store_moved_reference(move(other.m_storage.fetch_reference())); }
|
if (m_has_value) { m_storage.store_moved_reference(move(other.m_storage.fetch_reference())); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Option()
|
Option() : m_has_value(false)
|
||||||
{
|
{
|
||||||
m_has_value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_value() const
|
bool has_value() const
|
||||||
@ -72,6 +67,7 @@ template <typename T> class Option
|
|||||||
if (has_value()) m_storage.destroy();
|
if (has_value()) m_storage.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For compatibility with TRY()
|
||||||
struct ErrorHandle
|
struct ErrorHandle
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -80,12 +76,10 @@ template <typename T> class Option
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Option(ErrorHandle)
|
Option(const ErrorHandle&) : m_has_value(false)
|
||||||
{
|
{
|
||||||
m_has_value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For compatibility with TRY()
|
|
||||||
ErrorHandle release_error()
|
ErrorHandle release_error()
|
||||||
{
|
{
|
||||||
expect(!has_value(), "Option::release_error called on a non-empty Option");
|
expect(!has_value(), "Option::release_error called on a non-empty Option");
|
||||||
|
@ -19,101 +19,73 @@ struct Error
|
|||||||
template <typename T> class Result
|
template <typename T> class Result
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Result(const T& value) : m_value(value)
|
Result(const T& value) : m_value(value), m_has_value(true)
|
||||||
{
|
{
|
||||||
m_has_value = true;
|
|
||||||
m_has_error = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(T&& value) : m_value(move(value))
|
Result(T&& value) : m_value(move(value)), m_has_value(true)
|
||||||
{
|
{
|
||||||
m_has_value = true;
|
|
||||||
m_has_error = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(const Result<T>& other) : m_value(other.m_value)
|
Result(const Result<T>& other) : m_value(other.m_value), m_has_value(other.m_has_value), m_error(other.m_error)
|
||||||
{
|
{
|
||||||
if (!other.m_has_error)
|
|
||||||
{
|
|
||||||
m_has_value = true;
|
|
||||||
m_has_error = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_has_error = true;
|
|
||||||
m_has_value = false;
|
|
||||||
m_error = other.m_error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(Result<T>&& other) : m_value(move(other.m_value))
|
Result(Result<T>&& other) : m_value(move(other.m_value)), m_has_value(other.m_has_value), m_error(other.m_error)
|
||||||
{
|
{
|
||||||
if (!other.m_has_error)
|
other.m_has_value = false;
|
||||||
{
|
|
||||||
m_has_value = true;
|
|
||||||
m_has_error = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_has_error = true;
|
|
||||||
m_has_value = false;
|
|
||||||
m_error = other.m_error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(const Error& err) : m_value()
|
Result(const Error& err) : m_value(), m_has_value(false), m_error(err.error)
|
||||||
{
|
{
|
||||||
m_error = err.error;
|
|
||||||
m_has_error = true;
|
|
||||||
m_has_value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_error()
|
bool has_error() const
|
||||||
{
|
{
|
||||||
return m_has_error;
|
return !m_has_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_value()
|
bool has_value() const
|
||||||
{
|
{
|
||||||
return m_has_value;
|
return m_has_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int error()
|
int error() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::error() called on a Result that holds a value");
|
expect(has_error(), "Result::error() called on a Result that holds a value");
|
||||||
return m_error;
|
return m_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error release_error()
|
Error release_error() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::release_error() called on a Result that holds a value");
|
expect(has_error(), "Result::release_error() called on a Result that holds a value");
|
||||||
return {m_error};
|
return {m_error};
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* error_string()
|
const char* error_string() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::error_string() called on a Result that holds a value");
|
expect(has_error(), "Result::error_string() called on a Result that holds a value");
|
||||||
return ::error_string(m_error);
|
return ::error_string(m_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
T value()
|
T value() const
|
||||||
{
|
{
|
||||||
expect(has_value(), "Result::value() called on a Result that holds an error");
|
expect(has_value(), "Result::value() called on a Result that holds an error");
|
||||||
return m_value.value();
|
return m_value.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
T expect_value(const char* reason)
|
T expect_value(const char* reason) const
|
||||||
{
|
{
|
||||||
expect(has_value(), reason);
|
expect(has_value(), reason);
|
||||||
return m_value.value();
|
return m_value.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
T value_or(const T& other)
|
T value_or(const T& other) const
|
||||||
{
|
{
|
||||||
return m_value.value_or(other);
|
return m_value.value_or(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_set_value(T& ref)
|
bool try_set_value(T& ref) const
|
||||||
{
|
{
|
||||||
return m_value.try_set_value(ref);
|
return m_value.try_set_value(ref);
|
||||||
}
|
}
|
||||||
@ -132,92 +104,84 @@ template <typename T> class Result
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Option<T> m_value;
|
Option<T> m_value;
|
||||||
int m_error;
|
|
||||||
bool m_has_error;
|
|
||||||
bool m_has_value;
|
bool m_has_value;
|
||||||
|
int m_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> class Result<void>
|
template <> class Result<void>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Result()
|
Result() : m_has_error(false)
|
||||||
{
|
{
|
||||||
m_has_error = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(const Result<void>& other)
|
Result(const Result<void>& other) : m_has_error(other.m_has_error), m_error(other.m_error)
|
||||||
{
|
{
|
||||||
m_has_error = other.m_has_error;
|
|
||||||
m_error = other.m_error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(Result<void>&& other)
|
Result(Result<void>&& other) : m_has_error(other.m_has_error), m_error(other.m_error)
|
||||||
{
|
{
|
||||||
m_has_error = other.m_has_error;
|
|
||||||
m_error = other.m_error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(const Error& err)
|
Result(const Error& err) : m_has_error(true), m_error(err.error)
|
||||||
{
|
{
|
||||||
m_error = err.error;
|
|
||||||
m_has_error = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_error()
|
bool has_error() const
|
||||||
{
|
{
|
||||||
return m_has_error;
|
return m_has_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_value()
|
bool has_value() const
|
||||||
{
|
{
|
||||||
return !m_has_error;
|
return !m_has_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int error()
|
int error() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::error() called on a Result that holds a value");
|
expect(has_error(), "Result::error() called on a Result that holds a value");
|
||||||
return m_error;
|
return m_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error release_error()
|
Error release_error() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::release_error() called on a Result that holds a value");
|
expect(has_error(), "Result::release_error() called on a Result that holds a value");
|
||||||
return {m_error};
|
return {m_error};
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* error_string()
|
const char* error_string() const
|
||||||
{
|
{
|
||||||
expect(has_error(), "Result::error_string() called on a Result that holds a value");
|
expect(has_error(), "Result::error_string() called on a Result that holds a value");
|
||||||
return ::error_string(m_error);
|
return ::error_string(m_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void value()
|
void value() const
|
||||||
{
|
{
|
||||||
expect(has_value(), "Result::value() called on a Result that holds an error");
|
expect(has_value(), "Result::value() called on a Result that holds an error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void expect_value(const char* reason)
|
void expect_value(const char* reason) const
|
||||||
{
|
{
|
||||||
expect(has_value(), reason);
|
expect(has_value(), reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void release_value()
|
void release_value() const
|
||||||
{
|
{
|
||||||
expect(has_value(), "Result::release_value() called on a Result that holds an error");
|
expect(has_value(), "Result::release_value() called on a Result that holds an error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void expect_release_value(const char* reason)
|
void expect_release_value(const char* reason) const
|
||||||
{
|
{
|
||||||
expect(has_value(), reason);
|
expect(has_value(), reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_error;
|
|
||||||
bool m_has_error;
|
bool m_has_error;
|
||||||
|
int m_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -228,5 +192,5 @@ template <> class Result<void>
|
|||||||
({ \
|
({ \
|
||||||
auto _expr_rc = (expr); \
|
auto _expr_rc = (expr); \
|
||||||
if (!_expr_rc.has_value()) return _expr_rc.release_error(); \
|
if (!_expr_rc.has_value()) return _expr_rc.release_error(); \
|
||||||
_expr_rc.expect_release_value("sanity check failed: has_error() returned false, yet result has no value"); \
|
_expr_rc.release_value(); \
|
||||||
})
|
})
|
||||||
|
@ -17,12 +17,13 @@ static bool is_valid_digit_for_base(int base, char c)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize parse_unsigned_integer(const char* str, const char** endptr, int base)
|
static usize do_unsigned_parse(const char* str, const char** endptr, int base)
|
||||||
{
|
{
|
||||||
usize val = 0;
|
usize val = 0;
|
||||||
|
|
||||||
while (_isspace(*str)) str++;
|
// 1. If base is zero or 16, the string may then include a "0x" prefix, and the number will be read in base 16;
|
||||||
|
// otherwise, a zero base is taken as 10 (decimal) unless the next character is '0', in which case it is taken as
|
||||||
|
// 8 (octal).
|
||||||
if ((base == 0 || base == 16) && *str == '0')
|
if ((base == 0 || base == 16) && *str == '0')
|
||||||
{
|
{
|
||||||
str++;
|
str++;
|
||||||
@ -37,17 +38,32 @@ usize parse_unsigned_integer(const char* str, const char** endptr, int base)
|
|||||||
else if (base == 0)
|
else if (base == 0)
|
||||||
base = 10;
|
base = 10;
|
||||||
|
|
||||||
|
// 2. The remainder of the string is converted to an unsigned long value in
|
||||||
|
// the obvious manner, stopping at the first character which is not a
|
||||||
|
// valid digit in the given base.
|
||||||
while (is_valid_digit_for_base(base, *str))
|
while (is_valid_digit_for_base(base, *str))
|
||||||
{
|
{
|
||||||
val = ((usize)base * val) + (usize)parse_digit_unchecked(*str);
|
val = ((usize)base * val) + (usize)parse_digit_unchecked(*str);
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. If endptr is not NULL, this function stores the address of the first invalid character in *endptr.
|
||||||
if (endptr) *endptr = str;
|
if (endptr) *endptr = str;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usize parse_unsigned_integer(const char* str, const char** endptr, int base)
|
||||||
|
{
|
||||||
|
// The string may begin with an arbitrary amount of white space (as determined by isspace(3)),
|
||||||
|
while (_isspace(*str)) str++;
|
||||||
|
|
||||||
|
// followed by a single optional '+' or '-' sign.
|
||||||
|
if (*str == '-' || *str == '+') str++;
|
||||||
|
|
||||||
|
return do_unsigned_parse(str, endptr, base);
|
||||||
|
}
|
||||||
|
|
||||||
#define SSIZE_MAX LONG_MAX
|
#define SSIZE_MAX LONG_MAX
|
||||||
#define SSIZE_MIN (-SSIZE_MAX - (isize)1)
|
#define SSIZE_MIN (-SSIZE_MAX - (isize)1)
|
||||||
|
|
||||||
@ -55,16 +71,19 @@ isize parse_signed_integer(const char* str, const char** endptr, int base)
|
|||||||
{
|
{
|
||||||
bool negative = false;
|
bool negative = false;
|
||||||
|
|
||||||
|
// The string may begin with an arbitrary amount of white space (as determined by isspace(3)),
|
||||||
while (_isspace(*str)) str++;
|
while (_isspace(*str)) str++;
|
||||||
|
|
||||||
|
// followed by a single optional '+' or '-' sign.
|
||||||
if (*str == '-' || *str == '+')
|
if (*str == '-' || *str == '+')
|
||||||
{
|
{
|
||||||
if (*str == '-') negative = true;
|
if (*str == '-') negative = true;
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize rc = parse_unsigned_integer(str, endptr, base);
|
usize rc = do_unsigned_parse(str, endptr, base);
|
||||||
|
|
||||||
|
// If an underflow occurs, this function returns SSIZE_MIN. If an overflow occurs, this function returns SSIZE_MAX.
|
||||||
if (rc > SSIZE_MAX) { return negative ? SSIZE_MIN : SSIZE_MAX; }
|
if (rc > SSIZE_MAX) { return negative ? SSIZE_MIN : SSIZE_MAX; }
|
||||||
|
|
||||||
return negative ? -(isize)rc : (isize)rc;
|
return negative ? -(isize)rc : (isize)rc;
|
||||||
|
Loading…
Reference in New Issue
Block a user