mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-03 15:01:16 -03:00
Add maybe_t template class
maybe_t is an implementation of the Maybe/Optional type, allowing for an optional value to be stored. This will enable a more principled approach for functions that return values or failure, such as env_get.
This commit is contained in:
@@ -676,6 +676,7 @@
|
||||
D03238891849D1980032CF2C /* pager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pager.cpp; sourceTree = "<group>"; };
|
||||
D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = "<group>"; };
|
||||
D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = "<group>"; };
|
||||
D043012D1F5350E400942A50 /* maybe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maybe.h; sourceTree = "<group>"; };
|
||||
D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_compile.c; sourceTree = "<group>"; };
|
||||
D04F7F901BA4DCE900B0F227 /* pcre2_config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_config.c; sourceTree = "<group>"; };
|
||||
D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_context.c; sourceTree = "<group>"; };
|
||||
@@ -1206,6 +1207,7 @@
|
||||
D03EE83814DF88B200FC7150 /* lru.h */,
|
||||
D0A0851A13B3ACEE0099B651 /* output.h */,
|
||||
D0A0855113B3ACEE0099B651 /* output.cpp */,
|
||||
D043012D1F5350E400942A50 /* maybe.h */,
|
||||
D032388A1849D1980032CF2C /* pager.h */,
|
||||
D03238891849D1980032CF2C /* pager.cpp */,
|
||||
D0A0851B13B3ACEE0099B651 /* parse_util.h */,
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "io.h"
|
||||
#include "iothread.h"
|
||||
#include "lru.h"
|
||||
#include "maybe.h"
|
||||
#include "pager.h"
|
||||
#include "parse_constants.h"
|
||||
#include "parse_tree.h"
|
||||
@@ -4247,6 +4248,37 @@ static void test_illegal_command_exit_code(void) {
|
||||
popd();
|
||||
}
|
||||
|
||||
void test_maybe() {
|
||||
say(L"Testing maybe_t");
|
||||
do_test(! bool(maybe_t<int>()));
|
||||
maybe_t<int> m(3);
|
||||
do_test(m.has_value());
|
||||
do_test(m.value() == 3);
|
||||
m.reset();
|
||||
do_test(!m.has_value());
|
||||
m = 123;
|
||||
do_test(m.has_value());
|
||||
do_test(m.has_value() && m.value() == 123);
|
||||
m = maybe_t<int>();
|
||||
do_test(!m.has_value());
|
||||
m = maybe_t<int>(64);
|
||||
do_test(m.has_value() && m.value() == 64);
|
||||
m = 123;
|
||||
do_test(m == maybe_t<int>(123));
|
||||
do_test(m != maybe_t<int>());
|
||||
do_test(maybe_t<int>() == none());
|
||||
do_test(!maybe_t<int>(none()).has_value());
|
||||
m = none();
|
||||
do_test(! bool(m));
|
||||
|
||||
maybe_t<std::string> m2("abc");
|
||||
do_test(!m2.missing_or_empty());
|
||||
m2 = "";
|
||||
do_test(m2.missing_or_empty());
|
||||
m2 = none();
|
||||
do_test(m2.missing_or_empty());
|
||||
}
|
||||
|
||||
/// Main test.
|
||||
int main(int argc, char **argv) {
|
||||
UNUSED(argc);
|
||||
@@ -4342,6 +4374,7 @@ int main(int argc, char **argv) {
|
||||
if (should_test_function("history_formats")) history_tests_t::test_history_formats();
|
||||
if (should_test_function("string")) test_string();
|
||||
if (should_test_function("illegal_command_exit_code")) test_illegal_command_exit_code();
|
||||
if (should_test_function("maybe")) test_maybe();
|
||||
// history_tests_t::test_history_speed();
|
||||
|
||||
say(L"Encountered %d errors in low-level tests", err_count);
|
||||
|
||||
112
src/maybe.h
Normal file
112
src/maybe.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef FISH_MAYBE_H
|
||||
#define FISH_MAYBE_H
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
// A none_t is a helper type used to implicitly initialize maybe_t.
|
||||
// Example usage:
|
||||
// maybe_t<int> sqrt(int x) {
|
||||
// if (x < 0) return none();
|
||||
// return (int)sqrt(x);
|
||||
// }
|
||||
enum class none_t { none = 1 };
|
||||
inline constexpr none_t none() { return none_t::none; }
|
||||
|
||||
// Support for a maybe, also known as Optional.
|
||||
// This is a value-type class that stores a value of T in aligned storage.
|
||||
template <typename T>
|
||||
class maybe_t {
|
||||
alignas(T) char storage[sizeof(T)];
|
||||
bool filled = false;
|
||||
|
||||
public:
|
||||
// return whether the receiver contains a value.
|
||||
bool has_value() const { return filled; }
|
||||
|
||||
// bool conversion indicates whether the receiver contains a value.
|
||||
explicit operator bool() const { return filled; }
|
||||
|
||||
// The default constructor constructs a maybe with no value.
|
||||
maybe_t() {}
|
||||
|
||||
// Construct a maybe_t from a none_t
|
||||
/* implicit */ maybe_t(none_t n) { (void)n; }
|
||||
|
||||
// Construct a maybe_t from a value T.
|
||||
/* implicit */ maybe_t(T &&v) : filled(true) { new (storage) T(std::forward<T>(v)); }
|
||||
|
||||
// Construct a maybe_t from a value T.
|
||||
/* implicit */ maybe_t(const T &v) : filled(true) { new (storage) T(v); }
|
||||
|
||||
// Move constructor.
|
||||
/* implicit */ maybe_t(maybe_t &&v) {
|
||||
if (v.filled) {
|
||||
*this = std::move(v.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Access the value.
|
||||
T &value() {
|
||||
assert(filled && "maybe_t does not have a value");
|
||||
return *reinterpret_cast<T *>(storage);
|
||||
}
|
||||
|
||||
const T &value() const {
|
||||
assert(filled && "maybe_t does not have a value");
|
||||
return *reinterpret_cast<const T *>(storage);
|
||||
}
|
||||
|
||||
// Clear the value.
|
||||
void reset() {
|
||||
if (filled) {
|
||||
value().~T();
|
||||
filled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign a new value.
|
||||
maybe_t &operator=(T &&v) {
|
||||
if (filled) {
|
||||
value() = std::move(v);
|
||||
} else {
|
||||
new (storage) T(std::move(v));
|
||||
filled = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
maybe_t &operator=(maybe_t &&v) {
|
||||
if (!v) {
|
||||
reset();
|
||||
} else {
|
||||
*this = std::move(*v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Dereference support.
|
||||
const T *operator->() const { return &value(); }
|
||||
T *operator->() { return &value(); }
|
||||
const T &operator*() const { return value(); }
|
||||
T &operator*() { return value(); }
|
||||
|
||||
// Helper to replace missing_or_empty() on env_var_t.
|
||||
// Uses SFINAE to only introduce this function if T has an empty() type.
|
||||
template<typename S = T>
|
||||
decltype(S().empty(), bool()) missing_or_empty() const {
|
||||
return ! has_value() || value().empty();
|
||||
}
|
||||
|
||||
// Compare values for equality.
|
||||
bool operator==(const maybe_t &rhs) const {
|
||||
if (this->has_value() && rhs.has_value()) return this->value() == rhs.value();
|
||||
return this->has_value() == rhs.has_value();
|
||||
}
|
||||
|
||||
bool operator!=(const maybe_t &rhs) const { return !(*this == rhs); }
|
||||
|
||||
~maybe_t() { reset(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user