/* * 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 */ #pragma once #include #include 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) { ; } constexpr tribool () : tribool (nullptr) { ; } // Try to avoid implicit casts to bool in the constructor. template constexpr tribool (ValueT&&) = delete; /// 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; }; }