2022-06-13 12:47:37 +10:00
|
|
|
/*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*
|
|
|
|
* Copyright 2022, Danny Robson <danny@nerdcruft.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
|
|
|
|
namespace cruft {
|
|
|
|
/// A tristate boolean object. ie, contains true, false, and "don't care".
|
|
|
|
///
|
|
|
|
/// This is helpful when folding multiple objects together (eg, in
|
|
|
|
/// configuration files).
|
|
|
|
///
|
|
|
|
/// The logical operators do not operate on the underlying logical value.
|
|
|
|
/// They only serve to overwrite DONTCARE states.
|
|
|
|
/// ie, FALSE | TRUE == FALSE, whereas DONTCARE | TRUE = TRUE.
|
|
|
|
class tribool {
|
|
|
|
public:
|
|
|
|
constexpr tribool (bool _value)
|
|
|
|
: m_value (_value ? value::TRUE : value::FALSE)
|
|
|
|
{ ; }
|
|
|
|
|
|
|
|
constexpr tribool (std::nullptr_t)
|
|
|
|
: m_value (value::DONTCARE)
|
|
|
|
{ ; }
|
|
|
|
|
2022-06-13 14:19:43 +10:00
|
|
|
constexpr tribool ()
|
|
|
|
: tribool (nullptr)
|
|
|
|
{ ; }
|
|
|
|
|
2022-06-13 15:37:24 +10:00
|
|
|
// Try to avoid implicit casts to bool in the constructor.
|
|
|
|
template <typename ValueT>
|
|
|
|
constexpr tribool (ValueT&&) = delete;
|
|
|
|
|
|
|
|
|
2022-06-13 12:47:37 +10:00
|
|
|
/// Return true if the object contains a concrete value
|
|
|
|
constexpr bool has (void) const
|
|
|
|
{
|
|
|
|
return m_value != value::DONTCARE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the concrete value, else throw an exception
|
|
|
|
constexpr bool get (void) const
|
|
|
|
{
|
|
|
|
if (m_value == value::DONTCARE) [[unlikely]]
|
|
|
|
throw std::logic_error ("tribool has not value");
|
|
|
|
return bool (m_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the concrete value, else return the provided default
|
|
|
|
constexpr bool get (bool fallback) const
|
|
|
|
{
|
|
|
|
return m_value == value::DONTCARE ? fallback : bool (m_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If the underlying value is "don't care" then update it with the
|
|
|
|
/// provided value. Otherwise no change.
|
|
|
|
constexpr tribool& ensure (bool fallback)&
|
|
|
|
{
|
|
|
|
if (m_value == value::DONTCARE)
|
|
|
|
m_value = fallback ? value::TRUE : value::FALSE;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr tribool
|
|
|
|
ensure (bool fallback) &&
|
|
|
|
{
|
|
|
|
return tribool (get (fallback));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
constexpr tribool
|
|
|
|
operator| (tribool const &rhs) const noexcept
|
|
|
|
{
|
|
|
|
if (m_value == value::DONTCARE)
|
|
|
|
return rhs;
|
|
|
|
else
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr tribool&
|
|
|
|
operator|= (tribool const &rhs)& noexcept
|
|
|
|
{
|
|
|
|
if (m_value == value::DONTCARE)
|
|
|
|
m_value = rhs.m_value;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool operator== (tribool const&) const noexcept = default;
|
|
|
|
constexpr bool operator!= (tribool const&) const noexcept = default;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Representation is:
|
|
|
|
// -1 == don't care
|
|
|
|
// 0 == false
|
|
|
|
// 1 == true
|
|
|
|
//
|
|
|
|
// This lets us test for validity with -ve, otherwise cast to bool
|
|
|
|
//
|
|
|
|
// Use an `enum class` so that the values aren't implicitly converted
|
|
|
|
// to bool when constructing a tribool (it's caught me out a few times
|
|
|
|
// already).
|
|
|
|
enum class value { DONTCARE = -1, TRUE = 1, FALSE = 0 } m_value;
|
|
|
|
};
|
|
|
|
}
|