/*! * Copyright (c) 2016 by Contributors * \file optional.h * \brief Container to hold optional data. */ #ifndef DMLC_OPTIONAL_H_ #define DMLC_OPTIONAL_H_ #include #include #include #include #include "./base.h" #include "./common.h" #include "./logging.h" #include "./type_traits.h" namespace dmlc { /*! \brief dummy type for assign null to optional */ struct nullopt_t { #if defined(_MSC_VER) && _MSC_VER < 1900 /*! \brief dummy constructor */ explicit nullopt_t(int a) {} #else /*! \brief dummy constructor */ constexpr nullopt_t(int a) {} #endif }; /*! Assign null to optional: optional x = nullopt; */ constexpr const nullopt_t nullopt = nullopt_t(0); /*! * \brief c++17 compatible optional class. * * At any time an optional instance either * hold no value (string representation "None") * or hold a value of type T. */ template class optional { public: /*! \brief construct an optional object that contains no value */ optional() : is_none(true) {} /*! \brief construct an optional object with value */ explicit optional(const T& value) { is_none = false; new (&val) T(value); } /*! \brief construct an optional object with another optional object */ optional(const optional& other) { is_none = other.is_none; if (!is_none) { new (&val) T(other.value()); } } /*! \brief deconstructor */ ~optional() { if (!is_none) { reinterpret_cast(&val)->~T(); } } /*! \brief swap two optional */ void swap(optional& other) { std::swap(val, other.val); std::swap(is_none, other.is_none); } /*! \brief set this object to hold value * \param value the value to hold * \return return self to support chain assignment */ optional& operator=(const T& value) { (optional(value)).swap(*this); return *this; } /*! \brief set this object to hold the same value with other * \param other the other object * \return return self to support chain assignment */ optional& operator=(const optional &other) { (optional(other)).swap(*this); return *this; } /*! \brief clear the value this object is holding. * optional x = nullopt; */ optional& operator=(nullopt_t) { (optional()).swap(*this); return *this; } /*! \brief non-const dereference operator */ T& operator*() { // NOLINT(*) return *reinterpret_cast(&val); } /*! \brief const dereference operator */ const T& operator*() const { return *reinterpret_cast(&val); } /*! \brief equal comparison */ bool operator==(const optional& other) const { return this->is_none == other.is_none && (this->is_none == true || this->value() == other.value()); } /*! \brief return the holded value. * throws std::logic_error if holding no value */ const T& value() const { if (is_none) { throw std::logic_error("bad optional access"); } return *reinterpret_cast(&val); } /*! \brief whether this object is holding a value */ explicit operator bool() const { return !is_none; } /*! \brief whether this object is holding a value (alternate form). */ bool has_value() const { return operator bool(); } private: // whether this is none bool is_none; // on stack storage of value typename std::aligned_storage::type val; }; /*! \brief serialize an optional object to string. * * \code * dmlc::optional x; * std::cout << x; // None * x = 0; * std::cout << x; // 0 * \endcode * * \param os output stream * \param t source optional object * \return output stream */ template std::ostream &operator<<(std::ostream &os, const optional &t) { if (t) { os << *t; } else { os << "None"; } return os; } /*! \brief parse a string object into optional * * \code * dmlc::optional x; * std::string s1 = "1"; * std::istringstream is1(s1); * s1 >> x; // x == optional(1) * * std::string s2 = "None"; * std::istringstream is2(s2); * s2 >> x; // x == optional() * \endcode * * \param is input stream * \param t target optional object * \return input stream */ template std::istream &operator>>(std::istream &is, optional &t) { char buf[4]; std::streampos origin = is.tellg(); is.read(buf, 4); if (is.fail() || buf[0] != 'N' || buf[1] != 'o' || buf[2] != 'n' || buf[3] != 'e') { is.clear(); is.seekg(origin); T x; is >> x; t = x; if (std::is_integral::value && !is.eof() && is.peek() == 'L') is.get(); } else { t = nullopt; } return is; } /*! \brief specialization of '>>' istream parsing for optional * * Permits use of generic parameter FieldEntry class to create * FieldEntry> without explicit specialization. * * \code * dmlc::optional x; * std::string s1 = "true"; * std::istringstream is1(s1); * s1 >> x; // x == optional(true) * * std::string s2 = "None"; * std::istringstream is2(s2); * s2 >> x; // x == optional() * \endcode * * \param is input stream * \param t target optional object * \return input stream */ inline std::istream &operator>>(std::istream &is, optional &t) { // Discard initial whitespace while (isspace(is.peek())) is.get(); // Extract chars that might be valid into a separate string, stopping // on whitespace or other non-alphanumerics such as ",)]". std::string s; while (isalnum(is.peek())) s.push_back(is.get()); if (!is.fail()) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); if (s == "1" || s == "true") t = true; else if (s == "0" || s == "false") t = false; else if (s == "none") t = nullopt; else is.setstate(std::ios::failbit); } return is; } /*! \brief description for optional int */ DMLC_DECLARE_TYPE_NAME(optional, "int or None"); /*! \brief description for optional bool */ DMLC_DECLARE_TYPE_NAME(optional, "boolean or None"); /*! \brief description for optional float */ DMLC_DECLARE_TYPE_NAME(optional, "float or None"); /*! \brief description for optional double */ DMLC_DECLARE_TYPE_NAME(optional, "double or None"); } // namespace dmlc namespace std { /*! \brief std hash function for optional */ template struct hash > { /*! * \brief returns hash of the optional value. * \param val value. * \return hash code. */ size_t operator()(const dmlc::optional& val) const { std::hash hash_bool; size_t res = hash_bool(val.has_value()); if (val.has_value()) { res = dmlc::HashCombine(res, val.value()); } return res; } }; } // namespace std #endif // DMLC_OPTIONAL_H_