And instead of `boost::any` we can use smaller C++11-compatible
implementation for older compilers (this is a patch for testing, I'm not
sure what will be the right place in 3rdparty to put the header).
Yuriy
From 647535bd9a2c70634a5cdd437f8f603b04b14a70 Mon Sep 17 00:00:00 2001
From: Yuriy Skalko <yuriy.ska...@gmail.com>
Date: Thu, 10 Dec 2020 14:56:46 +0200
Subject: [PATCH] =?UTF-8?q?Use=20C++11-compatible=20`any`=20implementation?=
=?UTF-8?q?=20by=20Denilson=20das=20Merc=C3=AAs=20Amorim?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/support/any.h | 6 +-
src/support/any.hpp | 505 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 508 insertions(+), 3 deletions(-)
create mode 100644 src/support/any.hpp
diff --git a/src/support/any.h b/src/support/any.h
index f246470353..e7daeb9fef 100644
--- a/src/support/any.h
+++ b/src/support/any.h
@@ -23,11 +23,11 @@ using std::any_cast;
#else
-#include <boost/any.hpp>
+#include "any.hpp"
namespace lyx {
-using boost::any;
-using boost::any_cast;
+using linb::any;
+using linb::any_cast;
}
#endif // __cplusplus >= 201703L
diff --git a/src/support/any.hpp b/src/support/any.hpp
new file mode 100644
index 0000000000..f322bb5efa
--- /dev/null
+++ b/src/support/any.hpp
@@ -0,0 +1,505 @@
+//
+// Implementation of N4562 std::experimental::any (merged into C++17) for
C++11 compilers.
+//
+// See also:
+// + http://en.cppreference.com/w/cpp/any
+// + http://en.cppreference.com/w/cpp/experimental/any
+// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
+// + https://cplusplus.github.io/LWG/lwg-active.html#2509
+//
+//
+// Copyright (c) 2016 Denilson das Mercês Amorim
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#ifndef LINB_ANY_HPP
+#define LINB_ANY_HPP
+#pragma once
+#include <typeinfo>
+#include <type_traits>
+#include <stdexcept>
+
+
+#if defined(PARTICLE)
+#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) &&
!defined(ANY_IMPL_EXCEPTIONS)
+# define ANY_IMPL_NO_EXCEPTIONS
+# endif
+#else
+// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS,
+// but you must ensure not to cast badly when passing an `any' object to
any_cast<T>(any)
+#endif
+
+#if defined(PARTICLE)
+#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) &&
!defined(ANY_IMPL_RTTI)
+# define ANY_IMPL_NO_RTTI
+# endif
+#else
+// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI,
+// in order to disable functions working with the typeid of a type
+#endif
+
+
+namespace linb
+{
+
+class bad_any_cast : public std::bad_cast
+{
+public:
+ const char* what() const noexcept override
+ {
+ return "bad any cast";
+ }
+};
+
+class any final
+{
+public:
+ /// Constructs an object of type any with an empty state.
+ any() :
+ vtable(nullptr)
+ {
+ }
+
+ /// Constructs an object of type any with an equivalent state as other.
+ any(const any& rhs) :
+ vtable(rhs.vtable)
+ {
+ if(!rhs.empty())
+ {
+ rhs.vtable->copy(rhs.storage, this->storage);
+ }
+ }
+
+ /// Constructs an object of type any with a state equivalent to the
original state of other.
+ /// rhs is left in a valid but otherwise unspecified state.
+ any(any&& rhs) noexcept :
+ vtable(rhs.vtable)
+ {
+ if(!rhs.empty())
+ {
+ rhs.vtable->move(rhs.storage, this->storage);
+ rhs.vtable = nullptr;
+ }
+ }
+
+ /// Same effect as this->clear().
+ ~any()
+ {
+ this->clear();
+ }
+
+ /// Constructs an object of type any that contains an object of type T
direct-initialized with std::forward<ValueType>(value).
+ ///
+ /// T shall satisfy the CopyConstructible requirements, otherwise the
program is ill-formed.
+ /// This is because an `any` may be copy constructed into another `any` at
any time, so a copy should always be allowed.
+ template<typename ValueType, typename = typename
std::enable_if<!std::is_same<typename std::decay<ValueType>::type,
any>::value>::type>
+ any(ValueType&& value)
+ {
+ static_assert(std::is_copy_constructible<typename
std::decay<ValueType>::type>::value,
+ "T shall satisfy the CopyConstructible requirements.");
+ this->construct(std::forward<ValueType>(value));
+ }
+
+ /// Has the same effect as any(rhs).swap(*this). No effects if an
exception is thrown.
+ any& operator=(const any& rhs)
+ {
+ any(rhs).swap(*this);
+ return *this;
+ }
+
+ /// Has the same effect as any(std::move(rhs)).swap(*this).
+ ///
+ /// The state of *this is equivalent to the original state of rhs and rhs
is left in a valid
+ /// but otherwise unspecified state.
+ any& operator=(any&& rhs) noexcept
+ {
+ any(std::move(rhs)).swap(*this);
+ return *this;
+ }
+
+ /// Has the same effect as
any(std::forward<ValueType>(value)).swap(*this). No effect if a exception is
thrown.
+ ///
+ /// T shall satisfy the CopyConstructible requirements, otherwise the
program is ill-formed.
+ /// This is because an `any` may be copy constructed into another `any` at
any time, so a copy should always be allowed.
+ template<typename ValueType, typename = typename
std::enable_if<!std::is_same<typename std::decay<ValueType>::type,
any>::value>::type>
+ any& operator=(ValueType&& value)
+ {
+ static_assert(std::is_copy_constructible<typename
std::decay<ValueType>::type>::value,
+ "T shall satisfy the CopyConstructible requirements.");
+ any(std::forward<ValueType>(value)).swap(*this);
+ return *this;
+ }
+
+ /// If not empty, destroys the contained object.
+ void clear() noexcept
+ {
+ if(!empty())
+ {
+ this->vtable->destroy(storage);
+ this->vtable = nullptr;
+ }
+ }
+
+ /// Returns true if *this has no contained object, otherwise false.
+ bool empty() const noexcept
+ {
+ return this->vtable == nullptr;
+ }
+
+#ifndef ANY_IMPL_NO_RTTI
+ /// If *this has a contained object of type T, typeid(T); otherwise
typeid(void).
+ const std::type_info& type() const noexcept
+ {
+ return empty()? typeid(void) : this->vtable->type();
+ }
+#endif
+
+ /// Exchange the states of *this and rhs.
+ void swap(any& rhs) noexcept
+ {
+ if(this->vtable != rhs.vtable)
+ {
+ any tmp(std::move(rhs));
+
+ // move from *this to rhs.
+ rhs.vtable = this->vtable;
+ if(this->vtable != nullptr)
+ {
+ this->vtable->move(this->storage, rhs.storage);
+ //this->vtable = nullptr; -- unneeded, see below
+ }
+
+ // move from tmp (previously rhs) to *this.
+ this->vtable = tmp.vtable;
+ if(tmp.vtable != nullptr)
+ {
+ tmp.vtable->move(tmp.storage, this->storage);
+ tmp.vtable = nullptr;
+ }
+ }
+ else // same types
+ {
+ if(this->vtable != nullptr)
+ this->vtable->swap(this->storage, rhs.storage);
+ }
+ }
+
+private: // Storage and Virtual Method Table
+
+ union storage_union
+ {
+ using stack_storage_t = typename std::aligned_storage<2 *
sizeof(void*), std::alignment_of<void*>::value>::type;
+
+ void* dynamic;
+ stack_storage_t stack; // 2 words for e.g. shared_ptr
+ };
+
+ /// Base VTable specification.
+ struct vtable_type
+ {
+ // Note: The caller is responssible for doing .vtable = nullptr after
destructful operations
+ // such as destroy() and/or move().
+
+#ifndef ANY_IMPL_NO_RTTI
+ /// The type of the object this vtable is for.
+ const std::type_info& (*type)() noexcept;
+#endif
+
+ /// Destroys the object in the union.
+ /// The state of the union after this call is unspecified, caller must
ensure not to use src anymore.
+ void(*destroy)(storage_union&) noexcept;
+
+ /// Copies the **inner** content of the src union into the yet
unitialized dest union.
+ /// As such, both inner objects will have the same state, but on
separate memory locations.
+ void(*copy)(const storage_union& src, storage_union& dest);
+
+ /// Moves the storage from src to the yet unitialized dest union.
+ /// The state of src after this call is unspecified, caller must
ensure not to use src anymore.
+ void(*move)(storage_union& src, storage_union& dest) noexcept;
+
+ /// Exchanges the storage between lhs and rhs.
+ void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
+ };
+
+ /// VTable for dynamically allocated storage.
+ template<typename T>
+ struct vtable_dynamic
+ {
+#ifndef ANY_IMPL_NO_RTTI
+ static const std::type_info& type() noexcept
+ {
+ return typeid(T);
+ }
+#endif
+
+ static void destroy(storage_union& storage) noexcept
+ {
+ //assert(reinterpret_cast<T*>(storage.dynamic));
+ delete reinterpret_cast<T*>(storage.dynamic);
+ }
+
+ static void copy(const storage_union& src, storage_union& dest)
+ {
+ dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
+ }
+
+ static void move(storage_union& src, storage_union& dest) noexcept
+ {
+ dest.dynamic = src.dynamic;
+ src.dynamic = nullptr;
+ }
+
+ static void swap(storage_union& lhs, storage_union& rhs) noexcept
+ {
+ // just exchage the storage pointers.
+ std::swap(lhs.dynamic, rhs.dynamic);
+ }
+ };
+
+ /// VTable for stack allocated storage.
+ template<typename T>
+ struct vtable_stack
+ {
+#ifndef ANY_IMPL_NO_RTTI
+ static const std::type_info& type() noexcept
+ {
+ return typeid(T);
+ }
+#endif
+
+ static void destroy(storage_union& storage) noexcept
+ {
+ reinterpret_cast<T*>(&storage.stack)->~T();
+ }
+
+ static void copy(const storage_union& src, storage_union& dest)
+ {
+ new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
+ }
+
+ static void move(storage_union& src, storage_union& dest) noexcept
+ {
+ // one of the conditions for using vtable_stack is a nothrow move
constructor,
+ // so this move constructor will never throw a exception.
+ new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
+ destroy(src);
+ }
+
+ static void swap(storage_union& lhs, storage_union& rhs) noexcept
+ {
+ storage_union tmp_storage;
+ move(rhs, tmp_storage);
+ move(lhs, rhs);
+ move(tmp_storage, lhs);
+ }
+ };
+
+ /// Whether the type T must be dynamically allocated or can be stored on
the stack.
+ template<typename T>
+ struct requires_allocation :
+ std::integral_constant<bool,
+ !(std::is_nothrow_move_constructible<T>::value // N4562
§6.3/3 [any.class]
+ && sizeof(T) <= sizeof(storage_union::stack)
+ && std::alignment_of<T>::value <=
std::alignment_of<storage_union::stack_storage_t>::value)>
+ {};
+
+ /// Returns the pointer to the vtable of the type T.
+ template<typename T>
+ static vtable_type* vtable_for_type()
+ {
+ using VTableType = typename
std::conditional<requires_allocation<T>::value, vtable_dynamic<T>,
vtable_stack<T>>::type;
+ static vtable_type table = {
+#ifndef ANY_IMPL_NO_RTTI
+ VTableType::type,
+#endif
+ VTableType::destroy,
+ VTableType::copy, VTableType::move,
+ VTableType::swap,
+ };
+ return &table;
+ }
+
+protected:
+ template<typename T>
+ friend const T* any_cast(const any* operand) noexcept;
+ template<typename T>
+ friend T* any_cast(any* operand) noexcept;
+
+#ifndef ANY_IMPL_NO_RTTI
+ /// Same effect as is_same(this->type(), t);
+ bool is_typed(const std::type_info& t) const
+ {
+ return is_same(this->type(), t);
+ }
+#endif
+
+#ifndef ANY_IMPL_NO_RTTI
+ /// Checks if two type infos are the same.
+ ///
+ /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address
of the
+ /// type infos, otherwise does an actual comparision. Checking addresses is
+ /// only a valid approach when there's no interaction with outside sources
+ /// (other shared libraries and such).
+ static bool is_same(const std::type_info& a, const std::type_info& b)
+ {
+#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
+ return &a == &b;
+#else
+ return a == b;
+#endif
+ }
+#endif
+
+ /// Casts (with no type_info checks) the storage pointer as const T*.
+ template<typename T>
+ const T* cast() const noexcept
+ {
+ return requires_allocation<typename std::decay<T>::type>::value?
+ reinterpret_cast<const T*>(storage.dynamic) :
+ reinterpret_cast<const T*>(&storage.stack);
+ }
+
+ /// Casts (with no type_info checks) the storage pointer as T*.
+ template<typename T>
+ T* cast() noexcept
+ {
+ return requires_allocation<typename std::decay<T>::type>::value?
+ reinterpret_cast<T*>(storage.dynamic) :
+ reinterpret_cast<T*>(&storage.stack);
+ }
+
+private:
+ storage_union storage; // on offset(0) so no padding for align
+ vtable_type* vtable;
+
+ template<typename ValueType, typename T>
+ typename std::enable_if<requires_allocation<T>::value>::type
+ do_construct(ValueType&& value)
+ {
+ storage.dynamic = new T(std::forward<ValueType>(value));
+ }
+
+ template<typename ValueType, typename T>
+ typename std::enable_if<!requires_allocation<T>::value>::type
+ do_construct(ValueType&& value)
+ {
+ new (&storage.stack) T(std::forward<ValueType>(value));
+ }
+
+ /// Chooses between stack and dynamic allocation for the type
decay_t<ValueType>,
+ /// assigns the correct vtable, and constructs the object on our storage.
+ template<typename ValueType>
+ void construct(ValueType&& value)
+ {
+ using T = typename std::decay<ValueType>::type;
+
+ this->vtable = vtable_for_type<T>();
+
+ do_construct<ValueType,T>(std::forward<ValueType>(value));
+ }
+};
+
+
+
+namespace detail
+{
+ template<typename ValueType>
+ inline ValueType any_cast_move_if_true(typename
std::remove_reference<ValueType>::type* p, std::true_type)
+ {
+ return std::move(*p);
+ }
+
+ template<typename ValueType>
+ inline ValueType any_cast_move_if_true(typename
std::remove_reference<ValueType>::type* p, std::false_type)
+ {
+ return *p;
+ }
+}
+
+/// Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand),
or throws bad_any_cast on failure.
+template<typename ValueType>
+inline ValueType any_cast(const any& operand)
+{
+ auto p = any_cast<typename std::add_const<typename
std::remove_reference<ValueType>::type>::type>(&operand);
+#ifndef ANY_IMPL_NO_EXCEPTIONS
+ if(p == nullptr) throw bad_any_cast();
+#endif
+ return *p;
+}
+
+/// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws
bad_any_cast on failure.
+template<typename ValueType>
+inline ValueType any_cast(any& operand)
+{
+ auto p = any_cast<typename
std::remove_reference<ValueType>::type>(&operand);
+#ifndef ANY_IMPL_NO_EXCEPTIONS
+ if(p == nullptr) throw bad_any_cast();
+#endif
+ return *p;
+}
+
+///
+/// If ValueType is MoveConstructible and isn't a lvalue reference, performs
+/// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise
+/// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on
failure.
+///
+template<typename ValueType>
+inline ValueType any_cast(any&& operand)
+{
+ using can_move = std::integral_constant<bool,
+ std::is_move_constructible<ValueType>::value
+ && !std::is_lvalue_reference<ValueType>::value>;
+
+ auto p = any_cast<typename
std::remove_reference<ValueType>::type>(&operand);
+#ifndef ANY_IMPL_NO_EXCEPTIONS
+ if(p == nullptr) throw bad_any_cast();
+#endif
+ return detail::any_cast_move_if_true<ValueType>(p, can_move());
+}
+
+/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer
to the object
+/// contained by operand, otherwise nullptr.
+template<typename ValueType>
+inline const ValueType* any_cast(const any* operand) noexcept
+{
+ using T = typename std::decay<ValueType>::type;
+
+#ifndef ANY_IMPL_NO_RTTI
+ if (operand && operand->is_typed(typeid(T)))
+#else
+ if (operand && operand->vtable == any::vtable_for_type<T>())
+#endif
+ return operand->cast<ValueType>();
+ else
+ return nullptr;
+}
+
+/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer
to the object
+/// contained by operand, otherwise nullptr.
+template<typename ValueType>
+inline ValueType* any_cast(any* operand) noexcept
+{
+ using T = typename std::decay<ValueType>::type;
+
+#ifndef ANY_IMPL_NO_RTTI
+ if (operand && operand->is_typed(typeid(T)))
+#else
+ if (operand && operand->vtable == any::vtable_for_type<T>())
+#endif
+ return operand->cast<ValueType>();
+ else
+ return nullptr;
+}
+
+}
+
+namespace std
+{
+ inline void swap(linb::any& lhs, linb::any& rhs) noexcept
+ {
+ lhs.swap(rhs);
+ }
+}
+
+#endif
--
2.28.0.windows.1
--
lyx-devel mailing list
lyx-devel@lists.lyx.org
http://lists.lyx.org/mailman/listinfo/lyx-devel