Files
TheRock/rocm-systems/projects/rocprofiler-systems/external/dyninst/dataflowAPI/rose/util/Optional.h
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

316 lines
10 KiB
C++

// WARNING: Changes to this file must be contributed back to Sawyer or else they will
// be clobbered by the next update from Sawyer. The Sawyer repository is at
// https://github.com/matzke1/sawyer.
#ifndef Sawyer_Optional_H
#define Sawyer_Optional_H
#include "Sawyer.h"
#include <stdexcept>
namespace Sawyer {
/** Represents no value.
*
* An object of type Nothing can be assigned to any @ref Optional object to make it contain nothing.
*
* @code
* Optional<int> opt1;
* Optional<int> opt2(Nothing());
* Optional<int> opt3 = 123;
* opt3 = Nothing();
* @endcode */
class Nothing { // final
public:
bool operator==(const Nothing&) const { return true; }
bool operator!=(const Nothing&) const { return false; }
bool operator>(const Nothing&) const { return false; }
bool operator>=(const Nothing&) const { return true; }
bool operator<(const Nothing&) const { return false; }
bool operator<=(const Nothing&) const { return true; }
};
/** Holds a value or nothing.
*
* This class is similar to dyncompat::optional except simpler in order to avoid problems we were seeing with Microsoft
* compilers.
*
* The stored value type (@ref Value) cannot be a reference type. */
template<typename T>
class Optional {
// Done this way to avoid aliasing warnings from GCC
union SAWYER_MAY_ALIAS MayAlias {
unsigned char data_[sizeof(T)];
} mayAlias_;
bool isEmpty_;
void *address() { return &mayAlias_; }
const void*address() const { return &mayAlias_; }
public:
/** Type of stored value. */
typedef T Value;
/** Default constructs nothing.
*
* Constructs an optional value that points to nothing. The default constructor for @ref Value is not called. */
Optional(): isEmpty_(true) {}
/** Construct from value.
*
* Constructs an optional object that holds a copy of @p v. */
Optional(const Value &v): isEmpty_(false) { // implicit
new (address()) Value(v); // copy constructed in place
}
/** Construct from nothing.
*
* Constructs an optional object that holds nothing. The default constructor for @ref Value is not called. */
Optional(const Nothing&): isEmpty_(true) {}
/** Copy constructor.
*
* Copy constructs an optional so that the new object has the same state (nothing or not) as the source object. If the
* source contains an object (not nothing) then the object is copy constructed into this optional. */
Optional(const Optional &other) {
isEmpty_ = other.isEmpty_;
if (!isEmpty_) {
const Value &otherValue = *other;
new (address()) Value(otherValue);
}
}
/** Destructor.
*
* The destructor invokes the destructor for the value if a value is stored, otherwise it does nothing. */
~Optional() {
if (!isEmpty_) {
Value &thisValue = **this;
thisValue.~Value();
}
}
/** Value assignment.
*
* Assigns the @p value to this optional. If this optional previously contained a value then the @ref Value assignment
* operator is used, otherwise the @ref Value copy constructor is used. */
Optional& operator=(const Value &value) {
if (isEmpty_) {
new (address()) Value(value);
} else {
Value &thisValue = **this;
thisValue = value;
}
isEmpty_ = false;
return *this;
}
/** %Nothing assignment.
*
* Assigns nothing to this optional. If this optional previously contained a value then the value's destructor is
* called. */
Optional& operator=(const Nothing&) {
if (!isEmpty_) {
Value &thisValue = **this;
thisValue.~Value();
}
isEmpty_ = true;
return *this;
}
/** Optional assignment.
*
* Causes this optional to contain nothing or a copy of the @p other value depending on whether @p other contains a value
* or not. If this didn't contain a value but @p other did, then the @ref Value copy constructor is used; if both
* contained a value then the @ref Value assignment operator is used; if this contained a value but @p other doesn't, then
* the @ref Value destructor is called. */
Optional& operator=(const Optional &other) {
if (isEmpty_ && !other.isEmpty_) {
const Value &otherValue = *other;
new (address()) Value(otherValue);
} else if (!isEmpty_) {
if (other.isEmpty_) {
Value &thisValue = **this;
thisValue.~Value();
} else {
Value &thisValue = **this;
const Value &otherValue = *other;
thisValue = otherValue;
}
}
isEmpty_ = other.isEmpty_;
return *this;
}
/** Dereference to obtain value.
*
* If this optional contains a value then a reference to that value is returned. Otherwise an
* <code>std::domain_error</code> is thrown (the value is not in the container's domain).
*
* @{ */
const Value& operator*() const {
return get();
}
Value& operator*() {
return get();
}
const Value& get() const {
if (isEmpty_)
throw std::domain_error("dereferenced nothing");
return *reinterpret_cast<const Value*>(address());
}
Value& get() {
if (isEmpty_)
throw std::domain_error("dereferenced nothing");
return *reinterpret_cast<Value*>(address());
}
/** @} */
/** Obtain a pointer to the value.
*
* If this optional contains a value then a pointer to the value is returned. Otherwise an <code>std::domain_error</code>
* is thrown (the value is not in the container's domain).
*
* @{ */
const Value* operator->() const {
return &get();
}
Value* operator->() {
return &get();
}
/** @} */
/** Obtain value or something else.
*
* Returns a reference to the contained value if it exists, otherwise returns a reference to the argument.
*
* @code
* Object bar = ...;
* Object foo = objects.getOptional(key).orElse(bar);
* Optional<Object> baz = ...;
* std::cerr <<"baz is " <<baz.orElse(bar) <<"\n";
* @endcode
*
* @{ */
const Value& orElse(const Value &dflt) const {
return isEmpty_ ? dflt : **this;
}
const Value& orElse(Value &dflt) {
return isEmpty_ ? dflt : **this;
}
/** @} */
/** Obtain a value or a default.
*
* Returns a copy of the contained value if it exists, otherwise returns a default constructed value.
*
* @code
* Object bar = ...;
* Object foo = objects.getOptional(key).orDefault();
* Optional<Object> baz = ...;
* std::cerr <<"baz is " <<baz.orDefault() <<"\n";
* @endcode
*/
Value orDefault() const {
return isEmpty_ ? Value() : **this;
}
/** Conditionally save a value.
*
* If this optional object has a value then its value is written to @p out and this method returns true; otherwise the
* value of @p out is not changed and this method returns false. This method is convenient to use in conditional
* statements like this:
*
* @code
* unsigned key = ...;
* std::string value;
* IntervalMap<Interval<unsigned>, std::string> imap = ...;
* while (imap.getOptional(key).assignTo(value)) ...
* @endcode
*
* where the alternative would be
*
* @code
* unsigned key = ...;
* IntervalMap<Interval<unsigned>, std::string> imap = ...;
* while (Optional<std::string> opt = imap.getOptional(key)) {
* std::string value = *opt;
* @endcode */
bool assignTo(Value &out) const {
if (isEmpty_) {
return false;
} else {
out = **this;
return true;
}
}
/** Compare two values.
*
* Compares two optionals and returns true if they are both empty or if neither is empty and their values compare equal.
* This method should be used instead of <code>==</code>. The <code>==</code> operator is disabled because it is prone to
* misuse in the presense of implicit conversion to @c bool.
*
* @{ */
bool isEqual(const Optional &other) const {
return (isEmpty_ && other.isEmpty_) || (!isEmpty_ && !other.isEmpty_ && get()==other.get());
}
bool isEqual(const Value &other) const {
return !isEmpty_ && get()==other;
}
bool isEqual(const Nothing&) const {
return isEmpty_;
}
/** @} */
// The following trickery is to allow things like "if (x)" to work but without having an implicit
// conversion to bool which would cause no end of other problems. This is fixed in C++11.
private:
typedef void(Optional::*unspecified_bool)() const;
void this_type_does_not_support_comparisons() const {}
public:
/** Type for Boolean context.
*
* Implicit conversion to a type that can be used in a boolean context such as an <code>if</code> or <code>while</code>
* statement. For instance:
*
* @code
* Optional<int> x = 0;
* if (x) {
* //this is reached
* }
* @endcode */
operator unspecified_bool() const {
return isEmpty_ ? 0 : &Optional::this_type_does_not_support_comparisons;
}
};
// These functions intentionally do not compile. They are to prevent comparisons and thus save users from making
// mistakes like this:
// Optional<int> x = 0;
// int y = 1;
// if (x == y) // won't compile
// if (x && *x == y) // what they really meant
// if (x.isEqual(y)) // another valid way to write it
template<typename T, typename U>
bool operator==(const Optional<T> &lhs, const U &/*rhs*/) {
lhs.this_type_does_not_support_comparisons();
return false;
}
template<typename T, typename U>
bool operator!=(const Optional<T> &lhs, const U &/*rhs*/) {
lhs.this_type_does_not_support_comparisons();
return false;
}
} // namespace
#endif