| //===- Any.h - Generic type erased holder of any type -----------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file provides Any, a non-template class modeled in the spirit of |
| // std::any. The idea is to provide a type-safe replacement for C's void*. |
| // It can hold a value of any copy-constructible copy-assignable type |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_ADT_ANY_H |
| #define LLVM_ADT_ANY_H |
| |
| #include "llvm/ADT/STLExtras.h" |
| |
| #include <cassert> |
| #include <memory> |
| #include <type_traits> |
| |
| namespace llvm { |
| |
| class Any { |
| template <typename T> struct TypeId { static const char Id; }; |
| |
| struct StorageBase { |
| virtual ~StorageBase() = default; |
| virtual std::unique_ptr<StorageBase> clone() const = 0; |
| virtual const void *id() const = 0; |
| }; |
| |
| template <typename T> struct StorageImpl : public StorageBase { |
| explicit StorageImpl(const T &Value) : Value(Value) {} |
| |
| explicit StorageImpl(T &&Value) : Value(std::move(Value)) {} |
| |
| std::unique_ptr<StorageBase> clone() const override { |
| return std::make_unique<StorageImpl<T>>(Value); |
| } |
| |
| const void *id() const override { return &TypeId<T>::Id; } |
| |
| T Value; |
| |
| private: |
| StorageImpl &operator=(const StorageImpl &Other) = delete; |
| StorageImpl(const StorageImpl &Other) = delete; |
| }; |
| |
| public: |
| Any() = default; |
| |
| Any(const Any &Other) |
| : Storage(Other.Storage ? Other.Storage->clone() : nullptr) {} |
| |
| // When T is Any or T is not copy-constructible we need to explicitly disable |
| // the forwarding constructor so that the copy constructor gets selected |
| // instead. |
| template < |
| typename T, |
| typename std::enable_if< |
| llvm::conjunction< |
| llvm::negation<std::is_same<typename std::decay<T>::type, Any>>, |
| // We also disable this overload when an `Any` object can be |
| // converted to the parameter type because in that case, this |
| // constructor may combine with that conversion during overload |
| // resolution for determining copy constructibility, and then |
| // when we try to determine copy constructibility below we may |
| // infinitely recurse. This is being evaluated by the standards |
| // committee as a potential DR in `std::any` as well, but we're |
| // going ahead and adopting it to work-around usage of `Any` with |
| // types that need to be implicitly convertible from an `Any`. |
| llvm::negation<std::is_convertible<Any, typename std::decay<T>::type>>, |
| std::is_copy_constructible<typename std::decay<T>::type>>::value, |
| int>::type = 0> |
| Any(T &&Value) { |
| using U = typename std::decay<T>::type; |
| Storage = std::make_unique<StorageImpl<U>>(std::forward<T>(Value)); |
| } |
| |
| Any(Any &&Other) : Storage(std::move(Other.Storage)) {} |
| |
| Any &swap(Any &Other) { |
| std::swap(Storage, Other.Storage); |
| return *this; |
| } |
| |
| Any &operator=(Any Other) { |
| Storage = std::move(Other.Storage); |
| return *this; |
| } |
| |
| bool hasValue() const { return !!Storage; } |
| |
| void reset() { Storage.reset(); } |
| |
| private: |
| template <class T> friend T any_cast(const Any &Value); |
| template <class T> friend T any_cast(Any &Value); |
| template <class T> friend T any_cast(Any &&Value); |
| template <class T> friend const T *any_cast(const Any *Value); |
| template <class T> friend T *any_cast(Any *Value); |
| template <typename T> friend bool any_isa(const Any &Value); |
| |
| std::unique_ptr<StorageBase> Storage; |
| }; |
| |
| template <typename T> const char Any::TypeId<T>::Id = 0; |
| |
| |
| template <typename T> bool any_isa(const Any &Value) { |
| if (!Value.Storage) |
| return false; |
| using U = |
| typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
| return Value.Storage->id() == &Any::TypeId<U>::Id; |
| } |
| |
| template <class T> T any_cast(const Any &Value) { |
| using U = |
| typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
| return static_cast<T>(*any_cast<U>(&Value)); |
| } |
| |
| template <class T> T any_cast(Any &Value) { |
| using U = |
| typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
| return static_cast<T>(*any_cast<U>(&Value)); |
| } |
| |
| template <class T> T any_cast(Any &&Value) { |
| using U = |
| typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
| return static_cast<T>(std::move(*any_cast<U>(&Value))); |
| } |
| |
| template <class T> const T *any_cast(const Any *Value) { |
| using U = |
| typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
| assert(Value && any_isa<T>(*Value) && "Bad any cast!"); |
| if (!Value || !any_isa<U>(*Value)) |
| return nullptr; |
| return &static_cast<Any::StorageImpl<U> &>(*Value->Storage).Value; |
| } |
| |
| template <class T> T *any_cast(Any *Value) { |
| using U = typename std::decay<T>::type; |
| assert(Value && any_isa<U>(*Value) && "Bad any cast!"); |
| if (!Value || !any_isa<U>(*Value)) |
| return nullptr; |
| return &static_cast<Any::StorageImpl<U> &>(*Value->Storage).Value; |
| } |
| |
| } // end namespace llvm |
| |
| #endif // LLVM_ADT_ANY_H |