any.hpp 4.41 KB
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2017 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#ifndef REALM_UTIL_ANY_HPP
#define REALM_UTIL_ANY_HPP

#include <memory>
#include <stdexcept>
#include <type_traits>
#include <typeinfo>

#include <realm/util/backtrace.hpp>

namespace realm {
namespace util {

using bad_cast = std::bad_cast;

// A naive implementation of C++17's std::any
// This does not perform the small-object optimization or make any particular
// attempt at being performant
class Any final {
public:
    // Constructors

    Any() = default;
    Any(Any&&) noexcept = default;
    ~Any() = default;
    Any& operator=(Any&&) noexcept = default;

    Any(Any const& rhs)
    : m_value(rhs.m_value ? rhs.m_value->copy() : nullptr)
    {
    }

    template<typename T, typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, Any>::value>::type>
    Any(T&& value)
    : m_value(std::make_unique<Value<typename std::decay<T>::type>>(std::forward<T>(value)))
    {
    }

    Any& operator=(Any const& rhs)
    {
        m_value = rhs.m_value ? rhs.m_value->copy() : nullptr;
        return *this;
    }

    template<typename T, typename = typename std::enable_if<!std::is_same<typename std::decay<T>::type, Any>::value>::type>
    Any& operator=(T&& value)
    {
        m_value = std::make_unique<Value<typename std::decay<T>::type>>(std::forward<T>(value));
        return *this;
    }

    // Modifiers

    void reset() noexcept { m_value.reset(); }
    void swap(Any& rhs) noexcept { std::swap(m_value, rhs.m_value); }

    // Observers

    bool has_value() const noexcept { return m_value != nullptr; }
    std::type_info const& type() const noexcept { return m_value ? m_value->type() : typeid(void); }

private:
    struct ValueBase {
        virtual ~ValueBase() noexcept { }
        virtual std::type_info const& type() const noexcept = 0;
        virtual std::unique_ptr<ValueBase> copy() const = 0;
    };
    template<typename T>
    struct Value : ValueBase {
        T value;
        template<typename U> Value(U&& v) : value(std::forward<U>(v)) { }

        std::type_info const& type() const noexcept override { return typeid(T); }
        std::unique_ptr<ValueBase> copy() const override
        {
            return std::make_unique<Value<T>>(value);
        }
    };
    std::unique_ptr<ValueBase> m_value;

    template<typename T>
    friend const T* any_cast(const Any* operand) noexcept;
    template<typename T>
    friend T* any_cast(Any* operand) noexcept;

    template<typename T>
    const T* cast() const noexcept
    {
        return &static_cast<Value<T>*>(m_value.get())->value;
    }

    template<typename T>
    T* cast() noexcept
    {
        return &static_cast<Value<T>*>(m_value.get())->value;
    }
};

template<typename T>
T any_cast(Any const& value)
{
    auto ptr = any_cast<typename std::add_const<typename std::remove_reference<T>::type>::type>(&value);
    if (!ptr)
        throw bad_cast();
    return *ptr;
}

template<typename T>
T any_cast(Any& value)
{
    auto ptr = any_cast<typename std::remove_reference<T>::type>(&value);
    if (!ptr)
        throw bad_cast();
    return *ptr;
}

template<typename T>
T any_cast(Any&& value)
{
    auto ptr = any_cast<typename std::remove_reference<T>::type>(&value);
    if (!ptr)
        throw bad_cast();
    return std::move(*ptr);
}

template<typename T>
T* any_cast(Any* value) noexcept
{
    return value && value->type() == typeid(T) ? value->cast<T>() : nullptr;
}

template<typename T>
const T* any_cast(const Any* value) noexcept
{
    return value && value->type() == typeid(T) ? value->cast<T>() : nullptr;
}
} // namespace util
} // namespace realm

namespace std {
inline void swap(realm::util::Any& lhs, realm::util::Any& rhs) noexcept
{
    lhs.swap(rhs);
}
} // namespace std

#endif // REALM_UTIL_ANY_HPP