Compare commits

..

No commits in common. "2bc6398d3edbe820dd26b1bc1c8360822cee794a" and "97cb57d52157d76b117a607b45fec03ea41c2dcf" have entirely different histories.

4 changed files with 86 additions and 70 deletions

View File

@ -1,14 +1,7 @@
#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);
isize parse_signed_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);

View File

@ -6,29 +6,34 @@
template <typename T> class Option template <typename T> class Option
{ {
public: public:
Option(const T& value) : m_has_value(true) Option(const T& value)
{ {
m_storage.store_reference(value); m_storage.store_reference(value);
m_has_value = true;
} }
Option(T&& value) : m_has_value(true) Option(T&& value)
{ {
m_storage.store_moved_reference(move(value)); m_storage.store_moved_reference(move(value));
m_has_value = true;
} }
Option(const Option<T>& other) : m_has_value(other.m_has_value) Option(const Option<T>& other)
{ {
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) : m_has_value(other.m_has_value) Option(Option<T>&& other)
{ {
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() : m_has_value(false) Option()
{ {
m_has_value = false;
} }
bool has_value() const bool has_value() const
@ -67,7 +72,6 @@ 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:
@ -76,10 +80,12 @@ template <typename T> class Option
} }
}; };
Option(const ErrorHandle&) : m_has_value(false) Option(ErrorHandle)
{ {
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");

View File

@ -19,73 +19,101 @@ struct Error
template <typename T> class Result template <typename T> class Result
{ {
public: public:
Result(const T& value) : m_value(value), m_has_value(true) Result(const T& value) : m_value(value)
{ {
m_has_value = true;
m_has_error = false;
} }
Result(T&& value) : m_value(move(value)), m_has_value(true) Result(T&& value) : m_value(move(value))
{ {
m_has_value = true;
m_has_error = false;
} }
Result(const Result<T>& other) : m_value(other.m_value), m_has_value(other.m_has_value), m_error(other.m_error) Result(const Result<T>& other) : m_value(other.m_value)
{ {
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)), m_has_value(other.m_has_value), m_error(other.m_error) Result(Result<T>&& other) : m_value(move(other.m_value))
{ {
other.m_has_value = false; 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(const Error& err) : m_value(), m_has_value(false), m_error(err.error) Result(const Error& err) : m_value()
{ {
m_error = err.error;
m_has_error = true;
m_has_value = false;
} }
bool has_error() const bool has_error()
{ {
return !m_has_value; return m_has_error;
} }
bool has_value() const bool has_value()
{ {
return m_has_value; return m_has_value;
} }
int error() const int error()
{ {
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() const Error release_error()
{ {
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 const char* error_string()
{ {
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() const T value()
{ {
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) const T expect_value(const char* reason)
{ {
expect(has_value(), reason); expect(has_value(), reason);
return m_value.value(); return m_value.value();
} }
T value_or(const T& other) const T value_or(const T& other)
{ {
return m_value.value_or(other); return m_value.value_or(other);
} }
bool try_set_value(T& ref) const bool try_set_value(T& ref)
{ {
return m_value.try_set_value(ref); return m_value.try_set_value(ref);
} }
@ -104,84 +132,92 @@ template <typename T> class Result
private: private:
Option<T> m_value; Option<T> m_value;
bool m_has_value;
int m_error; int m_error;
bool m_has_error;
bool m_has_value;
}; };
template <> class Result<void> template <> class Result<void>
{ {
public: public:
Result() : m_has_error(false) Result()
{ {
m_has_error = false;
} }
Result(const Result<void>& other) : m_has_error(other.m_has_error), m_error(other.m_error) Result(const Result<void>& other)
{ {
m_has_error = other.m_has_error;
m_error = other.m_error;
} }
Result(Result<void>&& other) : m_has_error(other.m_has_error), m_error(other.m_error) Result(Result<void>&& other)
{ {
m_has_error = other.m_has_error;
m_error = other.m_error;
} }
Result(const Error& err) : m_has_error(true), m_error(err.error) Result(const Error& err)
{ {
m_error = err.error;
m_has_error = true;
} }
bool has_error() const bool has_error()
{ {
return m_has_error; return m_has_error;
} }
bool has_value() const bool has_value()
{ {
return !m_has_error; return !m_has_error;
} }
int error() const int error()
{ {
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() const Error release_error()
{ {
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 const char* error_string()
{ {
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() const void value()
{ {
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) const void expect_value(const char* reason)
{ {
expect(has_value(), reason); expect(has_value(), reason);
return; return;
} }
void release_value() const void release_value()
{ {
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) const void expect_release_value(const char* reason)
{ {
expect(has_value(), reason); expect(has_value(), reason);
return; return;
} }
private: private:
bool m_has_error;
int m_error; int m_error;
bool m_has_error;
}; };
// clang-format off // clang-format off
@ -192,5 +228,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.release_value(); \ _expr_rc.expect_release_value("sanity check failed: has_error() returned false, yet result has no value"); \
}) })

View File

@ -17,13 +17,12 @@ static bool is_valid_digit_for_base(int base, char c)
return true; return true;
} }
static usize do_unsigned_parse(const char* str, const char** endptr, int base) usize parse_unsigned_integer(const char* str, const char** endptr, int base)
{ {
usize val = 0; usize val = 0;
// 1. If base is zero or 16, the string may then include a "0x" prefix, and the number will be read in base 16; while (_isspace(*str)) str++;
// 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++;
@ -38,32 +37,17 @@ static usize do_unsigned_parse(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)
@ -71,19 +55,16 @@ 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 = do_unsigned_parse(str, endptr, base); usize rc = parse_unsigned_integer(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;