Skip to content

Commit

Permalink
Rewrite base::lambda_guard, use only one pointer.
Browse files Browse the repository at this point in the history
Support base::enable_weak_from_this for guarding, not only QObject,
in base::lambda_guard, in App::CallDelayed and in App::LambdaDelayed.

Allow only one guarding pointer, no places in code use more than one.
  • Loading branch information
john-preston committed Nov 16, 2017
1 parent 101d4f6 commit f6ed3df
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 130 deletions.
171 changes: 64 additions & 107 deletions Telegram/SourceFiles/base/lambda_guard.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,150 +21,107 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once

#include <QPointer>
#include "base/weak_unique_ptr.h"

namespace base {

// Guard lambda call by one or many QObject* weak pointers.
// Guard lambda call by QObject* or enable_weak_from_this* pointers.

namespace base {
namespace lambda_internal {

template <int N, typename Lambda>
class guard_data {
template <typename Lambda>
class guard_with_QObject {
public:
template <typename ...PointersAndLambda>
inline guard_data(PointersAndLambda&&... qobjectsAndLambda) : _lambda(init(_pointers, std::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
}

inline guard_data(const guard_data &other) : _lambda(other._lambda) {
for (auto i = 0; i != N; ++i) {
_pointers[i] = other._pointers[i];
}
template <typename OtherLambda>
guard_with_QObject(const QObject *object, OtherLambda &&other)
: _guard(object)
, _callable(std::forward<OtherLambda>(other)) {
}

template <
typename ...Args,
typename return_type = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
inline auto operator()(Args&&... args) {
for (int i = 0; i != N; ++i) {
if (!_pointers[i]) {
return return_type();
}
}
return _lambda(std::forward<Args>(args)...);
typename ...OtherArgs,
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
Return operator()(OtherArgs &&...args) {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return();
}

template <
typename ...Args,
typename return_type = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
inline auto operator()(Args&&... args) const {
for (int i = 0; i != N; ++i) {
if (!_pointers[i]) {
return return_type();
}
}
return _lambda(std::forward<Args>(args)...);
typename ...OtherArgs,
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
Return operator()(OtherArgs &&...args) const {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return();
}

private:
template <typename ...PointersAndLambda>
Lambda init(QPointer<QObject> *pointers, QObject *qobject, PointersAndLambda&&... qobjectsAndLambda) {
*pointers = qobject;
return init(++pointers, std::forward<PointersAndLambda>(qobjectsAndLambda)...);
}
Lambda init(QPointer<QObject> *pointers, Lambda &&lambda) {
return std::move(lambda);
}

QPointer<QObject> _pointers[N];
mutable Lambda _lambda;
QPointer<const QObject> _guard;
Lambda _callable;

};

template <int N, typename Lambda>
struct lambda_call_type<guard_data<N, Lambda>> {
using type = lambda_call_type_t<Lambda>;
};

template <int N, typename Lambda>
class guard {
template <typename Lambda>
class guard_with_weak {
public:
template <typename Pointer, typename Other, typename ...PointersAndLambda>
inline guard(Pointer &&qobject, Other &&other, PointersAndLambda&&... qobjectsAndLambda) : _data(std::make_unique<guard_data<N, Lambda>>(std::forward<Pointer>(qobject), std::forward<Other>(other), std::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
static_assert(1 + 1 + sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!");
}

inline guard(const guard &other) : _data(std::make_unique<guard_data<N, Lambda>>(static_cast<const guard_data<N, Lambda> &>(*other._data))) {
}

inline guard(guard &&other) : _data(std::move(other._data)) {
}

inline guard &operator=(const guard &&other) {
_data = std::move(other._data);
return *this;
}

inline guard &operator=(guard &&other) {
_data = std::move(other._data);
return *this;
template <typename OtherLambda>
guard_with_weak(
const base::enable_weak_from_this *object,
OtherLambda &&other)
: _guard(base::make_weak_unique(object))
, _callable(std::forward<OtherLambda>(other)) {
}

template <
typename ...Args,
typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
inline decltype(auto) operator()(Args&&... args) {
return (*_data)(std::forward<Args>(args)...);
typename ...OtherArgs,
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
Return operator()(OtherArgs &&...args) {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return{};
}

template <
typename ...Args,
typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
inline decltype(auto) operator()(Args&&... args) const {
return (*_data)(std::forward<Args>(args)...);
}

bool isNull() const {
return !_data;
typename ...OtherArgs,
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
Return operator()(OtherArgs &&...args) const {
return _guard
? _callable(std::forward<OtherArgs>(args)...)
: Return{};
}

private:
mutable std::unique_ptr<guard_data<N, Lambda>> _data;
base::weak_unique_ptr<const base::enable_weak_from_this> _guard;
Lambda _callable;

};

template <int N, typename Lambda>
struct lambda_call_type<guard<N, Lambda>> {
template <typename Lambda>
struct lambda_call_type<guard_with_QObject<Lambda>> {
using type = lambda_call_type_t<Lambda>;
};

template <int N, int K, typename ...PointersAndLambda>
struct guard_type;

template <int N, int K, typename Pointer, typename ...PointersAndLambda>
struct guard_type<N, K, Pointer, PointersAndLambda...> {
using type = typename guard_type<N, K - 1, PointersAndLambda...>::type;
};

template <int N, typename Lambda>
struct guard_type<N, 0, Lambda> {
using type = guard<N, std::decay_t<Lambda>>;
};

template <typename ...PointersAndLambda>
struct guard_type_helper {
static constexpr int N = sizeof...(PointersAndLambda);
using type = typename guard_type<N - 1, N - 1, PointersAndLambda...>::type;
template <typename Lambda>
struct lambda_call_type<guard_with_weak<Lambda>> {
using type = lambda_call_type_t<Lambda>;
};

template <typename ...PointersAndLambda>
using guard_t = typename guard_type_helper<PointersAndLambda...>::type;

} // namespace lambda_internal

template <typename ...PointersAndLambda>
inline lambda_internal::guard_t<PointersAndLambda...> lambda_guarded(PointersAndLambda&&... qobjectsAndLambda) {
static_assert(sizeof...(PointersAndLambda) > 0, "Lambda should be passed here.");
return lambda_internal::guard_t<PointersAndLambda...>(std::forward<PointersAndLambda>(qobjectsAndLambda)...);
template <typename Lambda>
inline auto lambda_guarded(const QObject *object, Lambda &&lambda) {
using Guarded = lambda_internal::guard_with_QObject<
std::decay_t<Lambda>>;
return Guarded(object, std::forward<Lambda>(lambda));
}

template <typename Lambda>
inline auto lambda_guarded(
const base::enable_weak_from_this *object,
Lambda &&lambda) {
using Guarded = lambda_internal::guard_with_weak<
std::decay_t<Lambda>>;
return Guarded(object, std::forward<Lambda>(lambda));
}

} // namespace base
20 changes: 14 additions & 6 deletions Telegram/SourceFiles/base/weak_unique_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,36 @@ class enable_weak_from_this {
template <typename Child>
friend class weak_unique_ptr;

std::shared_ptr<enable_weak_from_this*> getGuarded() {
std::shared_ptr<enable_weak_from_this*> getGuarded() const {
if (!_guarded) {
_guarded = std::make_shared<enable_weak_from_this*>(static_cast<enable_weak_from_this*>(this));
_guarded = std::make_shared<enable_weak_from_this*>(
const_cast<enable_weak_from_this*>(
static_cast<const enable_weak_from_this*>(this)));
}
return _guarded;
}

std::shared_ptr<enable_weak_from_this*> _guarded;
mutable std::shared_ptr<enable_weak_from_this*> _guarded;

};

template <typename T>
class weak_unique_ptr {
public:
weak_unique_ptr() = default;
weak_unique_ptr(T *value) : _guarded(value ? value->getGuarded() : std::shared_ptr<enable_weak_from_this*>()) {
weak_unique_ptr(T *value)
: _guarded(value
? value->getGuarded()
: std::shared_ptr<enable_weak_from_this*>()) {
}
weak_unique_ptr(const std::unique_ptr<T> &value) : weak_unique_ptr(value.get()) {
weak_unique_ptr(const std::unique_ptr<T> &value)
: weak_unique_ptr(value.get()) {
}

weak_unique_ptr &operator=(T *value) {
_guarded = value ? value->getGuarded() : std::shared_ptr<enable_weak_from_this*>();
_guarded = value
? value->getGuarded()
: std::shared_ptr<enable_weak_from_this*>();
return *this;
}
weak_unique_ptr &operator=(const std::unique_ptr<T> &value) {
Expand Down
99 changes: 82 additions & 17 deletions Telegram/SourceFiles/facades.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,95 @@ void CallDelayed(int duration, base::lambda_once<void()> &&lambda);

} // namespace internal

template <int N, typename Lambda>
inline void CallDelayed(int duration, base::lambda_internal::guard<N, Lambda> &&guarded) {
return internal::CallDelayed(duration, [guarded = std::move(guarded)] { guarded(); });
template <typename Lambda>
inline void CallDelayed(
int duration,
base::lambda_internal::guard_with_QObject<Lambda> &&guarded) {
return internal::CallDelayed(
duration,
std::move(guarded));
}

template <typename Pointer, typename ...PointersAndLambda>
inline void CallDelayed(int duration, Pointer &&qobject, PointersAndLambda&&... qobjectsAndLambda) {
auto guarded = base::lambda_guarded(std::forward<Pointer>(qobject), std::forward<PointersAndLambda>(qobjectsAndLambda)...);
return CallDelayed(duration, std::move(guarded));
template <typename Lambda>
inline void CallDelayed(
int duration,
base::lambda_internal::guard_with_weak<Lambda> &&guarded) {
return internal::CallDelayed(
duration,
std::move(guarded));
}

template <typename ...PointersAndLambda>
inline base::lambda<void()> LambdaDelayed(int duration, PointersAndLambda&&... qobjectsAndLambda) {
auto guarded = base::lambda_guarded(std::forward<PointersAndLambda>(qobjectsAndLambda)...);
return [guarded = std::move(guarded), duration] {
internal::CallDelayed(duration, [guarded] { guarded(); });
template <typename Lambda>
inline void CallDelayed(
int duration,
const QObject *object,
Lambda &&lambda) {
return internal::CallDelayed(
duration,
base::lambda_guarded(object, std::forward<Lambda>(lambda)));
}

template <typename Lambda>
inline void CallDelayed(
int duration,
const base::enable_weak_from_this *object,
Lambda &&lambda) {
return internal::CallDelayed(
duration,
base::lambda_guarded(object, std::forward<Lambda>(lambda)));
}

template <typename Lambda>
inline auto LambdaDelayed(
int duration,
const QObject *object,
Lambda &&lambda) {
auto guarded = base::lambda_guarded(
object,
std::forward<Lambda>(lambda));
return [saved = std::move(guarded), duration] {
auto copy = saved;
internal::CallDelayed(duration, std::move(copy));
};
}

template <typename Lambda>
inline auto LambdaDelayed(
int duration,
const base::enable_weak_from_this *object,
Lambda &&lambda) {
auto guarded = base::lambda_guarded(
object,
std::forward<Lambda>(lambda));
return [saved = std::move(guarded), duration] {
auto copy = saved;
internal::CallDelayed(duration, std::move(copy));
};
}

template <typename Lambda>
inline auto LambdaDelayedOnce(
int duration,
const QObject *object,
Lambda &&lambda) {
auto guarded = base::lambda_guarded(
object,
std::forward<Lambda>(lambda));
return [saved = std::move(guarded), duration]() mutable {
internal::CallDelayed(duration, std::move(saved));
};
}

template <typename ...PointersAndLambda>
inline base::lambda_once<void()> LambdaDelayedOnce(int duration, PointersAndLambda&&... qobjectsAndLambda) {
auto guarded = base::lambda_guarded(std::forward<PointersAndLambda>(qobjectsAndLambda)...);
return [guarded = std::move(guarded), duration]() mutable {
internal::CallDelayed(duration, [guarded = std::move(guarded)] { guarded(); });
template <typename Lambda>
inline auto LambdaDelayedOnce(
int duration,
const base::enable_weak_from_this *object,
Lambda &&lambda) {
auto guarded = base::lambda_guarded(
object,
std::forward<Lambda>(lambda));
return [saved = std::move(guarded), duration]() mutable {
internal::CallDelayed(duration, std::move(saved));
};
}

Expand Down

0 comments on commit f6ed3df

Please sign in to comment.