You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
5.7 KiB
C++

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_ANY_H
#define SRSLOG_DETAIL_SUPPORT_ANY_H
#include <memory>
#include <type_traits>
namespace srslog {
namespace detail {
/// Tag for in place construction of the any class.
template <typename T>
struct in_place_type_t {
explicit in_place_type_t() = default;
};
/// Type trait to detect if T is a in_place_type_t tag.
template <typename T>
struct is_in_place_type_t : std::false_type {};
template <typename T>
struct is_in_place_type_t<in_place_type_t<T>> : std::true_type {};
/// This is a very minimalist and non compliant implementation of std::any which
/// is included in C++17.
/// From the standard: "The class any describes a type-safe container for single
/// values of any type".
class any
{
public:
//:TODO: Clang 3.8 does not compile when default constructing a const object
// due to DR253. Declare the defaulted constructor out of the class.
any();
/// Disallow copies for simplicity.
any(const any& other) = delete;
any& operator=(const any& other) = delete;
/// Constructs an object of type decayed T with the provided argument.
/// This constructor only participates in overload resolution when the decayed
/// T meets all of the following conditions:
/// a) is not of class any.
/// b) is move constructible.
/// c) is not an specialization of in_place_type_t.
/// Otherwise the rest of special member functions are considered.
template <
typename T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, any>{} &&
std::is_move_constructible<typename std::decay<T>::type>{} &&
!is_in_place_type_t<typename std::decay<T>::type>{},
int>::type = 0>
explicit any(T&& t) :
storage(new storage_impl<typename std::decay<T>::type>(std::forward<T>(t)))
{}
/// Constructs an object of type decayed T directly into the internal storage
/// forwarding the provided arguments.
/// This constructor only participates in overload resolution when the decayed
/// T meets all of the following conditions:
/// a) T(args...) is constructible.
/// Otherwise the rest of special member functions are considered.
template <typename T,
typename... Args,
typename std::enable_if<
std::is_constructible<typename std::decay<T>::type, Args...>{},
int>::type = 0>
explicit any(in_place_type_t<T>, Args&&... args) :
storage(new storage_impl<typename std::decay<T>::type>(
std::forward<Args>(args)...))
{}
any(any&& other) : storage(std::move(other.storage)) {}
any& operator=(any&& other)
{
storage = std::move(other.storage);
return *this;
}
/// Checks whether the object contains a value.
bool has_value() const { return (storage != nullptr); }
/// If not empty, destroys the contained object.
void reset() { storage.reset(); }
/// Swaps the content of two any objects.
void swap(any& other)
{
using std::swap;
swap(storage, other.storage);
}
private:
template <typename T>
friend T* any_cast(any* operand);
template <typename T>
friend const T* any_cast(const any* operand);
/// Type erased interface for type identification purposes.
struct type_interface {
virtual ~type_interface() = default;
virtual const void* type() const = 0;
};
/// Concrete type implementation with data storage.
template <typename T>
struct storage_impl : public type_interface {
template <typename... Args>
explicit storage_impl(Args&&... args) : data(std::forward<Args>(args)...)
{}
storage_impl(const storage_impl& other) = delete;
storage_impl& operator=(const storage_impl& other) = delete;
const void* type() const override { return &type_tag<T>::tag; }
T data;
};
/// Discriminant tag for type identification.
template <typename T>
struct type_tag {
static const char tag;
};
private:
std::unique_ptr<type_interface> storage;
};
//:TODO: declared out of line, see TODO above.
inline any::any() = default;
/// Constructs an any object containing an object of type T, passing the
/// provided arguments to T's constructor.
template <typename T, typename... Args>
inline any make_any(Args&&... args)
{
return any(in_place_type_t<T>{}, std::forward<Args>(args)...);
}
/// When the requested T matches that of the contents of operand, a pointer to
/// the value contained by operand is returned, otherwise returns a nullptr.
/// Non-const overload.
template <typename T>
inline T* any_cast(any* operand)
{
if (!operand || !operand->storage)
return nullptr;
using U =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
if (operand->storage->type() != &any::type_tag<U>::tag)
return nullptr;
return &static_cast<any::storage_impl<U>*>(operand->storage.get())->data;
}
/// When the requested T matches that of the contents of operand, a pointer to
/// the value contained by operand is returned, otherwise returns a nullptr.
/// Const overload.
template <typename T>
inline const T* any_cast(const any* operand)
{
if (!operand || !operand->storage)
return nullptr;
using U =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
if (operand->storage->type() != &any::type_tag<U>::tag)
return nullptr;
return &static_cast<any::storage_impl<U>*>(operand->storage.get())->data;
}
inline void swap(any& lhs, any& rhs)
{
lhs.swap(rhs);
}
template <typename T>
const char any::type_tag<T>::tag = 0;
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_ANY_H