Files
tdesktop/cmake/external/glib/cppgir/gi/callback.hpp
allhaileris afb81b8278
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
init
2026-02-16 15:50:16 +03:00

961 lines
28 KiB
C++

#ifndef GI_CALLBACK_HPP
#define GI_CALLBACK_HPP
#include "base.hpp"
#include "exception.hpp"
#include "wrap.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
#if GI_CONFIG_EXCEPTIONS
inline ::GError **
find_gerror(bool &has_gerror)
{
has_gerror = false;
return nullptr;
}
inline ::GError **
find_gerror(bool &has_gerror, ::GError **error)
{
has_gerror = true;
return error;
}
template<typename CT, typename... CType>
inline ::GError **
find_gerror(bool &has_gerror, CT /*arg*/, CType... args)
{
return find_gerror(has_gerror, args...);
}
inline ::GError *
exception_error(const repository::GLib::Error &exc)
{
return g_error_copy(exc.gobj_());
}
inline ::GError *
exception_error(const repository::GLib::Error_Ref &exc)
{
return g_error_copy(exc.gobj_());
}
template<typename E>
inline ::GError *
exception_error(const E &exc)
{
static auto quark = g_quark_from_static_string("gi-error-quark");
return g_error_new(quark, 0, "%s", exception_desc(exc).c_str());
}
template<bool SILENT = FALSE, typename E, typename... CType>
void
report_exception(const E &exc, CType... args)
{
// see if we can really report error somewhere
bool has_gerror = false;
GError **error = find_gerror(has_gerror, args...);
if (has_gerror) {
g_return_if_fail(error == NULL || *error == NULL);
if (error)
*error = exception_error(exc);
// if caller does not need/want error, exception disappears here
} else {
// simply report the hard and simple way
// otherwise catch internally if something else/more is desired
if (!SILENT) {
auto msg = std::string("handler exception; ") + exception_desc(exc);
g_critical("%s", msg.c_str());
}
}
}
#endif
// (re)float only applies to GObject
template<typename CppType, typename Transfer,
typename std::enable_if<!traits::is_object<CppType>::value>::type * =
nullptr>
auto
unwrap_maybe_float(CppType &&v, const Transfer &t)
{
return unwrap(std::forward<CppType>(v), t);
}
// (re) float only for transfer none/floating
template<typename CppType,
typename std::enable_if<traits::is_object<CppType>::value>::type * =
nullptr>
inline typename traits::ctype<CppType>::type
unwrap_maybe_float(CppType &&v, const transfer_full_t &t)
{
return unwrap(std::forward<CppType>(v), t);
}
template<typename CppType,
typename std::enable_if<traits::is_object<CppType>::value>::type * =
nullptr>
inline typename traits::ctype<CppType>::type
unwrap_maybe_float(CppType &&v, const transfer_none_t &)
{
// expected called with r-value
static_assert(!std::is_reference<CppType>::value, "");
// steal/take from wrapper
auto result = (typename traits::ctype<CppType>::type)v.release_();
// the following is essentially a bit of a hack as mentioned in
// https://bugzilla.gnome.org/show_bug.cgi?id=693393)
// that is, we are about to return an object to C (from binding/callback)
// and this should be done with none transfer
// this none may actually mean floating (e.g. a factory-like callback)
// but no way to know from annotations
// so if at runtime the wrapper actually holds the only reference,
// then it is about to be destroyed (when wrapper goes away)
// *before* the object can make it back to caller
// so turn that ref into a floating one (as only that makes sense)
// if it is not the only ref, it is kept alive elsewhere
// (as typically so for a "getter" callback)
// so it is really treated as none
auto obj = (GObject *)result;
// theoretically not MT safe, but if == 1, only 1 thread should be involved
if (obj->ref_count == 1) {
g_object_force_floating(obj);
} else {
// otherwise unref as wrapper would have
g_object_unref(obj);
}
return result;
}
template<typename T>
constexpr T
unconst(T t)
{
return t;
}
inline char *
unconst(const char *t)
{
return (char *)t;
}
// helper types to provide additional argument info beyond Transfer
template<std::size_t... index>
struct args_index
{
static constexpr auto value = std::make_tuple(index...);
using value_type = decltype(value);
};
template<typename Transfer, bool _inout, typename CustomTraits = void,
typename ArgsIndex = args_index<>>
struct arg_info
{
using transfer_type = Transfer;
static constexpr bool inout = _inout;
// tuple of index; used to selects the C arguments (to assemble C++ argument)
using args_type = ArgsIndex;
// additional info as used by corresponding cb_arg_handler
using custom_type = CustomTraits;
};
// access above info by forwarding types
template<typename T, typename Enable = void>
struct arg_traits
{
using transfer_type = typename T::transfer_type;
static constexpr bool inout = T::inout;
using args_type = typename T::args_type;
using custom_type = typename T::custom_type;
};
// legacy case; only transfer type
template<typename T>
struct arg_traits<T,
typename std::enable_if<std::is_base_of<transfer_t, T>::value>::type>
{
using transfer_type = T;
static constexpr bool inout = false;
using args_type = args_index<>;
using custom_type = void;
};
// IndexTuple is essentially an args_index<...>
template<typename IndexTuple, std::size_t... Index, typename F,
typename ArgTuple>
decltype(auto)
apply_with_args(std::index_sequence<Index...>, F &&f, ArgTuple &&args)
{
return f(std::get<std::get<Index>(IndexTuple::value)>(args)...);
}
template<typename IndexTuple, typename F, typename... Args>
decltype(auto)
apply_with_args(F &&f, Args &&...args)
{
return apply_with_args<IndexTuple>(
std::make_index_sequence<
std::tuple_size<typename IndexTuple::value_type>::value>(),
std::forward<F>(f), std::forward_as_tuple(args...));
}
// a simple callback has no (need for) args_index
template<typename T>
struct is_simple_cb : public std::true_type
{};
template<typename Transfer, typename... Transfers>
struct is_simple_cb<std::tuple<Transfer, Transfers...>>
{
static constexpr bool value =
std::tuple_size<
typename arg_traits<Transfer>::args_type::value_type>::value == 0 ||
(sizeof...(Transfers) > 0 &&
is_simple_cb<std::tuple<Transfers...>>::value);
};
template<typename T, typename CT = void>
struct map_cpp_function;
// handles all calls C -> C++ (callbacks, virtual method calls)
// restrictions though on types supported (enforced by code generation)
template<typename T, typename RetTransfer, typename ArgTransfers,
typename CT = typename map_cpp_function<T>::type,
bool SIMPLE = is_simple_cb<ArgTransfers>::value>
struct transform_caller;
// helper used below that provides pre-call and post-call steps
// to handle each argument's conversion to and from C++
// in so-called (most) simple cases, there is one-to-one mapping between
// C and C++ arguments and C++ argument type that allows to deduce context
// (in particular, no callbacks or sized array)
// as such, proper steps can be obtained by specialization on Cpp argument type
template<typename CppArg, typename Enable = void>
struct cb_arg_handler;
// in simple cases, C signature can be derived from C++ signature
template<typename T, typename CT>
struct map_cpp_function
{
using type = CT;
};
template<typename R, typename... Args>
struct map_cpp_function<R(Args...), void>
{
using type = typename traits::ctype<R>::type(
typename cb_arg_handler<Args>::c_type...);
};
// signature used in virtual method handling
template<typename R, typename... Args>
struct map_cpp_function<R (*)(Args...), void>
{
using type = typename traits::ctype<R>::type(
typename cb_arg_handler<Args>::c_type...);
};
// NOTE function type R(const A) is identical to R(A)
// so no deduced Args below will retain const (if such)
// in simple cases, *Transfer* is simply a transfer type
// but it may also be a more elaborate argument trait
template<typename R, typename... Args, typename RetTransfer,
typename... Transfers, typename CR, typename... CArgs, bool SIMPLE>
struct transform_caller<R(Args...), RetTransfer, std::tuple<Transfers...>,
CR(CArgs...), SIMPLE>
{
static_assert(sizeof...(Args) == sizeof...(Transfers), "");
static_assert(!SIMPLE || sizeof...(Args) == sizeof...(CArgs), "");
typedef transform_caller self_type;
typedef R (*caller_type)(Args &&..., void *d);
private:
static R do_call(Args &&...args, caller_type func, void *d)
{
return func(std::forward<Args>(args)..., d);
}
// helper that provides context for pack expansion below
static void dummy_call(...){};
template<typename T>
static auto _tt(const T &)
{
return typename arg_traits<T>::transfer_type();
}
// non-void return
template<typename T, std::size_t... Index,
typename std::enable_if<SIMPLE && !std::is_void<T>::value>::type * =
nullptr>
static CR _wrapper(
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
{
std::tuple<cb_arg_handler<Args>...> handlers;
auto ret = do_call(
std::get<Index>(handlers).arg(args, _tt(Transfers()), Transfers())...,
func, d);
dummy_call((std::get<Index>(handlers).post(args, _tt(Transfers())), 0)...);
return unconst(unwrap_maybe_float(std::move(ret), RetTransfer()));
}
// void return
template<typename T, std::size_t... Index,
typename std::enable_if<SIMPLE && std::is_void<T>::value>::type * =
nullptr>
static CR _wrapper(
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
{
std::tuple<cb_arg_handler<Args>...> handlers;
do_call(
std::get<Index>(handlers).arg(args, _tt(Transfers()), Transfers())...,
func, d);
dummy_call((std::get<Index>(handlers).post(args, _tt(Transfers())), 0)...);
}
// complex; non-void return
template<typename T, std::size_t... Index,
typename std::enable_if<!SIMPLE && !std::is_void<T>::value>::type * =
nullptr>
static CR _wrapper(
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
{
std::tuple<cb_arg_handler<Args>...> handlers;
auto ret = do_call(
apply_with_args<typename arg_traits<Transfers>::args_type>(
[&handlers](auto... selargs) mutable -> decltype(auto) {
return std::get<Index>(handlers).arg(selargs...,
typename arg_traits<Transfers>::transfer_type(), Transfers());
},
args...)...,
func, d);
dummy_call((apply_with_args<typename arg_traits<Transfers>::args_type>(
[&handlers](auto... selargs) mutable -> decltype(auto) {
return std::get<Index>(handlers).post(selargs...,
typename arg_traits<Transfers>::transfer_type());
},
args...),
0)...);
return unconst(unwrap_maybe_float(std::move(ret), RetTransfer()));
}
// complex; void return
template<typename T, std::size_t... Index,
typename std::enable_if<!SIMPLE && std::is_void<T>::value>::type * =
nullptr>
static CR _wrapper(
CArgs... args, caller_type func, void *d, std::index_sequence<Index...>)
{
std::tuple<cb_arg_handler<Args>...> handlers;
do_call(apply_with_args<typename arg_traits<Transfers>::args_type>(
[&handlers](auto... selargs) mutable -> decltype(auto) {
return std::get<Index>(handlers).arg(selargs...,
typename arg_traits<Transfers>::transfer_type(),
Transfers());
},
args...)...,
func, d);
dummy_call((apply_with_args<typename arg_traits<Transfers>::args_type>(
[&handlers](auto... selargs) mutable -> decltype(auto) {
return std::get<Index>(handlers).post(selargs...,
typename arg_traits<Transfers>::transfer_type());
},
args...),
0)...);
}
public:
static CR wrapper(CArgs... args, caller_type func, void *d)
{
// exceptions should not escape into plain C
#if GI_CONFIG_EXCEPTIONS
try {
#endif
return self_type::template _wrapper<R>(
args..., func, d, std::make_index_sequence<sizeof...(Args)>());
#if GI_CONFIG_EXCEPTIONS
} catch (const repository::GLib::Error &exc) {
report_exception(exc, args...);
} catch (const repository::GLib::Error_Ref &exc) {
report_exception(exc, args...);
} catch (const std::exception &exc) {
report_exception(exc, args...);
} catch (...) {
report_exception(nullptr, args...);
}
return typename traits::ctype<R>::type();
#endif
}
};
// minor helper traits used below
namespace _traits
{
template<typename T>
struct remove_all_pointers
{
using type = T;
};
template<typename T>
struct remove_all_pointers<T *>
{
using type = typename remove_all_pointers<T>::type;
};
template<typename T>
struct remove_all_pointers<T *const>
{
using type = typename remove_all_pointers<T>::type;
};
template<typename T>
using is_basic_or_void = typename std::conditional<traits::is_basic<T>::value ||
std::is_void<T>::value,
std::true_type, std::false_type>::type;
template<typename T>
using is_basic_argument = typename std::conditional<
is_basic_or_void<typename std::remove_cv<
typename remove_all_pointers<T>::type>::type>::value,
std::true_type, std::false_type>::type;
template<typename T>
using is_const_lvalue = typename std::conditional<
std::is_lvalue_reference<T>::value &&
std::is_const<typename std::remove_reference<T>::type>::value,
std::true_type, std::false_type>::type;
} // namespace _traits
// generic fallback case; assume input parameter
// (also covers boxed callerallocates, which pretty much is/becomes input)
template<typename CppArg, typename Enable>
struct cb_arg_handler
{
using c_type = typename traits::ctype<CppArg>::type;
// use generic CType to handle const differences wrt c_type
template<typename CType, typename Transfer, typename ArgTrait>
typename std::decay<CppArg>::type arg(
CType arg, const Transfer &t, const ArgTrait &) noexcept
{
// wrap to normalized destination target (no const &)
// the destination type here is really only relevant for collection cases
// (since that depends on the contained element)
// otherwise all the info is pretty much in c_type type
return wrap_to<typename std::decay<CppArg>::type>(arg, t);
}
// minor variation; dynamic sized array input
// also accepts length input
template<typename CType, typename Transfer, typename ArgTrait>
typename std::decay<CppArg>::type arg(
CType arg, int length, const Transfer &t, const ArgTrait &) noexcept
{
// destination also really needed here
return wrap_to<typename std::decay<CppArg>::type>(arg, length, t);
}
void post(...){};
};
// passthrough on (pointers to) basic values;
// handles input/output of basic values, as well as arrays of such
template<typename CppArg>
struct cb_arg_handler<CppArg,
typename std::enable_if<_traits::is_basic_argument<CppArg>::value>::type>
{
using c_type = CppArg;
template<typename CType, typename Transfer, typename ArgTrait>
CType arg(CType arg, const Transfer &, const ArgTrait &) noexcept
{
return arg;
}
void post(...){};
// array size case; inout C int* to in Cpp int
// (only enable if so tagged by non-default custom trait type)
template<typename CType, typename Transfer, typename ArgTrait>
typename std::enable_if<
!std::is_void<typename arg_traits<ArgTrait>::custom_type>::value,
CType>::type
arg(CType *arg, const Transfer &, const ArgTrait &) noexcept
{
return *arg;
}
};
// handle "complex" (in)out argument
// (though also handles e.g. int& case, which might be optimized, but anyways)
// these are recognized/assumed to be a pointer or reference to non-basic type
template<typename CppArg>
struct cb_arg_handler<CppArg,
typename std::enable_if<
!_traits::is_basic_argument<CppArg>::value &&
(std::is_pointer<CppArg>::value ||
(std::is_lvalue_reference<CppArg>::value &&
!_traits::is_const_lvalue<CppArg>::value))>::type>
{
using BaseCppType =
typename std::decay<typename std::remove_pointer<CppArg>::type>::type;
// no more pointer expected here
// (other than for void* cases, which do not introspect well, but anyways)
static_assert(!std::is_pointer<BaseCppType>::value ||
std::is_same<BaseCppType, gpointer>::value ||
std::is_same<BaseCppType, gconstpointer>::value,
"");
using c_type = typename traits::ctype<BaseCppType>::type *;
// intermediate helper storage
BaseCppType var_{};
// pointer case
CppArg rv(std::true_type) { return &var_; }
// ref case
CppArg rv(std::false_type) { return var_; }
// handle const variations (e.g. const char**)
template<typename T, typename X>
static void assign(T *&t, X val)
{
t = const_cast<T *>(val);
}
template<typename T, typename X>
static void assign(T &t, X val)
{
t = unconst(val);
}
template<typename Transfer, typename ArgTrait>
CppArg arg(c_type arg, const Transfer &t, const ArgTrait &)
{
bool inout = arg_traits<ArgTrait>::inout;
// a plain type has no special needs
// so we can always take any bogus value as-is
// (which is then less sensitive to incorrect annotation)
if (arg && (inout || traits::is_plain<BaseCppType>::value))
var_ = wrap_to<BaseCppType>(*arg, t);
return rv(std::is_pointer<CppArg>());
}
// overall function call happens following arg call above
// post invoked after function call
template<typename Transfer>
void post(c_type arg, const Transfer &t)
{
if (arg)
assign(*arg, unwrap_maybe_float(std::move(var_), t));
}
// sized array variants; arguments (data, size)
// latter could be input (int) or (in)out (int*)
template<typename Transfer, typename Int, typename ArgTrait>
CppArg arg(c_type arg, Int size, const Transfer &t, const ArgTrait &)
{
bool inout = arg_traits<ArgTrait>::inout;
if (arg && inout)
var_ = wrap_to<BaseCppType>(*arg, size, t);
return rv(std::is_pointer<CppArg>());
}
template<typename Transfer, typename Int, typename ArgTrait>
CppArg arg(c_type arg, Int *size, const Transfer &t, const ArgTrait &)
{
bool inout = arg_traits<ArgTrait>::inout;
if (arg && size && inout)
var_ = wrap_to<BaseCppType>(*arg, *size, t);
return rv(std::is_pointer<CppArg>());
}
template<typename Transfer, typename Int>
void post(c_type arg, Int, const Transfer &t)
{
post(arg, nullptr, t);
}
template<typename Transfer, typename Int>
void post(c_type arg, Int *size, const Transfer &t)
{
if (arg)
assign(*arg, unwrap_maybe_float(std::move(var_), t));
if (size)
*size = var_.size();
}
};
// handles callback argument
// (as argument in anther callback, most likely a virtual method)
template<typename CppArg>
struct cb_arg_handler<CppArg,
typename std::enable_if<
std::is_pointer<typename CppArg::cfunction_type>::value>::type>
{
template<typename CType, typename Transfer, typename ArgTrait>
CppArg arg(CType cb, gpointer userdata, ::GDestroyNotify destroy,
const Transfer &, const ArgTrait &) noexcept
{
using ct = typename arg_traits<ArgTrait>::custom_type;
// setup shared pointer to userdata with destroy as Deleter
auto sp = destroy ? std::shared_ptr<void>(userdata, destroy) : nullptr;
// keep userdata/destroy alive as long as lambda handler
auto h = [cb, userdata, sp = std::move(sp)](
auto &&...args) -> decltype(auto) {
// original callback type CType should match deduced handler_cb_tpe
// but let's make sure as usual
return ct::handler(std::forward<decltype(args)>(args)...,
typename ct::handler_cb_type(cb), userdata);
};
return h;
}
template<typename CType, typename Transfer, typename ArgTrait>
CppArg arg(CType cb, gpointer userdata, const Transfer &t,
const ArgTrait &tt) noexcept
{
return arg(cb, userdata, nullptr, t, tt);
}
void post(...){};
};
class connection_status
{
public:
struct data
{
bool connected{false};
};
bool connected() const
{
auto sp = data_.lock();
return sp && sp->connected;
}
protected:
std::weak_ptr<data> data_;
};
// callback handling
template<typename G>
class connectable;
template<typename G, bool AUTODESTROY = false>
class callback_wrapper;
template<typename R, typename... Args>
class connectable<R(Args...)>
{
friend class callback_wrapper<R(Args...)>;
struct data : public connection_status::data
{
template<typename T,
typename Enable = typename detail::disable_if_same_or_derived<data, T>>
data(T &&t) : callable(std::forward<T>(t))
{}
std::function<R(Args... args)> callable;
};
struct connection_status_factory : public connection_status
{
connection_status_factory(std::shared_ptr<data> p)
{
data_ = std::weak_ptr<connection_status::data>(p);
}
};
public:
// avoid copy constructor mishaps
template<typename T,
typename Enable =
typename detail::disable_if_same_or_derived<connectable, T>>
connectable(T &&t) : data_(std::make_shared<data>(std::forward<T>(t)))
{}
connection_status connection() const
{
return connection_status_factory(data_);
}
R operator()(Args... args) const
{
return data_->callable(std::forward<Args>(args)...);
}
explicit operator bool() const { return data_ && data_->callable; }
private:
// state management by wrapper
void connected(bool conn) { data_->connected = conn; }
void disconnect() { data_->connected = false; }
private:
std::shared_ptr<data> data_;
};
template<typename R, typename... Args, bool AUTODESTROY>
class callback_wrapper<R(Args...), AUTODESTROY>
{
typedef callback_wrapper self_type;
public:
template<typename T,
typename Enable =
typename detail::disable_if_same_or_derived<callback_wrapper, T>>
callback_wrapper(T &&t) : _callback(std::forward<T>(t))
{
// mark connected now that it is wrapped
_callback.connected(true);
}
// (only) used by manual callback workaround
static R wrapper(Args... args, gpointer user_data)
{
auto t = reinterpret_cast<callback_wrapper *>(user_data);
std::unique_ptr<self_type> wt(AUTODESTROY ? t : nullptr);
return t->_callback(args...);
}
static void destroy(gpointer user_data)
{
auto t = reinterpret_cast<callback_wrapper *>(user_data);
delete t;
}
// (async scope) wrapper may have to take ownership of additional data
// (other callback wrapper)
template<typename T>
void take_data(std::shared_ptr<T> d)
{
auto cb = std::move(_callback.data_->callable);
auto newcb = [d, cb](Args &&...args) {
return cb(std::forward<Args>(args)...);
};
_callback.data_->callable = std::move(newcb);
}
template<typename T>
void take_data(T *d)
{
take_data(std::shared_ptr<T>(d));
}
~callback_wrapper()
{
// other shared ptr to data might be around (unlikely though)
// but regardless disconnect now as requested (as wrapper is going away)
_callback.disconnect();
}
connectable<R(Args... args)> _callback;
};
template<typename G, typename CG = typename map_cpp_function<G>::type>
struct transform_callback_wrapper;
template<typename R, typename... Args, typename CR, typename... CArgs>
struct transform_callback_wrapper<R(Args...), CR(CArgs...)>
{
// transfers of arguments
template<bool AUTODESTROY, typename RetTransfer, typename... Transfers>
class with_transfer : public callback_wrapper<R(Args...)>
{
typedef callback_wrapper<R(Args...)> super_type;
typedef with_transfer self_type;
public:
template<typename T>
explicit with_transfer(T &&t) : super_type(std::forward<T>(t))
{}
private:
static R caller(Args &&...args, void *d)
{
auto this_ = (self_type *)d;
return this_->_callback(std::forward<Args>(args)...);
}
public:
static CR wrapper(CArgs... args, gpointer user_data)
{
auto t = reinterpret_cast<self_type *>(user_data);
std::unique_ptr<self_type> wt(AUTODESTROY ? t : nullptr);
return transform_caller<R(Args...), RetTransfer, std::tuple<Transfers...>,
CR(CArgs...)>::wrapper(args..., caller, t);
}
};
};
// used early in declarations, so avoid using unknown types in CSigOrVoid
template<typename CppSig, typename RetTransfer,
typename ArgTransfers = std::tuple<>, typename CSigOrVoid = void>
class callback;
template<typename CppSig, typename RetTransfer, typename... Transfers,
typename CSigOrVoid>
class callback<CppSig, RetTransfer, std::tuple<Transfers...>, CSigOrVoid>
: public connectable<CppSig>
{
typedef connectable<CppSig> super_type;
public:
typedef CppSig function_type;
typedef typename map_cpp_function<CppSig, CSigOrVoid>::type CSig;
template<bool ASYNC = false>
using wrapper_type = typename transform_callback_wrapper<function_type,
CSig>::template with_transfer<ASYNC, RetTransfer, Transfers...>;
typedef typename std::add_pointer<decltype(wrapper_type<>::wrapper)>::type
cfunction_type;
using super_type::super_type;
};
// signal handling;
// transfer none for arguments
// transfer full for return
template<typename G>
struct transform_signal_wrapper;
template<typename T>
struct signal_arg_transfer
{
typedef transfer_none_t type;
};
template<typename R, typename... Args>
struct transform_signal_wrapper<R(Args...)>
: public transform_callback_wrapper<R(
Args...)>::template with_transfer<false, transfer_full_t,
typename detail::signal_arg_transfer<Args>::type...>
{
private:
typedef typename transform_callback_wrapper<R(
Args...)>::template with_transfer<false, transfer_full_t,
typename detail::signal_arg_transfer<Args>::type...>
super_;
public:
template<typename T>
transform_signal_wrapper(T &&t) : super_(std::forward<T>(t))
{}
};
// connection helpers
class connection_impl
{
public:
connection_impl(gulong id, connection_status s) : id_(id), status_(s) {}
bool connected() const { return status_.connected(); }
gulong id() const { return id_; }
protected:
gulong id_;
connection_status status_;
};
template<typename Connection>
class connection
{
typedef Connection impl;
public:
connection() = default;
template<typename... Args>
explicit connection(Args... arg)
: conn_(std::make_shared<impl>(std::forward<Args>(arg)...))
{
// this is to be expected at this time
if (!connected()) {
g_warning("creating non-connected connection");
}
}
// implicit copy/move
bool connected() const { return conn_ && conn_->connected(); }
void disconnect()
{
if (connected())
conn_->disconnect();
}
protected:
std::shared_ptr<impl> conn_;
};
template<typename ConnectionBase>
class scoped_connection : public ConnectionBase
{
typedef ConnectionBase connection;
public:
scoped_connection() : connection() {}
~scoped_connection() { this->disconnect(); }
void release() { this->conn_.reset(); }
// ensure default movable
scoped_connection(scoped_connection &&other) = default;
scoped_connection &operator=(scoped_connection &&other) = default;
// not copyable; to avoid inadvertent disconnect
scoped_connection(const scoped_connection &other) = delete;
scoped_connection &operator=(const scoped_connection &other) = delete;
// but convert/assign from base class
scoped_connection(const connection &other) : connection(other) {}
scoped_connection &operator=(const connection &other)
{
(*(connection *)(this)) = other;
return *this;
}
scoped_connection &operator=(const connection &&other)
{
(*(connection *)(this)) = std::move(other);
return *this;
}
};
} // namespace detail
// function bind helpers
template<typename R, typename T, typename Tp, typename... Args>
inline std::function<R(Args...)>
mem_fun(R (T::*pm)(Args...), Tp object)
{
return [object, pm](Args... args) {
return (object->*pm)(std::forward<Args>(args)...);
};
}
template<typename R, typename T, typename Tp, typename... Args>
inline std::function<R(Args...)>
mem_fun(R (T::*pm)(Args...) const, Tp object)
{
return [object, pm](Args... args) {
return (object->*pm)(std::forward<Args>(args)...);
};
}
// expose for use in fallback scenarios
using detail::callback_wrapper;
} // namespace gi
#endif // CALLBACK_HPP