1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*!
21 * \file tvm/runtime/container/optional.h
22 * \brief Runtime Optional container types.
23 */
24#ifndef TVM_RUNTIME_CONTAINER_OPTIONAL_H_
25#define TVM_RUNTIME_CONTAINER_OPTIONAL_H_
26
27#include <utility>
28
29#include "./base.h"
30
31namespace tvm {
32namespace runtime {
33
34/*! \brief Helper to represent nullptr for optional. */
35struct NullOptType {};
36
37/*!
38 * \brief Optional container that to represent to a Nullable variant of T.
39 * \tparam T The original ObjectRef.
40 *
41 * \code
42 *
43 * Optional<String> opt0 = nullptr;
44 * Optional<String> opt1 = String("xyz");
45 * ICHECK(opt0 == nullptr);
46 * ICHECK(opt1 == "xyz");
47 *
48 * \endcode
49 */
50template <typename T>
51class Optional : public ObjectRef {
52 public:
53 using ContainerType = typename T::ContainerType;
54 static_assert(std::is_base_of<ObjectRef, T>::value, "Optional is only defined for ObjectRef.");
55 // default constructors.
56 Optional() = default;
57 Optional(const Optional<T>&) = default;
58 Optional(Optional<T>&&) = default;
59 Optional<T>& operator=(const Optional<T>&) = default;
60 Optional<T>& operator=(Optional<T>&&) = default;
61 /*!
62 * \brief Construct from an ObjectPtr
63 * whose type already matches the ContainerType.
64 * \param ptr
65 */
66 explicit Optional(ObjectPtr<Object> ptr) : ObjectRef(ptr) {}
67 /*! \brief Nullopt handling */
68 Optional(NullOptType) {} // NOLINT(*)
69 // nullptr handling.
70 // disallow implicit conversion as 0 can be implicitly converted to nullptr_t
71 explicit Optional(std::nullptr_t) {}
72 Optional<T>& operator=(std::nullptr_t) {
73 data_ = nullptr;
74 return *this;
75 }
76 // normal value handling.
77 Optional(T other) // NOLINT(*)
78 : ObjectRef(std::move(other)) {}
79 Optional<T>& operator=(T other) {
80 ObjectRef::operator=(std::move(other));
81 return *this;
82 }
83 // delete the int constructor
84 // since Optional<Integer>(0) is ambiguious
85 // 0 can be implicitly casted to nullptr_t
86 explicit Optional(int val) = delete;
87 Optional<T>& operator=(int val) = delete;
88 /*!
89 * \return A not-null container value in the optional.
90 * \note This function performs not-null checking.
91 */
92 T value() const {
93 ICHECK(data_ != nullptr);
94 return T(data_);
95 }
96 /*!
97 * \return The internal object pointer with container type of T.
98 * \note This function do not perform not-null checking.
99 */
100 const ContainerType* get() const { return static_cast<ContainerType*>(data_.get()); }
101 /*!
102 * \return The contained value if the Optional is not null
103 * otherwise return the default_value.
104 */
105 T value_or(T default_value) const { return data_ != nullptr ? T(data_) : default_value; }
106
107 /*! \return Whether the container is not nullptr.*/
108 explicit operator bool() const { return *this != nullptr; }
109 // operator overloadings
110 bool operator==(std::nullptr_t) const { return data_ == nullptr; }
111 bool operator!=(std::nullptr_t) const { return data_ != nullptr; }
112 auto operator==(const Optional<T>& other) const {
113 // support case where sub-class returns a symbolic ref type.
114 using RetType = decltype(value() == other.value());
115 if (same_as(other)) return RetType(true);
116 if (*this != nullptr && other != nullptr) {
117 return value() == other.value();
118 } else {
119 // one of them is nullptr.
120 return RetType(false);
121 }
122 }
123 auto operator!=(const Optional<T>& other) const {
124 // support case where sub-class returns a symbolic ref type.
125 using RetType = decltype(value() != other.value());
126 if (same_as(other)) return RetType(false);
127 if (*this != nullptr && other != nullptr) {
128 return value() != other.value();
129 } else {
130 // one of them is nullptr.
131 return RetType(true);
132 }
133 }
134 auto operator==(const T& other) const {
135 using RetType = decltype(value() == other);
136 if (same_as(other)) return RetType(true);
137 if (*this != nullptr) return value() == other;
138 return RetType(false);
139 }
140 auto operator!=(const T& other) const { return !(*this == other); }
141 template <typename U>
142 auto operator==(const U& other) const {
143 using RetType = decltype(value() == other);
144 if (*this == nullptr) return RetType(false);
145 return value() == other;
146 }
147 template <typename U>
148 auto operator!=(const U& other) const {
149 using RetType = decltype(value() != other);
150 if (*this == nullptr) return RetType(true);
151 return value() != other;
152 }
153 static constexpr bool _type_is_nullable = true;
154};
155
156} // namespace runtime
157
158// expose the functions to the root namespace.
159using runtime::Optional;
160constexpr runtime::NullOptType NullOpt{};
161} // namespace tvm
162
163#endif // TVM_RUNTIME_CONTAINER_OPTIONAL_H_
164