tdesktop/Telegram/SourceFiles/base/unique_any.h

244 lines
6.4 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <any>
namespace base {
namespace details {
template <typename Value>
struct moveable_as_copyable_wrap {
moveable_as_copyable_wrap(Value &&other)
: value(std::move(other)) {
}
moveable_as_copyable_wrap &operator=(Value &&other) {
value = std::move(other);
return *this;
}
moveable_as_copyable_wrap(moveable_as_copyable_wrap &&other)
: value(std::move(other.value)) {
}
moveable_as_copyable_wrap(
const moveable_as_copyable_wrap &other) {
Unexpected("Attempt to copy-construct a move-only type.");
}
moveable_as_copyable_wrap &operator=(
moveable_as_copyable_wrap &&other) {
value = std::move(other.value);
return *this;
}
moveable_as_copyable_wrap &operator=(
const moveable_as_copyable_wrap &other) {
Unexpected("Attempt to copy-assign a move-only type.");
}
Value value;
};
template <
typename Value,
typename = std::enable_if_t<
std::is_move_constructible_v<std::decay_t<Value>>
&& !std::is_lvalue_reference_v<Value>>>
auto wrap_moveable_as_copyable(Value &&value) {
return moveable_as_copyable_wrap<Value>(std::move(value));
}
} // namespace details
class unique_any;
template <typename Value>
Value *any_cast(unique_any *value) noexcept;
template <typename Value>
const Value *any_cast(const unique_any *value) noexcept;
class unique_any final {
public:
// Construction and destruction [any.cons]
constexpr unique_any() noexcept {
}
unique_any(const unique_any &other) = delete;
unique_any &operator=(const unique_any &other) = delete;
unique_any(unique_any &&other) noexcept
: _impl(std::move(other._impl)) {
}
unique_any &operator=(unique_any &&other) noexcept {
_impl = std::move(other._impl);
return *this;
}
template <
typename Value,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<Value>, unique_any>>>
unique_any(Value &&other)
: unique_any(
std::forward<Value>(other),
std::is_copy_constructible<std::decay_t<Value>>()) {
}
template <
typename Value,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<Value>, unique_any>>>
unique_any &operator=(Value &&other) {
if constexpr (std::is_copy_constructible_v<std::decay_t<Value>>) {
_impl = std::forward<Value>(other);
} else if constexpr (std::is_move_constructible_v<std::decay_t<Value>>
&& !std::is_lvalue_reference_v<Value>) {
_impl = details::wrap_moveable_as_copyable(std::move(other));
} else {
static_assert(
false_t(Value{}),
"Bad value for base::unique_any.");
}
return *this;
}
template <
typename Value,
typename ...Args,
typename = std::enable_if_t<
std::is_constructible_v<std::decay_t<Value>, Args...>
&& std::is_copy_constructible_v<decay_t<Value>>>>
std::decay_t<Value> &emplace(Args &&...args) {
return _impl.emplace<Value>(std::forward<Args>(args)...);
}
void reset() noexcept {
_impl.reset();
}
void swap(unique_any &other) noexcept {
_impl.swap(other._impl);
}
bool has_value() const noexcept {
return _impl.has_value();
}
// Should check if it is a moveable_only wrap first.
//const std::type_info &type() const noexcept {
// return _impl.type();
//}
private:
template <
typename Value,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<Value>, unique_any>
&& std::is_copy_constructible_v<std::decay_t<Value>>>>
unique_any(Value &&other, std::true_type)
: _impl(std::forward<Value>(other)) {
}
template <
typename Value,
typename = std::enable_if_t<
!std::is_same_v<std::decay_t<Value>, unique_any>
&& !std::is_copy_constructible_v<std::decay_t<Value>>
&& std::is_move_constructible_v<std::decay_t<Value>>
&& !std::is_lvalue_reference_v<Value>>>
unique_any(Value &&other, std::false_type)
: _impl(details::wrap_moveable_as_copyable(std::move(other))) {
}
template <
typename Value,
typename ...Args>
friend unique_any make_any(Args &&...args);
template <typename Value>
friend const Value *any_cast(const unique_any *value) noexcept;
template <typename Value>
friend Value *any_cast(unique_any *value) noexcept;
std::any _impl;
};
inline void swap(unique_any &a, unique_any &b) noexcept {
a.swap(b);
}
template <
typename Value,
typename ...Args>
inline auto make_any(Args &&...args)
-> std::enable_if_t<
std::is_copy_constructible_v<std::decay_t<Value>>,
unique_any> {
return std::make_any<Value>(std::forward<Args>(args)...);
}
template <
typename Value,
typename ...Args>
inline auto make_any(Args &&...args)
-> std::enable_if_t<
!std::is_copy_constructible_v<std::decay_t<Value>>
&& std::is_move_constructible_v<std::decay_t<Value>>,
unique_any> {
return Value(std::forward<Args>(args)...);
}
template <typename Value>
inline Value *any_cast(unique_any *value) noexcept {
if constexpr (std::is_copy_constructible_v<Value>) {
return std::any_cast<Value>(&value->_impl);
} else if constexpr (std::is_move_constructible_v<Value>) {
auto wrap = std::any_cast<
details::moveable_as_copyable_wrap<Value>
>(&value->_impl);
return wrap ? &wrap->value : nullptr;
} else {
static_assert(
false_t(Value{}),
"Bad type for base::any_cast.");
}
}
template <typename Value>
inline const Value *any_cast(const unique_any *value) noexcept {
if constexpr (std::is_copy_constructible_v<Value>) {
return std::any_cast<Value>(&value->_impl);
} else if constexpr (std::is_move_constructible_v<Value>) {
auto wrap = std::any_cast<
details::moveable_as_copyable_wrap<Value>
>(&value->_impl);
return wrap ? &wrap->value : nullptr;
} else {
static_assert(
false_t(Value{}),
"Bad type for base::any_cast.");
}
}
} // namespace base