Add base::optional as a wrapper of base::variant.

This commit is contained in:
John Preston 2017-08-14 13:38:23 +03:00
parent ae1dacb7d7
commit bca444b92e
10 changed files with 295 additions and 74 deletions

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "base/optional.h"
namespace base {
template <typename Key, typename Type>
@ -378,7 +380,6 @@ public:
using iterator = typename parent::iterator;
using const_iterator = typename parent::const_iterator;
using value_type = typename parent::value_type;
using reference = typename parent::reference;
iterator insert(const value_type &value) {
if (this->empty() || (value.first < this->front().first)) {
@ -424,19 +425,29 @@ public:
return this->findFirst(key);
}
reference operator[](const Key &key) {
Type &operator[](const Key &key) {
if (this->empty() || (key < this->front().first)) {
this->_impl.push_front(Type());
return this->front();
this->_impl.push_front({ key, Type() });
return this->front().second;
} else if (this->back().first < key) {
this->_impl.push_back(Type());
return this->back();
this->_impl.push_back({ key, Type() });
return this->back().second;
}
auto where = this->getLowerBound(key);
if (key < where->first) {
return *this->_impl.insert(where, { key, Type() });
return this->_impl.insert(where, { key, Type() })->second;
}
return *where;
return where->second;
}
optional<Type> take(const Key &key) {
auto it = find(key);
if (it == this->end()) {
return base::none;
}
auto result = std::move(it->second);
this->erase(it);
return std::move(result);
}
};

View File

@ -0,0 +1,42 @@
/*
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
namespace base {
namespace functors {
struct abs_helper {
template <typename Type,
typename = decltype(0 < std::declval<Type>()),
typename = decltype(-std::declval<Type>())>
constexpr Type operator()(Type value) const {
return (0 < value) ? value : (-value);
}
};
constexpr auto abs = abs_helper {};
template <typename Type>
inline auto add(Type a) {
return [a](auto b) { return a + b; };
};
} // namespace functors
} // namespace base

View File

@ -0,0 +1,214 @@
/*
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 "base/variant.h"
namespace base {
struct none_type {
bool operator==(none_type other) const {
return true;
}
bool operator!=(none_type other) const {
return false;
}
bool operator<(none_type other) const {
return false;
}
bool operator<=(none_type other) const {
return true;
}
bool operator>(none_type other) const {
return false;
}
bool operator>=(none_type other) const {
return true;
}
};
constexpr none_type none = {};
template <typename... Types>
class optional_variant {
public:
optional_variant() : _impl(none) {
}
optional_variant(const optional_variant &other) : _impl(other._impl) {
}
optional_variant(optional_variant &&other) : _impl(std::move(other._impl)) {
}
template <typename T, typename = std::enable_if_t<!std::is_base_of<optional_variant, std::decay_t<T>>::value>>
optional_variant(T &&value) : _impl(std::forward<T>(value)) {
}
optional_variant &operator=(const optional_variant &other) {
_impl = other._impl;
return *this;
}
optional_variant &operator=(optional_variant &&other) {
_impl = std::move(other._impl);
return *this;
}
template <typename T, typename = std::enable_if_t<!std::is_base_of<optional_variant, std::decay_t<T>>::value>>
optional_variant &operator=(T &&value) {
_impl = std::forward<T>(value);
return *this;
}
explicit operator bool() const {
return (get_if<none_type>(&_impl) == nullptr);
}
bool operator==(const optional_variant &other) const {
return _impl == other._impl;
}
bool operator!=(const optional_variant &other) const {
return _impl != other._impl;
}
bool operator<(const optional_variant &other) const {
return _impl < other._impl;
}
bool operator<=(const optional_variant &other) const {
return _impl <= other._impl;
}
bool operator>(const optional_variant &other) const {
return _impl > other._impl;
}
bool operator>=(const optional_variant &other) const {
return _impl >= other._impl;
}
template <typename T>
decltype(auto) is() const {
return _impl.template is<T>();
}
template <typename T>
decltype(auto) get_unchecked() {
return _impl.template get_unchecked<T>();
}
template <typename T>
decltype(auto) get_unchecked() const {
return _impl.template get_unchecked<T>();
}
private:
variant<none_type, Types...> _impl;
};
template <typename T, typename... Types>
inline T *get_if(optional_variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename T, typename... Types>
inline const T *get_if(const optional_variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename Type>
class optional;
template <typename Type>
struct optional_wrap_once {
using type = optional<Type>;
};
template <typename Type>
struct optional_wrap_once<optional<Type>> {
using type = optional<Type>;
};
template <typename Type>
using optional_wrap_once_t = typename optional_wrap_once<std::decay_t<Type>>::type;
template <typename Type>
struct optional_chain_result {
using type = optional_wrap_once_t<Type>;
};
template <>
struct optional_chain_result<void> {
using type = bool;
};
template <typename Type>
using optional_chain_result_t = typename optional_chain_result<Type>::type;
template <typename Type>
class optional : public optional_variant<Type> {
public:
using optional_variant<Type>::optional_variant;
Type &operator*() {
auto result = get_if<Type>(this);
Expects(result != nullptr);
return *result;
}
const Type &operator*() const {
auto result = get_if<Type>(this);
Expects(result != nullptr);
return *result;
}
Type *operator->() {
auto result = get_if<Type>(this);
Expects(result != nullptr);
return result;
}
const Type *operator->() const {
auto result = get_if<Type>(this);
Expects(result != nullptr);
return result;
}
};
template <typename Type>
optional_wrap_once_t<Type> make_optional(Type &&value) {
return optional_wrap_once_t<Type> { std::forward<Type>(value) };
}
template <typename Type, typename Method>
inline auto optional_chain(
const optional<Type> &value,
Method &method,
std::false_type)
-> optional_chain_result_t<decltype(method(*value))> {
return value ? make_optional(method(*value)) : none;
}
template <typename Type, typename Method>
inline auto optional_chain(
const optional<Type> &value,
Method &method,
std::true_type)
-> optional_chain_result_t<decltype(method(*value))> {
return value ? (method(*value), true) : false;
}
template <typename Type, typename Method>
inline auto operator|(const optional<Type> &value, Method method)
-> optional_chain_result_t<decltype(method(*value))> {
using is_void_return = std::is_same<decltype(method(*value)), void>;
return optional_chain(value, method, is_void_return {});
}
} // namespace base

View File

@ -25,21 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
// We use base::variant<> alias and base::get_if() helper while we don't have std::variant<>.
namespace base {
struct null_variant_type {
};
inline constexpr null_variant_type null_variant() {
return null_variant_type {};
}
inline bool operator<(null_variant_type a, null_variant_type b) {
return false;
}
inline bool operator==(null_variant_type a, null_variant_type b) {
return true;
}
template <typename... Types>
using variant = mapbox::util::variant<Types...>;
@ -53,36 +38,4 @@ inline const T *get_if(const variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename... Types>
using optional_variant = variant<null_variant_type, Types...>;
template <typename... Types>
inline bool is_null_variant(const optional_variant<Types...> &variant) {
return get_if<null_variant_type>(&variant) != nullptr;
}
template <typename Type>
using optional = optional_variant<Type>;
using null_optional_type = null_variant_type;
template <typename Type>
inline Type *get_if(optional<Type> *v) {
return (v && v->template is<Type>()) ? &v->template get_unchecked<Type>() : nullptr;
}
template <typename Type>
inline const Type *get_if(const optional<Type> *v) {
return (v && v->template is<Type>()) ? &v->template get_unchecked<Type>() : nullptr;
}
template <typename Type>
inline bool is_null_optional(const optional<Type> &optional) {
return is_null_variant(optional);
}
inline constexpr null_optional_type null_optional() {
return null_optional_type {};
}
} // namespace base

View File

@ -635,7 +635,7 @@ void StickersListWidget::paintFeaturedStickers(Painter &p, QRect clip) {
auto &sets = shownSets();
auto selectedSticker = base::get_if<OverSticker>(&_selected);
auto selectedButton = base::get_if<OverButton>(base::is_null_variant(_pressed) ? &_selected : &_pressed);
auto selectedButton = base::get_if<OverButton>(_pressed ? &_pressed : &_selected);
auto tilly = st::stickerPanPadding;
auto ms = getms();
@ -727,7 +727,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
auto ms = getms();
auto &sets = shownSets();
auto selectedSticker = base::get_if<OverSticker>(&_selected);
auto selectedButton = base::get_if<OverButton>(base::is_null_variant(_pressed) ? &_selected : &_pressed);
auto selectedButton = base::get_if<OverButton>(_pressed ? &_pressed : &_selected);
enumerateSections([this, &p, clip, fromColumn, toColumn, selectedSticker, selectedButton, ms](const SectionInfo &info) {
if (clip.top() >= info.rowsBottom) {
return true;
@ -979,7 +979,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.stop();
auto pressed = _pressed;
setPressed(base::null_variant());
setPressed(base::none);
if (pressed != _selected) {
update();
}
@ -994,7 +994,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
updateSelected();
auto &sets = shownSets();
if (!base::is_null_variant(pressed) && pressed == _selected) {
if (pressed && pressed == _selected) {
if (auto sticker = base::get_if<OverSticker>(&pressed)) {
t_assert(sticker->section >= 0 && sticker->section < sets.size());
auto &set = sets[sticker->section];
@ -1104,8 +1104,8 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
}
void StickersListWidget::clearSelection() {
setPressed(base::null_variant());
setSelected(base::null_variant());
setPressed(base::none);
setSelected(base::none);
update();
}
@ -1394,11 +1394,11 @@ bool StickersListWidget::preventAutoHide() {
}
void StickersListWidget::updateSelected() {
if (!base::is_null_variant(_pressed) && !_previewShown) {
if (_pressed && !_previewShown) {
return;
}
auto newSelected = OverState { base::null_variant() };
auto newSelected = OverState { base::none };
auto p = mapFromGlobal(_lastMousePosition);
if (!rect().contains(p)
|| p.y() < getVisibleTop() || p.y() >= getVisibleBottom()
@ -1487,7 +1487,7 @@ bool StickersListWidget::stickerHasDeleteButton(const Set &set, int index) const
void StickersListWidget::setSelected(OverState newSelected) {
if (_selected != newSelected) {
setCursor(base::is_null_variant(newSelected) ? style::cur_default : style::cur_pointer);
setCursor(newSelected ? style::cur_pointer : style::cur_default);
auto &sets = shownSets();
auto updateSelected = [this, &sets]() {

View File

@ -226,8 +226,8 @@ private:
Footer *_footer = nullptr;
OverState _selected = base::null_variant();
OverState _pressed = base::null_variant();
OverState _selected;
OverState _pressed;
QPoint _lastMousePosition;
Text _megagroupSetAbout;

View File

@ -1174,11 +1174,10 @@ void History::setUnreadMentionsCount(int count) {
}
bool History::addToUnreadMentions(MsgId msgId, AddToOverviewMethod method) {
auto count = base::get_if(&_unreadMentionsCount);
auto allLoaded = count ? (_unreadMentions.size() >= *count) : false;
auto allLoaded = _unreadMentionsCount ? (_unreadMentions.size() >= *_unreadMentionsCount) : false;
if (allLoaded) {
if (method == AddToOverviewNew) {
++*count;
++*_unreadMentionsCount;
_unreadMentions.insert(msgId);
return true;
}
@ -1191,9 +1190,9 @@ bool History::addToUnreadMentions(MsgId msgId, AddToOverviewMethod method) {
void History::eraseFromUnreadMentions(MsgId msgId) {
_unreadMentions.remove(msgId);
if (auto count = base::get_if(&_unreadMentionsCount)) {
if (*count > 0) {
--*count;
if (_unreadMentionsCount) {
if (*_unreadMentionsCount > 0) {
--*_unreadMentionsCount;
}
}
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged);

View File

@ -361,7 +361,7 @@ public:
return _unreadMentions.empty() ? 0 : _unreadMentions.back();
}
int getUnreadMentionsCount(int notLoadedValue = -1) const {
return base::is_null_optional(_unreadMentionsCount) ? notLoadedValue : *base::get_if(&_unreadMentionsCount);
return _unreadMentionsCount ? *_unreadMentionsCount : notLoadedValue;
}
bool hasUnreadMentions() const {
return (getUnreadMentionsCount() > 0);
@ -559,7 +559,7 @@ private:
bool _mute = false;
int _unreadCount = 0;
base::optional<int> _unreadMentionsCount = base::null_optional();
base::optional<int> _unreadMentionsCount;
base::flat_set<MsgId> _unreadMentions;
Dialogs::RowsByLetter _chatListLinks[2];

View File

@ -65,6 +65,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <gsl/gsl>
#include "base/variant.h"
#include "base/optional.h"
#include "base/algorithm.h"
#include "core/basic_types.h"

View File

@ -7,6 +7,7 @@
<(src_loc)/base/observer.h
<(src_loc)/base/ordered_set.h
<(src_loc)/base/openssl_help.h
<(src_loc)/base/optional.h
<(src_loc)/base/parse_helper.cpp
<(src_loc)/base/parse_helper.h
<(src_loc)/base/qthelp_regex.h