init
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

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

503
cmake/external/glib/cppgir/gi/base.hpp vendored Normal file
View File

@@ -0,0 +1,503 @@
#ifndef GI_BASE_HPP
#define GI_BASE_HPP
#include "gi_inc.hpp"
#include "boxed.hpp"
#include "objectbase.hpp"
// un-inline some glib parts
// (otherwise they have internal linkage and not usable in non-TU-local context)
#ifdef GI_MODULE_IN_INTERFACE
#ifdef g_strdup
#undef g_strdup
#endif
#endif
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
inline std::string
exception_desc(const std::exception &e)
{
auto desc = e.what();
return desc ? desc : typeid(e).name();
}
inline std::string
exception_desc(...)
{
return "[unknown]";
}
template<typename E>
[[noreturn]] inline void
try_throw(E &&e)
{
#if GI_CONFIG_EXCEPTIONS
throw std::forward<E>(e);
#else
g_critical("no throw exception; %s", exception_desc(e).c_str());
abort();
#endif
}
// constructor does not appreciate NULL, so wrap that here
// map NULL to empty string; not quite the same, but it will do
inline std::string
make_string(const char *s)
{
return std::string(s ? s : "");
}
// helper string subtype
// used to overload unwrap of optional string argument
// (transfrom empty string to null)
// NOTE std::optional requires C++17
class optional_string : public std::string
{};
class noncopyable
{
public:
noncopyable() {}
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
noncopyable(noncopyable &&) = default;
noncopyable &operator=(noncopyable &&) = default;
};
class scope_guard : public noncopyable
{
private:
std::function<void()> cleanup_;
public:
scope_guard(std::function<void()> &&cleanup) : cleanup_(std::move(cleanup)) {}
~scope_guard() noexcept(false)
{
#if GI_CONFIG_EXCEPTIONS
#if __cplusplus >= 201703L
auto pending = std::uncaught_exceptions();
#else
auto pending = std::uncaught_exception();
#endif
try {
#endif
cleanup_();
#if GI_CONFIG_EXCEPTIONS
} catch (...) {
if (!pending)
throw;
}
#endif
}
};
// as in
// http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/
template<typename A, typename B>
using disable_if_same_or_derived = typename std::enable_if<
!std::is_base_of<A, typename std::remove_reference<B>::type>::value>::type;
} // namespace detail
namespace repository
{
// class types declare c type within class
// others can do so using this (e.g. enum)
template<typename CppType>
struct declare_ctype_of
{};
// and for all cases the reverse cpp type
template<typename CType>
struct declare_cpptype_of
{};
// generate code must specialize appropriately
template<typename T>
struct is_enumeration : public std::false_type
{};
template<typename T>
struct is_bitfield : public std::false_type
{};
} // namespace repository
struct transfer_full_t;
struct transfer_none_t;
namespace traits
{
template<typename T, typename U = void>
struct if_valid_type
{
typedef U type;
};
template<typename, typename = void>
struct is_type_complete : public std::false_type
{};
template<typename T>
struct is_type_complete<T, typename if_valid_type<decltype(sizeof(
typename std::decay<T>::type))>::type>
: public std::true_type
{};
template<typename T>
using is_decayed = std::is_same<typename std::decay<T>::type, T>;
template<typename T>
using is_cboxed =
typename std::conditional<std::is_base_of<detail::CBoxed, T>::value,
std::true_type, std::false_type>::type;
template<typename T>
using is_gboxed =
typename std::conditional<std::is_base_of<detail::GBoxed, T>::value,
std::true_type, std::false_type>::type;
template<typename T>
using is_boxed =
typename std::conditional<std::is_base_of<detail::Boxed, T>::value,
std::true_type, std::false_type>::type;
// avoid derived cases
template<typename T>
using is_object =
typename std::conditional<std::is_base_of<detail::ObjectBase, T>::value &&
sizeof(T) == sizeof(gpointer),
std::true_type, std::false_type>::type;
template<typename T>
using is_wrapper =
typename std::conditional<std::is_base_of<detail::wrapper_tag, T>::value &&
sizeof(T) == sizeof(gpointer),
std::true_type, std::false_type>::type;
// bring in to this namespace
using repository::is_bitfield;
using repository::is_enumeration;
// aka passthrough
template<typename T>
using is_basic =
typename std::conditional<std::is_same<T, gpointer>::value ||
std::is_same<T, gconstpointer>::value ||
std::is_arithmetic<T>::value,
std::true_type, std::false_type>::type;
// almost passthrough (on lower level at least)
template<typename T>
using is_plain = typename std::conditional<traits::is_basic<T>::value ||
std::is_enum<T>::value,
std::true_type, std::false_type>::type;
template<typename T, typename E = void>
struct is_reftype : public std::false_type
{};
template<typename T>
struct is_reftype<T,
typename if_valid_type<typename std::decay<T>::type::BoxType>::type>
: public std::true_type
{};
template<typename T, typename Enable = void>
struct has_ctype_member : public std::false_type
{};
template<typename T>
struct has_ctype_member<T,
typename if_valid_type<typename T::BaseObjectType>::type>
: public std::true_type
{};
// return corresponding c type (if any)
// (string and basic type not considered)
// preserve const
template<typename T, typename Enable = void>
struct ctype
{};
// class case
template<typename T>
struct ctype<T,
typename if_valid_type<typename std::decay<T>::type::BaseObjectType>::type>
{
typedef typename std::remove_reference<T>::type CppType;
// make sure; avoid subclassed cases
static_assert(is_wrapper<CppType>::value || is_boxed<CppType>::value,
"must be object or boxed wrapper");
typedef typename CppType::BaseObjectType CType;
typedef typename std::conditional<std::is_const<CppType>::value, const CType,
CType>::type *type;
};
// remaining cases
template<typename T>
struct ctype<T, typename if_valid_type<
typename repository::declare_ctype_of<T>::type>::type>
{
typedef typename repository::declare_ctype_of<T>::type CType;
typedef typename std::conditional<std::is_const<T>::value, const CType,
CType>::type type;
};
// basic cases passthrough
template<typename T>
struct ctype<T,
typename std::enable_if<(std::is_fundamental<T>::value &&
!std::is_same<T, bool>::value) ||
std::is_same<T, gpointer>::value ||
std::is_same<T, gconstpointer>::value>::type>
{
typedef T type;
};
// ... exception though for bool
template<>
struct ctype<bool, void>
{
typedef gboolean type;
};
// as used in callback signatures
// or in list (un)wrapping
template<>
struct ctype<const std::string &, void>
{
typedef const char *type;
};
template<>
struct ctype<std::string, void>
{
typedef char *type;
};
template<typename T1, typename T2>
struct ctype<std::pair<T1, T2>>
{
typedef std::pair<typename ctype<T1>::type, typename ctype<T2>::type> type;
};
// conversely
// return corresponding cpp type (if known)
// (string and basic type not considered)
// preserve const
template<typename T, typename Transfer = transfer_full_t,
typename Enable = void>
struct cpptype
{};
// generic
template<typename T>
struct cpptype<T *, transfer_full_t,
typename if_valid_type<typename repository::declare_cpptype_of<
typename std::remove_const<T>::type>::type>::type>
{
typedef typename repository::declare_cpptype_of<
typename std::remove_const<T>::type>::type CppType;
typedef typename std::conditional<std::is_const<T>::value, const CppType,
CppType>::type type;
};
template<typename T>
struct cpptype<T, transfer_full_t,
typename if_valid_type<typename repository::declare_cpptype_of<
typename std::remove_const<T>::type>::type>::type>
{
typedef typename repository::declare_cpptype_of<
typename std::remove_const<T>::type>::type CppType;
typedef typename std::conditional<std::is_const<T>::value, const CppType,
CppType>::type type;
};
// basic cases passthrough
template<typename T>
struct cpptype<T, transfer_full_t,
typename std::enable_if<std::is_fundamental<T>::value ||
std::is_same<T, gpointer>::value ||
std::is_same<T, gconstpointer>::value>::type>
{
typedef T type;
};
#if 0
template<>
struct cpptype<char *, transfer_full_t>
{
using type = std::string;
};
#endif
// handle none transfer case
template<typename T>
struct cpptype<T, transfer_none_t>
{
using CppType = typename cpptype<T, transfer_full_t>::type;
template<typename TT, typename Enable = void>
struct map_type
{
using type = TT;
};
template<typename TT>
struct map_type<TT, typename if_valid_type<typename TT::ReferenceType>::type>
{
using type = typename TT::ReferenceType;
};
using type = typename map_type<CppType>::type;
};
// map owning box type to corresponding reference box type
template<typename T>
struct reftype
{
typedef typename T::ReferenceType type;
};
} // namespace traits
// specify transfer type when (un)wrapping
// this approach is safer than some booleans and allows overload combinations
struct transfer_t
{
const int value;
constexpr explicit transfer_t(int v = 0) : value(v) {}
};
struct transfer_none_t : public transfer_t
{
constexpr transfer_none_t() : transfer_t(0) {}
};
struct transfer_full_t : public transfer_t
{
constexpr transfer_full_t() : transfer_t(1) {}
};
struct transfer_container_t : public transfer_t
{
constexpr transfer_container_t() : transfer_t(2) {}
};
GI_MODULE_INLINE const constexpr transfer_t transfer_dummy = transfer_t();
GI_MODULE_INLINE const constexpr transfer_none_t transfer_none =
transfer_none_t();
GI_MODULE_INLINE const constexpr transfer_full_t transfer_full =
transfer_full_t();
GI_MODULE_INLINE const constexpr transfer_container_t transfer_container =
transfer_container_t();
template<typename Transfer>
struct element_transfer
{};
template<>
struct element_transfer<transfer_none_t>
{
typedef transfer_none_t type;
};
template<>
struct element_transfer<transfer_full_t>
{
typedef transfer_full_t type;
};
template<>
struct element_transfer<transfer_container_t>
{
typedef transfer_none_t type;
};
// unwrapping a callback
// specify call scope type
struct scope_t
{
const int value;
constexpr explicit scope_t(int v = 0) : value(v) {}
};
struct scope_call_t : public scope_t
{
constexpr scope_call_t() : scope_t(0) {}
};
struct scope_async_t : public scope_t
{
constexpr scope_async_t() : scope_t(1) {}
};
struct scope_notified_t : public scope_t
{
constexpr scope_notified_t() : scope_t(2) {}
};
GI_MODULE_INLINE const constexpr scope_t scope_dummy = scope_t();
GI_MODULE_INLINE const constexpr scope_call_t scope_call = scope_call_t();
GI_MODULE_INLINE const constexpr scope_async_t scope_async = scope_async_t();
GI_MODULE_INLINE const constexpr scope_notified_t scope_notified =
scope_notified_t();
// (dummy) helper tag to aid in overload resolution
template<typename Interface>
struct interface_tag
{
typedef Interface type;
};
// CallArgs minimal helper type
// only provide what is needed
// not intended for general use, even though not in internal namespace
template<typename T>
class required
{
T data_;
public:
constexpr required(T v) : data_(std::move(v)) {}
constexpr operator T &() &noexcept { return data_; }
constexpr operator T &&() &&noexcept { return std::move(data_); }
constexpr T &value() &noexcept { return data_; }
constexpr T &&value() &&noexcept { return std::move(data_); }
};
// helper tag type to aid in overload resolution of CallArgs
// this is used as the first argument of a (non-plain) signature variant
// (since it may otherwise have only 1 struct argument, like the plain variant)
template<typename... CA_TAG>
using ca = std::tuple<CA_TAG...>;
struct ca_tag
{};
struct ca_in_tag : public ca_tag
{};
struct ca_bc_tag : public ca_tag
{};
// convenience abbreviation
using ca_in = ca<ca_in_tag>;
#if GI_DL
namespace detail
{
// dynamic load of symbol
inline void *
load_symbol(const std::vector<const char *> libs, const char *symbol)
{
void *s = nullptr;
for (const auto &l : libs) {
auto h = dlopen(l, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE);
if (h) {
s = dlsym(h, symbol);
dlclose(h);
if (s)
break;
}
}
return s;
}
} // namespace detail
#endif // GI_DL
} // namespace gi
#endif // GI_BASE_HPP

349
cmake/external/glib/cppgir/gi/boxed.hpp vendored Normal file
View File

@@ -0,0 +1,349 @@
#ifndef GI_BOXED_HPP
#define GI_BOXED_HPP
#include "gi_inc.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace repository
{
// specialize to enable copyable boxed wrapper
// ideally only used in case where the copy is (equivalently);
// * cheap
// * does not change the underlying pointer
// * refcount based
// * has no semantics effects
// (e.g. GstMiniObject refcount affects writable)
template<typename CType>
struct enable_boxed_copy :
#if GI_ENABLE_BOXED_COPY_ALL
public std::true_type
#else
public std::false_type
#endif
{};
} // namespace repository
namespace detail
{
// class tags
class Boxed
{};
class CBoxed : public Boxed
{};
class GBoxed : public Boxed
{};
template<typename CType>
class SharedWrapper
{
public:
typedef SharedWrapper self;
protected:
CType *data_ = nullptr;
public:
CType *gobj_() { return data_; }
const CType *gobj_() const { return data_; }
explicit operator bool() const { return (bool)data_; }
bool operator==(const SharedWrapper &other) const
{
return data_ == other.data_;
}
bool operator==(std::nullptr_t o) const { return data_ == o; }
bool operator!=(const SharedWrapper &other) const
{
return data_ != other.data_;
}
bool operator!=(std::nullptr_t o) const { return data_ != o; }
CType *release_()
{
auto tmp = this->data_;
this->data_ = nullptr;
return tmp;
}
};
template<typename CppType, typename CType>
struct GBoxedFuncs
{
static CType *copy(const void *data)
{
return (CType *)g_boxed_copy(CppType::get_type_(), data);
}
static void free(void *data) { g_boxed_free(CppType::get_type_(), data); }
};
struct CBoxedFuncsBase
{
static void free(void *data) { g_free(data); }
};
template<typename CppType, typename CType>
struct CBoxedFuncs : CBoxedFuncsBase
{
static CType *copy(const void *data)
{
#if GLIB_CHECK_VERSION(2, 68, 0)
return (CType *)g_memdup2(data, sizeof(CType));
#else
return (CType *)g_memdup(data, sizeof(CType));
#endif
}
};
template<typename CType, typename Funcs, typename TagType>
class BoxedWrapper : public SharedWrapper<CType>, public TagType
{
typedef BoxedWrapper self;
protected:
static void _deleter(CType *obj, ...)
{
if (obj)
Funcs::free(obj);
}
static CType *_copy(const CType *obj)
{
return obj ? Funcs::copy(obj) : nullptr;
}
public:
typedef CType BaseObjectType;
BoxedWrapper(CType *obj = nullptr) noexcept { this->data_ = obj; }
CType *gobj_copy_() const { return _copy(this->gobj_()); }
// resulting casted type determines ownership
template<typename Cpp>
static Cpp wrap(const typename Cpp::BaseObjectType *obj)
{
static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported");
static_assert(std::is_base_of<self, Cpp>::value, "type wrap not supported");
BoxedWrapper w(const_cast<typename Cpp::BaseObjectType *>(obj));
return std::move(*static_cast<Cpp *>(&w));
}
};
// in templates below, Base should be a subclass of BoxedWrapper
// so we re-use the members it provides, as well as the wrap template
// to avoid ambiguous reference to the latter
// (if BoxedWrapper were inherited from again)
// the nullptr_t constructor (indirectly) supports `= nullptr` (assignment)
template<typename CppType, typename CType>
using GBoxedWrapperBase =
BoxedWrapper<CType, GBoxedFuncs<CppType, CType>, GBoxed>;
// assuming Base has suitable members such as above BoxedWrapper
template<typename Base>
class MoveWrapper : public Base
{
typedef Base super;
public:
using super::super;
~MoveWrapper() { this->_deleter(this->data_, static_cast<Base *>(this)); }
MoveWrapper(const MoveWrapper &) = delete;
MoveWrapper &operator=(const MoveWrapper &) = delete;
MoveWrapper(MoveWrapper &&o) noexcept { *this = std::move(o); }
MoveWrapper &operator=(MoveWrapper &&o) noexcept
{
if (this != &o) {
this->_deleter(this->data_, static_cast<Base *>(this));
(Base &)(*this) = std::move(o);
o.data_ = nullptr;
}
return *this;
}
};
template<typename Base>
class CopyWrapper : public MoveWrapper<Base>
{
typedef MoveWrapper<Base> super;
public:
using super::super;
CopyWrapper(const CopyWrapper &o) noexcept : super()
{
this->data_ = this->_copy(o.data_);
}
CopyWrapper &operator=(const CopyWrapper &o) noexcept
{
if (this != &o) {
this->_deleter(this->data_, static_cast<Base *>(this));
if (sizeof(Base) > sizeof(this->data_))
(Base &)(*this) = std::move(o);
this->data_ = this->_copy(o.data_);
}
return *this;
}
CopyWrapper(CopyWrapper &&o) noexcept = default;
CopyWrapper &operator=(CopyWrapper &&o) noexcept = default;
// also accept from corresponding Reference (also based on Base)
CopyWrapper(const Base &o) noexcept : super()
{
this->data_ = this->_copy(((CopyWrapper &)o).data_);
}
CopyWrapper(Base &&o) noexcept : super()
{
(super &)(*this) = std::move((super &)o);
}
};
template<typename CType, typename Base>
using SelectWrapper =
typename std::conditional<repository::enable_boxed_copy<CType>::value,
CopyWrapper<Base>, MoveWrapper<Base>>::type;
// basis for registered boxed types
template<typename CppType, typename CType,
typename Base = GBoxedWrapperBase<CppType, CType>, typename RefType = void>
class GBoxedWrapper : public SelectWrapper<CType, Base>
{
typedef SelectWrapper<CType, Base> super;
public:
typedef RefType ReferenceType;
using super::super;
GBoxedWrapper(std::nullptr_t = nullptr) {}
void allocate_()
{
if (this->data_)
return;
// make sure we match boxed allocation with boxed free
// still guessing here that all-0 makes for a decent init :-(
CType tmp;
memset(&tmp, 0, sizeof(tmp));
this->data_ = (CType *)g_boxed_copy(this->get_type_(), &tmp);
}
// essentially g_boxed_copy
CppType copy_() const
{
return super::template wrap<CppType>(this->gobj_copy_());
}
};
template<typename CppType, typename CType>
using CBoxedWrapperBase =
BoxedWrapper<CType, CBoxedFuncs<CppType, CType>, CBoxed>;
// basis for non-registered plain C boxed type
template<typename CppType, typename CType,
typename Base = CBoxedWrapperBase<CppType, CType>, typename RefType = void>
class CBoxedWrapper : public MoveWrapper<Base>
{
typedef Base super;
public:
typedef RefType ReferenceType;
CBoxedWrapper(std::nullptr_t = nullptr) {}
void allocate_()
{
if (this->data_)
return;
this->data_ = g_new0(CType, 1);
}
static CppType new_()
{
return super::template wrap<CppType>(g_new0(CType, 1));
}
};
// allocate helper;
// dispatch to method if available
template<typename T, typename Enable = void>
struct allocator : public std::false_type
{
static void allocate(T &) {}
};
template<typename T>
struct allocator<T, decltype(T().allocate_())> : public std::true_type
{
static void allocate(T &v) { v.allocate_(); }
};
template<typename T>
void
allocate(T &v)
{
allocator<T>::allocate(v);
}
// basis for ref-holding to registered box type
template<typename CppType, typename CType, typename Base>
class GBoxedRefWrapper : public Base
{
typedef Base super;
public:
typedef CppType BoxType;
GBoxedRefWrapper(std::nullptr_t = nullptr) {}
// construct from owning version, but not the other way around
// (which requires an explicit copy)
GBoxedRefWrapper(const CppType &other)
{
(super &)(*this) = super::template wrap<super>(other.gobj_());
}
// support way to convert to owning box
// (by means of copy as opposed to a simple ref)
CppType copy_() const
{
return super::template wrap<CppType>(this->gobj_copy_());
}
};
// basis for ref-holding to non-registered plain C boxed type
template<typename CppType, typename CType, typename Base>
class CBoxedRefWrapper : public Base
{
typedef Base super;
public:
typedef CppType BoxType;
CBoxedRefWrapper(std::nullptr_t = nullptr) {}
// construct from owning version, but not the other way around
// (which requires an explicit copy)
CBoxedRefWrapper(const CppType &other)
{
(super &)(*this) = super::template wrap<super>(other.gobj_());
}
};
} // namespace detail
} // namespace gi
#endif // GI_BOXED_HPP

View File

@@ -0,0 +1,960 @@
#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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
#ifndef GI_ENUMFLAG_HPP
#define GI_ENUMFLAG_HPP
#include "base.hpp"
#include "exception.hpp"
#include "value.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
struct EnumValueTraits
{
typedef GEnumClass class_type;
typedef GEnumValue value_type;
static class_type *get_class(GType gtype)
{
auto c = g_type_class_peek(gtype);
return (class_type *)(c ? G_ENUM_CLASS(c) : c);
}
static value_type *get_value(class_type *klass, int v)
{
return g_enum_get_value(klass, v);
}
static value_type *get_by_name(class_type *klass, const char *name)
{
return g_enum_get_value_by_name(klass, name);
}
static value_type *get_by_nick(class_type *klass, const char *name)
{
return g_enum_get_value_by_nick(klass, name);
}
};
struct FlagsValueTraits
{
typedef GFlagsClass class_type;
typedef GFlagsValue value_type;
static class_type *get_class(GType gtype)
{
auto c = g_type_class_peek(gtype);
return (class_type *)(c ? G_FLAGS_CLASS(c) : c);
}
static value_type *get_value(class_type *klass, int v)
{
return g_flags_get_first_value(klass, v);
}
static value_type *get_by_name(class_type *klass, const char *name)
{
return g_flags_get_value_by_name(klass, name);
}
static value_type *get_by_nick(class_type *klass, const char *name)
{
return g_flags_get_value_by_nick(klass, name);
}
};
template<typename EnumType, typename Traits>
class EnumValue
{
typedef EnumValue self;
typedef typename Traits::value_type value_type;
typedef typename Traits::class_type class_type;
value_type *value_;
static class_type *get_class()
{
auto c = Traits::get_class(get_type_());
if (!c) {
detail::try_throw(std::invalid_argument("unknown class"));
} else {
return c;
}
}
EnumValue(gint v) : EnumValue(static_cast<EnumType>(v)) {}
EnumValue(value_type *_value) : value_(_value) {}
public:
typedef EnumType enum_type;
static GType get_type_() { return traits::gtype<EnumType>::get_type(); }
EnumValue(EnumType v) : value_(Traits::get_value(get_class(), (int)v))
{
if (!value_)
detail::try_throw(std::invalid_argument(
"invalid value " + std::to_string((unsigned)v)));
}
static self get_by_name(const gi::cstring_v name)
{
auto v = Traits::get_by_name(get_class(), name.c_str());
if (!v)
detail::try_throw(std::invalid_argument("unknown name"));
else
return self(v);
}
static self get_by_nick(const gi::cstring_v nick)
{
auto v = Traits::get_by_nick(get_class(), nick.c_str());
if (!v)
detail::try_throw(std::invalid_argument("unknown nick"));
else
return self(v);
}
operator EnumType() const noexcept
{
return static_cast<EnumType>(value_->value);
}
gi::cstring_v value_name() const noexcept { return value_->value_name; }
gi::cstring_v value_nick() const noexcept { return value_->value_nick; }
};
} // namespace detail
// enumeration nick/name helpers
template<typename EnumType>
using EnumValue = detail::EnumValue<EnumType, detail::EnumValueTraits>;
template<typename EnumType,
typename std::enable_if<traits::is_enumeration<EnumType>::value &&
traits::gtype<EnumType>::value>::type * = nullptr>
inline EnumValue<EnumType>
value_info(EnumType v)
{
return EnumValue<EnumType>(v);
}
// bitfield nick/name helpers
template<typename EnumType>
using FlagsValue = detail::EnumValue<EnumType, detail::FlagsValueTraits>;
template<typename EnumType,
typename std::enable_if<traits::is_bitfield<EnumType>::value &&
traits::gtype<EnumType>::value>::type * = nullptr>
inline FlagsValue<EnumType>
value_info(EnumType v)
{
return FlagsValue<EnumType>(v);
}
// bitfield operation helpers
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType
operator|(FlagType lhs, FlagType rhs)
{
return static_cast<FlagType>(
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType
operator&(FlagType lhs, FlagType rhs)
{
return static_cast<FlagType>(
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType
operator^(FlagType lhs, FlagType rhs)
{
return static_cast<FlagType>(
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType
operator~(FlagType flags)
{
return static_cast<FlagType>(~static_cast<unsigned>(flags));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType &
operator|=(FlagType &lhs, FlagType rhs)
{
return (lhs = static_cast<FlagType>(
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType &
operator&=(FlagType &lhs, FlagType rhs)
{
return (lhs = static_cast<FlagType>(
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)));
}
template<typename FlagType,
typename std::enable_if<traits::is_bitfield<FlagType>::value>::type * =
nullptr>
inline FlagType &
operator^=(FlagType &lhs, FlagType rhs)
{
return (lhs = static_cast<FlagType>(
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)));
}
} // namespace gi
// sadly, the above templates are not picked up by ADL
// and might therefore only end up used in case of a `using namespace gi`
// so also explicitly add operators in generated namespace
// (by means of macro to simplify generation)
// see macro interface
/* NOTE:
*
* enumeration/bitfield TypeX is currently represented by an enum class TypeX
* along with a namespace TypeXNS_ that holds the member functions.
*
* This could be merged into single class TypeX
* (possibly using templated helpers);
*
* class TypeX
* {
* enum Enum {
* VALUE_A,
* VALUE_B
* }
* Enum value;
*
* (or C++17):
* inline static const TypeX VALUE_A;
* (note; constexpr not possible with incomplete type)
*
* constructor (Enum)
* operator Enum()
* ... etc ...
* std::string value_name(); [opt]
* std::string value_nick(); [opt]
* }
*
* Disadvantage is that TypeX::VALUE_A would not have type TypeX,
* although easily converted from/to TypeX, and so it would mostly work.
* However, the operators above would have to be (even more) complicated and
* accept various combinations of TypeX and TypeX::Enum (in case of a bitfield).
*
* All in all, the type mismatch (and moderately rare member function
* occurrence) does not warrant going this way at this time. Also avoid C++17
* for now, so as it stands ...
*/
#endif // GI_ENUMFLAG_HPP

View File

@@ -0,0 +1,170 @@
#ifndef GI_EXCEPTION_HPP
#define GI_EXCEPTION_HPP
#include "base.hpp"
#include "wrap.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
inline std::logic_error
transform_error(GType tp, const char *name = nullptr)
{
auto n = g_type_name(tp);
auto msg = std::string("could not transform value to type ") +
detail::make_string(n);
if (name)
msg += std::string(" of property \'") + name + "\'";
return std::invalid_argument(msg);
}
inline std::logic_error
unknown_property_error(GType tp, const gchar *property)
{
auto n = g_type_name(tp);
auto msg = std::string("object of type ") + detail::make_string(n) +
" does not have property \'" + detail::make_string(property) +
"\'";
return std::invalid_argument(msg);
}
inline std::logic_error
unknown_signal_error(GType tp, const std::string &name)
{
auto n = g_type_name(tp);
auto msg = std::string("object of type ") + detail::make_string(n) +
" does not have signal \'" + name + "\'";
return std::invalid_argument(msg);
}
inline std::logic_error
invalid_signal_callback_error(
GType tp, const std::string &name, const std::string &_msg)
{
auto n = g_type_name(tp);
auto msg = std::string("invalid callback for signal ") + n + "::" + name +
"; " + _msg;
return std::invalid_argument(msg);
}
// partially generated GError wrapper
class Error : public gi::detail::GBoxedWrapperBase<Error, GError>
{
typedef gi::detail::GBoxedWrapperBase<Error, GError> super_type;
public:
Error(GError *obj = nullptr) : super_type(obj) {}
static GType get_type_() G_GNUC_CONST { return g_error_get_type(); }
// use with care; dangling reference caution applies here
gint &code_() { return gobj_()->code; }
const gint &code_() const { return gobj_()->code; }
// use with care; dangling reference caution applies here
gi::cstring_v message_() const { return gobj_()->message; }
// gboolean g_error_matches (const GError* error, GQuark domain, gint code);
inline bool matches(GQuark domain, gint code) const
{
return g_error_matches(gobj_(), domain, code);
}
}; // class
} // namespace detail
namespace repository
{
namespace GLib
{
class Error_Ref;
class Error
: public std::runtime_error,
public detail::GBoxedWrapper<Error, ::GError, detail::Error, Error_Ref>
{
typedef std::runtime_error super;
static inline std::string make_message(GError *error)
{
return error ? detail::make_string(g_quark_to_string(error->domain)) +
": " + detail::make_string(error->message) + "(" +
std::to_string(error->code) + ")"
: "";
}
public:
explicit Error(GError *obj = nullptr) : super(make_message(obj))
{
data_ = obj;
}
// GError* g_error_new_literal (GQuark domain, gint code, const gchar*
// message);
static inline Error new_literal(
GQuark domain, gint code, const std::string &message)
{
return Error(g_error_new_literal(
domain, code, gi::unwrap(message, gi::transfer_none)));
}
// GError* g_error_copy (const GError* error);
inline Error copy() const { return Error(g_error_copy(gobj_())); }
// override wrap since we are no longer in a simple single-base case
template<typename Cpp, typename Enable = typename std::enable_if<
std::is_base_of<Error, Cpp>::value>::type>
static Cpp wrap(const typename Cpp::BaseObjectType *obj)
{
static_assert(sizeof(Cpp) == sizeof(Error), "type wrap not supported");
Error w(const_cast<GError *>(obj));
return std::move(*static_cast<Cpp *>(&w));
}
};
class Error_Ref
: public gi::detail::GBoxedRefWrapper<GLib::Error, ::GError, detail::Error>
{
typedef gi::detail::GBoxedRefWrapper<GLib::Error, ::GError, detail::Error>
super_type;
using super_type::super_type;
// GError* g_error_copy (const GError* error);
inline Error copy() const { return Error(g_error_copy(gobj_())); }
};
} // namespace GLib
template<>
struct declare_cpptype_of<GError>
{
typedef GLib::Error type;
};
} // namespace repository
inline void
check_error(GError *error)
{
if (error)
detail::try_throw(repository::GLib::Error(error));
}
namespace detail
{
inline repository::GLib::Error
missing_symbol_error(const std::string &symbol)
{
::GQuark domain = g_quark_from_static_string("gi-error-quark");
auto error =
g_error_new(domain, 0, "could not find symbol %s", symbol.c_str());
return repository::GLib::Error(error);
}
} // namespace detail
} // namespace gi
#endif // GI_EXCEPTION_HPP

View File

@@ -0,0 +1,112 @@
#ifndef GI_EXPECTED_HPP
#define GI_EXPECTED_HPP
#include "base.hpp"
#include "exception.hpp"
GI_MODULE_EXPORT
namespace gi
{
// alias so we might route to a std type some day ...
template<typename T, typename E>
#ifdef expected_lite_VERSION
using expected = nonstd::expected<T, E>;
#else
using expected = std::expected<T, E>;
#endif
template<typename E>
#ifdef expected_lite_VERSION
using unexpected = nonstd::unexpected_type<E>;
#else
using unexpected = std::unexpected<E>;
#endif
// standardize on glib error
template<typename T>
using result = expected<T, repository::GLib::Error>;
namespace detail
{
// only use nonstd if it does not delegate to std (in incomplete way)
#if defined(expected_lite_VERSION) && !nsel_USES_STD_EXPECTED
inline unexpected<repository::GLib::Error>
make_unexpected(repository::GLib::Error error)
{
assert(error);
return nonstd::make_unexpected(std::move(error));
}
#else
inline unexpected<repository::GLib::Error>
make_unexpected(repository::GLib::Error error)
{
assert(error);
return std::unexpected<repository::GLib::Error>(std::move(error));
}
#endif
inline unexpected<repository::GLib::Error>
make_unexpected(GError *error)
{
assert(error);
return make_unexpected(repository::GLib::Error(error));
}
} // namespace detail
// no forwarding reference; T must be non-reference type
template<typename T>
result<T>
make_result(T t, GError *error)
{
if (error)
return detail::make_unexpected(error);
return t;
}
// rough helpers to unwrap result/expected
// unwrap by move
template<typename T>
T
expect(gi::result<T> &&t)
{
if (!t) {
g_critical("error result %s", t.error().what());
detail::try_throw(std::move(t.error()));
}
return std::move(*t);
}
namespace detail
{
template<typename T>
void test_result(const gi::result<T> &);
template<typename T>
int test_result(const T &);
template<typename T>
using is_result = std::is_same<void,
decltype(test_result(std::forward<T>(std::declval<T>())))>;
} // namespace detail
// should only be used for a non-result
// (e.g. avoid l-value result ending up here)
template<typename T, typename Enable = typename std::enable_if<
!detail::is_result<T>::value>::type>
T
expect(T &&t)
{
return std::forward<T>(t);
}
template<typename T>
struct rv
{
#if GI_DL && GI_EXPECTED
using type = gi::result<T>;
#else
using type = T;
#endif
};
} // namespace gi
#endif // GI_EXPECTED_HPP

20
cmake/external/glib/cppgir/gi/gi.hpp vendored Normal file
View File

@@ -0,0 +1,20 @@
#ifndef GI_HPP
#define GI_HPP
#include "base.hpp"
#include "container.hpp"
#include "enumflag.hpp"
#include "exception.hpp"
#include "expected.hpp"
#include "object.hpp"
#include "objectclass.hpp"
#include "string.hpp"
// check that include path has been setup properly to include override
#if defined(__has_include)
#if !__has_include(<glib/glib_extra_def.hpp>)
#warning "overrides not found in include path"
#endif
#endif
#endif // GI_HPP

283
cmake/external/glib/cppgir/gi/gi_inc.hpp vendored Normal file
View File

@@ -0,0 +1,283 @@
#ifndef GI_INC_HPP
#define GI_INC_HPP
// gi preprocessor/macro interface
// aka global module fragment part
#define GI_VERSION_MAJAOR (2)
#define GI_VERSION_MINOR (0)
#define GI_VERSION_MICRO (0)
#ifdef GI_INLINE
#define GI_INLINE_DECL inline
#else
#define GI_INLINE_DECL
#endif
// == we could be included in some module setting
#ifdef GI_MODULE_IN_INTERFACE
#if __cplusplus < 201703L
#error "need at least C++17 for modules"
#endif
#define GI_MODULE_EXPORT export
#define GI_MODULE_INLINE inline
#define GI_MODULE_STATIC_OR_INLINE inline
#else // GI_MODULE_IN_INTERFACE
#define GI_MODULE_EXPORT
#define GI_MODULE_INLINE
#define GI_MODULE_STATIC_OR_INLINE static
#endif
// only used in module context
#ifdef GI_MODULE_EXTERN
#define GI_MODULE_EXTERN_BEGIN extern "C++" {
#define GI_MODULE_EXTERN_END }
#else
#define GI_MODULE_EXTERN_BEGIN
#define GI_MODULE_EXTERN_END
#endif
// related; another clang warning
// https://github.com/llvm/llvm-project/issues/68615
// `#include <filename>' attaches the declarations to the named module`
#ifdef __clang__
#define GI_MODULE_DISABLE_WARN_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Winclude-angled-in-module-purview\"")
#define GI_MODULE_DISABLE_WARN_END _Pragma("GCC diagnostic pop")
#else
#define GI_MODULE_DISABLE_WARN_BEGIN
#define GI_MODULE_DISABLE_WARN_END
#endif
#define GI_MODULE_BEGIN \
GI_MODULE_DISABLE_WARN_BEGIN \
GI_MODULE_EXTERN_BEGIN
#define GI_MODULE_END \
GI_MODULE_EXTERN_END \
GI_MODULE_DISABLE_WARN_BEGIN
// ==
// lots of declarations might be attributed as deprecated,
// but not so annotated, so let's avoid warning floods
// also handle complaints about const qualified casts
// (due to silly const qualified scalar parameters)
#define GI_DISABLE_DEPRECATED_WARN_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"")
#define GI_DISABLE_DEPRECATED_WARN_END \
_Pragma("GCC diagnostic pop") _Pragma("GCC diagnostic pop")
// typically clang might warn but gcc might complain about pragma clang ...
#ifdef GI_CLASS_IMPL_PRAGMA
#ifndef GI_CLASS_IMPL_BEGIN
#define GI_CLASS_IMPL_BEGIN \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"")
#endif
#ifndef GI_CLASS_IMPL_END
#define GI_CLASS_IMPL_END _Pragma("GCC diagnostic pop")
#endif
#else
#define GI_CLASS_IMPL_BEGIN
#define GI_CLASS_IMPL_END
#endif
// attempt to auto-discover exception support:
#ifndef GI_CONFIG_EXCEPTIONS
#if defined(_MSC_VER)
#include <cstddef> // for _HAS_EXCEPTIONS
#endif
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
#define GI_CONFIG_EXCEPTIONS 1
#else
#define GI_CONFIG_EXCEPTIONS 0
#endif
#endif
#include <cstddef>
#include <exception>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <iterator>
#include <map>
#include <vector>
#if __has_include("nonstd/expected.hpp")
#if __cpp_concepts >= 202002L
// this is also required in gcc's libstdc++ expected
#else
// so if that is missing, then prevent delegation to <expected>
#define nsel_CONFIG_SELECT_EXPECTED nsel_EXPECTED_NONSTD
#endif
#include "nonstd/expected.hpp"
#else
#include <expected>
#endif
#if __cplusplus >= 201703L
#include <optional>
#include <string_view>
#endif
// define << operator if not unwanted
#ifndef GI_NO_STRING_IOS
#include <iostream>
#endif
#if GI_DL
#include <dlfcn.h>
#endif
#include <assert.h>
#include <string.h>
#include <glib-object.h>
#include <glib.h>
#include <gobject/gvaluecollector.h>
// various macros from all over the place
// sadly, module refactor means breaking things apart
// exception
// exception specification is generated according to settings and situation
// some derived code (e.g. overrides) may need to follow suit accordingly
#if GI_EXPECTED
// no exception if reported through expected
#define GI_NOEXCEPT_DECL(nonthrowing) noexcept
#elif GI_DL
// otherwise, everything can start failing if resolved at runtime
#define GI_NOEXCEPT_DECL(nonthrowing)
#else
// otherwise, depends on whether (wrapped) function is (GError) throwing
#define GI_NOEXCEPT_DECL(nonthrowing) noexcept(nonthrowing)
#endif
// callback
#define GI_CB_ARG_CALLBACK_CUSTOM(Type, CF_CType, CF_handler) \
struct Type \
{ \
using handler_cb_type = CF_CType; \
static constexpr auto handler = CF_handler; \
}
// boxed
// should be used within gi.repository namespace
#define GI_ENABLE_BOXED_COPY(CType) \
template<> \
struct enable_boxed_copy<CType> : public std::true_type \
{};
// enumflag
#define GI_FLAG_OPERATORS(FlagType) \
inline FlagType operator|(FlagType lhs, FlagType rhs) \
{ \
return static_cast<FlagType>( \
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs)); \
} \
\
inline FlagType operator&(FlagType lhs, FlagType rhs) \
{ \
return static_cast<FlagType>( \
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs)); \
} \
\
inline FlagType operator^(FlagType lhs, FlagType rhs) \
{ \
return static_cast<FlagType>( \
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs)); \
} \
\
inline FlagType operator~(FlagType flags) \
{ \
return static_cast<FlagType>(~static_cast<unsigned>(flags)); \
} \
\
inline FlagType &operator|=(FlagType &lhs, FlagType rhs) \
{ \
return (lhs = static_cast<FlagType>( \
static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs))); \
} \
\
inline FlagType &operator&=(FlagType &lhs, FlagType rhs) \
{ \
return (lhs = static_cast<FlagType>( \
static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs))); \
} \
\
inline FlagType &operator^=(FlagType &lhs, FlagType rhs) \
{ \
return (lhs = static_cast<FlagType>( \
static_cast<unsigned>(lhs) ^ static_cast<unsigned>(rhs))); \
}
#define GI_ENUM_NUMERIC(EnumType) \
inline std::underlying_type<EnumType>::type operator+(EnumType e) \
{ \
return static_cast<std::underlying_type<EnumType>::type>(e); \
}
// objectclass
// helper macro to obtain data from factory (if provided)
#define GI_MEMBER_INIT_DATA(ClassType, factory) \
(factory ? ((ClassType(*)())(factory))() : ClassType());
// conflicts might arise between interfaces and/or class
// generate some dummy check types to force failure
#define GI_MEMBER_CHECK_CONFLICT(member) _check_member_conflict_##member
// generated code tries to detect a defined member in SubClass as follows
#define GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member) \
template<typename SubClass> \
constexpr static bool has_definition(const member##_t *, const SubClass *) \
{ \
/* the use of conflict type check is only to trigger a compiler error \
* if there is such a conflict \
* in that case, manual specification of definitions are needed \
* (which will then avoid this code path instantiation) \
* (type should never be void, so merely serves as dummy check) \
*/ \
return std::is_void<typename SubClass::GI_MEMBER_CHECK_CONFLICT( \
member)>::value || \
!std::is_same<decltype(&BaseDef::member##_), \
decltype(&SubClass::member##_)>::value; \
}
// helper macro used in generated code
#define GI_MEMBER_DEFINE(BaseDef, member) \
struct member##_tag; \
using member##_t = detail::member_type<member##_tag>; \
member##_t member; \
GI_MEMBER_DEFAULT_HAS_DEFINITION(BaseDef, member)
// the automated way might/will fail in case of overload resolution failure
// (due to member conflicts with interfaces)
// so the following can be used to specify definition situation
// should be used in an inner struct DefinitionData in the SubClass
#define GI_DEFINES_MEMBER(BaseDef, member, _defines) \
template<typename SubClass> \
constexpr static bool defines( \
const BaseDef::TypeInitData::member##_t *, const SubClass *) \
{ \
return _defines; \
}
// uses function overload on all of the above to determine
// if member of DefData is defined/overridden in SubClass
// (and should then be registered in the class/interface struct)
#define GI_MEMBER_HAS_DEFINITION(SubClass, DefData, member) \
DefData::defines((member##_t *)(nullptr), (SubClass *)(nullptr))
#endif // GI_INC_HPP

694
cmake/external/glib/cppgir/gi/object.hpp vendored Normal file
View File

@@ -0,0 +1,694 @@
#ifndef GI_OBJECT_HPP
#define GI_OBJECT_HPP
#include "callback.hpp"
#include "container.hpp"
#include "exception.hpp"
#include "objectbase.hpp"
#include "paramspec.hpp"
#include "value.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
// helper
// signal argument connect/emit helper;
// turn (C++) argument into a GType or GValue
// most arguments are inputs (with specific GType),
// but some arguments are used as output with G_TYPE_POINTER (e.g. int*)
// which are mapped to (e.g.) int* or int& in C++ signature
template<typename Arg, bool DECAY>
struct signal_arg
{
static GType get_type() { return traits::gtype<Arg>::get_type(); }
static detail::Value make(Arg arg)
{
return detail::Value(std::forward<Arg>(arg));
}
};
// re-route e.g. const std::string& cases
template<typename Arg>
struct signal_arg<const Arg &, false> : public signal_arg<const Arg &, true>
{};
template<typename Arg>
struct signal_arg<Arg &, false>
{
static GType get_type() { return G_TYPE_POINTER; }
static detail::Value make(Arg &arg)
{
return signal_arg<Arg *, false>::make(&arg);
}
};
template<typename Arg>
struct signal_arg<Arg *, false>
{
static GType get_type() { return G_TYPE_POINTER; }
static detail::Value make(Arg *arg)
{
// (size of) wrapper argument should match wrappee
static_assert(sizeof(typename traits::ctype<Arg>::type) == sizeof(Arg), "");
// the above should suffice for proper handling
// however, in these output cases, transfer should also be considered,
// which is not (yet) available here
// (but could be passed along similar to callback argument info)
// so, restrict to plain cases for now
static_assert(traits::is_plain<Arg>::value, "");
return detail::Value(gpointer(arg));
}
};
// returns -size if signed numeric, +size if unsigned numeric, otherwise 0
inline int
get_number_size_signed(GType type)
{
// note; these are generally lower (absolute) bounds
// at least it works in the context where it is used below
#define GI_HANDLE_TYPE_SWITCH(cpptype, g_type, factor) \
case g_type: \
return factor * int(sizeof(cpptype));
switch (type) {
GI_HANDLE_TYPE_SWITCH(gchar, G_TYPE_CHAR, -1)
GI_HANDLE_TYPE_SWITCH(guchar, G_TYPE_UCHAR, 1)
GI_HANDLE_TYPE_SWITCH(gint, G_TYPE_INT, -1)
GI_HANDLE_TYPE_SWITCH(guint, G_TYPE_UINT, 1)
GI_HANDLE_TYPE_SWITCH(glong, G_TYPE_LONG, -1)
GI_HANDLE_TYPE_SWITCH(gulong, G_TYPE_ULONG, 1)
GI_HANDLE_TYPE_SWITCH(gint64, G_TYPE_INT64, -1)
GI_HANDLE_TYPE_SWITCH(guint64, G_TYPE_UINT64, 1)
}
#undef GI_HANDLE_TYPE_SWITCH
return 0;
}
// glib type systems treats G_TYPE_INT64 as distinct from the other types
// in practice, however, quite likely C gint64 == long
inline bool
compatible_type(GType expected, GType actual)
{
if (expected == G_TYPE_BOOLEAN)
return std::abs(get_number_size_signed(actual)) == sizeof(gboolean);
auto ssa_e = get_number_size_signed(expected);
auto ssa_a = get_number_size_signed(actual);
return ssa_e == ssa_a;
}
inline void
check_signal_type(GType tp, const gi::cstring_v name, GType return_type,
GType *param_types, guint n_params)
{
const char *errmsg("expected ");
auto check_types = [tp, &name, &errmsg](const std::string &desc,
GType expected, GType actual) {
// normalize
expected &= ~G_SIGNAL_TYPE_STATIC_SCOPE;
actual &= ~G_SIGNAL_TYPE_STATIC_SCOPE;
if (expected == actual || compatible_type(expected, actual) ||
g_type_is_a(expected, actual))
return;
std::string msg = errmsg;
msg += desc + " type ";
msg += detail::make_string(g_type_name(expected)) + " != ";
msg += detail::make_string(g_type_name(actual));
detail::try_throw(invalid_signal_callback_error(tp, name, msg));
};
// determine signal (detail)
guint id;
GQuark detail;
if (!g_signal_parse_name(name.c_str(), tp, &id, &detail, false) || (id == 0))
detail::try_throw(unknown_signal_error(tp, name));
// get signal info
GSignalQuery query;
g_signal_query(id, &query);
// check
if (n_params != query.n_params + 1) {
auto msg = std::string(errmsg) + "argument count ";
msg += std::to_string(query.n_params);
msg += " != " + std::to_string(n_params);
detail::try_throw(invalid_signal_callback_error(tp, name, msg));
}
check_types("return", query.return_type, return_type);
check_types("instance", query.itype, param_types[0]);
const std::string arg("argument ");
for (guint i = 0; i < query.n_params; ++i)
check_types(
arg + std::to_string(i + 1), query.param_types[i], param_types[i + 1]);
}
template<typename G>
struct signal_type;
template<typename R, typename... Args>
struct signal_type<R(Args...)>
{
static void check(GType tp, const gi::cstring_v name)
{
// capture type info and delegate
const int argcount = sizeof...(Args);
GType ti[] = {signal_arg<Args, false>::get_type()...};
check_signal_type(tp, name, traits::gtype<R>::get_type(), ti, argcount);
}
};
// like GParameter, but with extra Value trimming
struct Parameter
{
const char *name;
detail::Value value;
};
#ifdef GI_OBJECT_NEWV
GI_DISABLE_DEPRECATED_WARN_BEGIN
static_assert(sizeof(Parameter) == sizeof(GParameter), "");
GI_DISABLE_DEPRECATED_WARN_END
#endif
inline void
fill_parameters(Parameter *)
{
// no-op
}
template<typename Arg, typename... Args>
inline void
fill_parameters(Parameter *param, const char *name, Arg &&arg, Args &&...args)
{
param->name = name;
param->value.init<typename std::remove_reference<Arg>::type>();
set_value(&param->value, std::forward<Arg>(arg));
fill_parameters(param + 1, std::forward<Args>(args)...);
}
} // namespace detail
#if GLIB_CHECK_VERSION(2, 54, 0)
#define GI_GOBJECT_PROPERTY_VALUE 1
#endif
namespace repository
{
/* if you have arrived here due to an ambiguous GObject reference
* (both the C typedef GObject and this namespace)
* then that can be worked-around by:
* + using _GObject (struct name instead)
* + adjust 'using namespace' style imports e.g. alias
* namespace GObject_ = gi::GObject;
* or simply do not mention GObject at all and simply use the wrappers ;-)
*/
namespace GObject
{
typedef std::vector<detail::Parameter> construct_params;
template<typename... Args>
construct_params
make_construct_params(Args &&...args)
{
const int nparams = sizeof...(Args) / 2;
construct_params parameters;
parameters.resize(nparams);
detail::fill_parameters(parameters.data(), std::forward<Args>(args)...);
return parameters;
}
class Object : public detail::ObjectBase
{
typedef Object self;
typedef detail::ObjectBase super_type;
public:
typedef ::GObject BaseObjectType;
Object(std::nullptr_t = nullptr) : super_type() {}
BaseObjectType *gobj_() { return (BaseObjectType *)super_type::gobj_(); }
const BaseObjectType *gobj_() const
{
return (const BaseObjectType *)super_type::gobj_();
}
BaseObjectType *gobj_copy_() const
{
return (BaseObjectType *)super_type::gobj_copy_();
}
// class type
static GType get_type_() { return G_TYPE_OBJECT; }
// instance type
GType gobj_type_() const { return G_OBJECT_TYPE(gobj_()); }
// type-erased generic object creation
// transfer full return
static gpointer new_(GType gtype, const construct_params &params)
{
#ifdef GI_OBJECT_NEWV
GI_DISABLE_DEPRECATED_WARN_BEGIN
auto result =
g_object_newv(gtype, params.size(), (GParameter *)params.data());
GI_DISABLE_DEPRECATED_WARN_END
#else
std::vector<const char *> names;
std::vector<GValue> values;
names.reserve(params.size());
values.reserve(params.size());
// ownership remains in params
for (auto &&p : params) {
names.push_back(p.name);
values.emplace_back(p.value);
}
auto result = g_object_new_with_properties(
gtype, params.size(), names.data(), values.data());
#endif
// GIR says transfer full, but let's be careful and really make it so
// if likely still floating, then we assume ownership
// but if it is no longer, then it has already been stolen (e.g.
// GtkWindow), and we need to add one here
if (g_type_is_a(gtype, G_TYPE_INITIALLY_UNOWNED))
g_object_ref_sink(result);
return result;
}
// type-based generic object creation
template<typename CTYPE, typename... Args>
static auto new_(GType gtype, Args &&...args)
{
auto parameters = make_construct_params(std::forward<Args>(args)...);
auto *result = CTYPE(new_(gtype, parameters));
return gi::wrap(result, transfer_full);
}
// type-based generic object creation
// Args are a sequence of name, value
template<typename TYPE, typename... Args>
static TYPE new_(Args &&...args)
{
return new_<typename TYPE::BaseObjectType *>(
TYPE::get_type_(), std::forward<Args>(args)...);
}
// property stuff
// generic type unsafe
template<typename V>
self &set_property(ParamSpec _pspec, V &&val)
{
// additional checks
// allows for basic conversion between arithmetic types
// without worrying about those details
auto pspec = _pspec.gobj_();
detail::Value v(std::forward<V>(val));
detail::Value dest;
GValue *p = &v;
if (G_VALUE_TYPE(&v) != pspec->value_type) {
g_value_init(&dest, pspec->value_type);
if (!g_value_transform(&v, &dest))
detail::try_throw(
detail::transform_error(pspec->value_type, pspec->name));
p = &dest;
}
g_object_set_property(gobj_(), pspec->name, p);
return *this;
}
template<typename V>
self &set_property(const gi::cstring_v propname, V &&val)
{
return set_property<V>(find_property(propname, true), std::forward<V>(val));
}
template<typename V>
self &set_properties(const gi::cstring_v propname, V &&val)
{
return set_property<V>(propname, std::forward<V>(val));
}
// set a number of props
template<typename V, typename... Args>
self &set_properties(const gi::cstring_v propname, V &&val, Args... args)
{
g_object_freeze_notify(gobj_());
#if GI_CONFIG_EXCEPTIONS
try {
#endif
set_property(propname, std::forward<V>(val));
set_properties(std::forward<Args>(args)...);
#if GI_CONFIG_EXCEPTIONS
} catch (...) {
g_object_thaw_notify(gobj_());
throw;
}
#endif
g_object_thaw_notify(gobj_());
return *this;
}
#ifdef GI_GOBJECT_PROPERTY_VALUE
self &set_property(const gi::cstring_v propname, Value val)
{
g_object_set_property(gobj_(), propname.c_str(), val.gobj_());
return *this;
}
#endif
template<typename V>
V get_property(const char *propname) const
{
// this would return a ref to what is owned by stack-local v below
static_assert(!traits::is_reftype<V>::value, "dangling ref");
detail::Value v;
v.init<V>();
// the _get_ already tries to transform
// also close enough to const
g_object_get_property(const_cast<::GObject *>(gobj_()), propname, &v);
return detail::get_value<V>(&v);
}
template<typename V>
V get_property(const gi::cstring_v propname) const
{
return get_property<V>(propname.c_str());
}
#ifdef GI_GOBJECT_PROPERTY_VALUE
Value get_property(const gi::cstring_v propname) const
{
Value result;
const gchar *name = propname.c_str();
GValue *val = result.gobj_();
g_object_getv(const_cast<::GObject *>(gobj_()), 1, &name, val);
return result;
}
#endif
static ParamSpec find_property(
GType gtype, const gi::cstring_v propname, bool _throw = false)
{
GParamSpec *spec;
if (g_type_is_a(gtype, G_TYPE_INTERFACE)) {
// interface should be loaded if we have an instance here
auto vtable = g_type_default_interface_peek(gtype);
spec = g_object_interface_find_property(vtable, propname.c_str());
} else {
spec = g_object_class_find_property(
(GObjectClass *)g_type_class_peek(gtype), propname.c_str());
}
if (_throw && !spec)
detail::try_throw(
detail::unknown_property_error(gtype, propname.c_str()));
return gi::wrap(spec, transfer_none);
}
ParamSpec find_property(
const gi::cstring_v propname, bool _throw = false) const
{
return find_property(gobj_type_(), propname, _throw);
}
gi::Collection<gi::DSpan, GParamSpec *, gi::transfer_container_t>
list_properties() const
{
GParamSpec **specs;
guint nspecs = 0;
if (g_type_is_a(gobj_type_(), G_TYPE_INTERFACE)) {
// interface should be loaded if we have an instance here
auto vtable = g_type_default_interface_peek(gobj_type_());
specs = g_object_interface_list_properties(vtable, &nspecs);
} else {
specs =
g_object_class_list_properties(G_OBJECT_GET_CLASS(gobj_()), &nspecs);
}
return wrap_to<
gi::Collection<gi::DSpan, GParamSpec *, gi::transfer_container_t>>(
specs, nspecs, transfer_container);
}
// signal stuff
private:
template<typename F, typename Functor>
gulong connect_data(
const gi::cstring_v signal, Functor &&f, GConnectFlags flags)
{
// runtime signature check
detail::signal_type<F>::check(gobj_type_(), signal);
auto w = new detail::transform_signal_wrapper<F>(std::forward<Functor>(f));
// mind gcc's -Wcast-function-type
return g_signal_connect_data(gobj_(), signal.c_str(),
(GCallback)&w->wrapper, w, (GClosureNotify)(GCallback)&w->destroy,
flags);
}
public:
template<typename F, typename Functor>
gulong connect(const gi::cstring_v signal, Functor &&f)
{
return connect_data<F, Functor>(
signal, std::forward<Functor>(f), (GConnectFlags)0);
}
template<typename F, typename Functor>
gulong connect_after(const gi::cstring_v signal, Functor &&f)
{
return connect_data<F, Functor>(
signal, std::forward<Functor>(f), G_CONNECT_AFTER);
}
// TODO the object variants ??
// in case of unsupported signal signature
// connect using a plain C signature without check/transform (wrap/unwrap)
template<typename F, typename Functor>
gulong connect_unchecked(
const gi::cstring_v signal, Functor &&f, GConnectFlags flags = {})
{
auto w = new detail::callback_wrapper<F>(std::forward<Functor>(f));
// mind gcc's -Wcast-function-type
return g_signal_connect_data(gobj_(), signal.c_str(),
(GCallback)&w->wrapper, w, (GClosureNotify)(GCallback)&w->destroy,
flags);
}
void disconnect(gulong id) { g_signal_handler_disconnect(gobj_(), id); }
// Args... may be explicitly specified or deduced
// if deduced; arrange to decay/strip reference below
// if not deduced; may need to considere specified type as-is
template<typename R, bool DECAY = true, typename... Args>
R emit(const gi::cstring_v signal, Args &&...args)
{
// static constexpr bool DECAY = true;
guint id;
GQuark detail;
if (!g_signal_parse_name(signal.c_str(), gobj_type_(), &id, &detail, true))
detail::try_throw(std::out_of_range(std::string("unknown signal name: ") +
detail::make_string(signal.c_str())));
detail::Value values[] = {detail::Value(*this),
detail::signal_arg<Args, DECAY>::make(std::forward<Args>(args))...};
detail::Value retv;
retv.init<R>();
g_signal_emitv(values, id, detail, &retv);
return detail::get_value<R>(&retv);
}
void handler_block(gulong handler_id)
{
g_signal_handler_block(gobj_(), handler_id);
}
void handler_unblock(gulong handler_id)
{
g_signal_handler_unblock(gobj_(), handler_id);
}
bool handler_is_connected(gulong handler_id)
{
return g_signal_handler_is_connected(gobj_(), handler_id);
}
void stop_emission(guint id, GQuark detail)
{
g_signal_stop_emission(gobj_(), id, detail);
}
void stop_emission_by_name(const gi::cstring_v signal)
{
g_signal_stop_emission_by_name(gobj_(), signal.c_str());
}
};
} // namespace GObject
template<>
struct declare_cpptype_of<::GObject>
{
typedef repository::GObject::Object type;
};
namespace GLib
{
// predefined
typedef detail::callback<void(), gi::transfer_none_t> DestroyNotify;
} // namespace GLib
} // namespace repository
// type safe signal connection
template<typename T, typename Base = repository::GObject::Object>
class signal_proxy;
template<typename R, typename Instance, typename... Args, typename Base>
class signal_proxy<R(Instance, Args...), Base>
{
protected:
typedef R(CppSig)(Instance, Args...);
Base object_;
gi::cstring name_;
public:
typedef CppSig function_type;
typedef detail::connectable<function_type> slot_type;
signal_proxy(Base owner, gi::cstring name)
: object_(owner), name_(std::move(name))
{}
template<typename Functor>
gulong connect(Functor &&f)
{
return object_.template connect<CppSig>(name_, std::forward<Functor>(f));
}
template<typename Functor>
gulong connect_after(Functor &&f)
{
return object_.template connect_after<CppSig>(
name_, std::forward<Functor>(f));
}
R emit(Args... args)
{
return object_.template emit<R, false, Args...>(
name_, std::forward<Args>(args)...);
}
template<typename Functor>
slot_type slot(Functor &&f)
{
return slot_type(std::forward<Functor>(f));
}
};
// type safe property setting
template<typename T, typename Base = repository::GObject::Object>
class property_proxy
{
typedef property_proxy self;
typedef repository::GObject::ParamSpec ParamSpec;
protected:
Base object_;
ParamSpec pspec_;
public:
property_proxy(Base owner, ParamSpec pspec) : object_(owner), pspec_(pspec) {}
property_proxy(Base owner, const gi::cstring_v name)
: property_proxy(owner, owner.find_property(name, true))
{}
void set(T v) { object_.set_property(pspec_, std::move(v)); }
self &operator=(T v)
{
set(v);
return *this;
}
T get() const
{
return object_.template get_property<T>(pspec_.gobj_()->name);
}
ParamSpec param_spec() const { return pspec_; }
signal_proxy<void(Base, ParamSpec)> signal_notify() const
{
return signal_proxy<void(Base, ParamSpec)>(
object_, gi::cstring_v("notify::") + gi::cstring_v(pspec_.name_()));
}
};
template<typename T, typename Base = repository::GObject::Object>
class property_proxy_read : private property_proxy<T, Base>
{
typedef property_proxy<T, Base> super;
public:
using super::get;
using super::property_proxy;
};
template<typename T, typename Base = repository::GObject::Object>
class property_proxy_write : private property_proxy<T, Base>
{
typedef property_proxy<T, Base> super;
public:
using super::property_proxy;
using super::set;
using super::operator=;
};
// interface (ptr) is wrapped the same way,
// as it is essentially a ptr to implementing object
// TODO use other intermediate base ??
using InterfaceBase = repository::GObject::Object;
namespace repository
{
namespace GObject
{
// connection helpers
namespace internal
{
class SignalConnection : public detail::connection_impl
{
public:
SignalConnection(gulong id, detail::connection_status s, Object object)
: connection_impl(id, s), object_(object)
{}
void disconnect() { object_.disconnect(id_); }
private:
Object object_;
};
} // namespace internal
using SignalConnection = detail::connection<internal::SignalConnection>;
using SignalScopedConnection = detail::scoped_connection<SignalConnection>;
} // namespace GObject
} // namespace repository
// connection callback type
template<typename G>
using slot = detail::connectable<G>;
template<typename G>
inline repository::GObject::SignalConnection
make_connection(
gulong id, const gi::slot<G> &s, repository::GObject::Object object)
{
using repository::GObject::SignalConnection;
return SignalConnection(id, s.connection(), object);
}
} // namespace gi
#endif // GI_OBJECT_HPP

View File

@@ -0,0 +1,172 @@
#ifndef GI_OBJECTBASE_HPP
#define GI_OBJECTBASE_HPP
#include "gi_inc.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace detail
{
struct GObjectFuncs
{
static void *ref(void *data) { return g_object_ref(data); }
static void *sink(void *data) { return g_object_ref_sink(data); }
static void free(void *data) { g_object_unref(data); }
static void *float_(void *data)
{
g_object_force_floating((GObject *)data);
return g_object_ref(data);
}
};
template<typename Funcs, typename CType = void>
class Wrapper
{
protected:
CType *data_ = nullptr;
static CType *_ref(CType *data) { return data ? Funcs::ref(data) : data; }
static CType *_sink(CType *data) { return data ? Funcs::sink(data) : data; }
static CType *_float(CType *data)
{
return data ? Funcs::float_(data) : data;
}
static void _deleter(CType *&data)
{
if (data)
Funcs::free(data);
}
public:
Wrapper(decltype(data_) d = nullptr, bool own = true, bool sink = true)
: data_(own ? d : (sink ? _sink(d) : _ref(d)))
{}
~Wrapper() { _deleter(data_); }
Wrapper(const Wrapper &other)
{
_deleter(data_);
data_ = _ref(other.data_);
}
Wrapper(Wrapper &&other) noexcept
{
_deleter(data_);
data_ = other.data_;
other.data_ = nullptr;
}
explicit operator bool() const { return (bool)data_; }
Wrapper &operator=(const Wrapper &other)
{
if (&other != this) {
_deleter(data_);
data_ = _ref(other.data_);
}
return *this;
}
Wrapper &operator=(Wrapper &&other) noexcept
{
if (&other != this) {
_deleter(data_);
data_ = other.data_;
other.data_ = nullptr;
}
return *this;
}
bool operator==(const Wrapper &other) const { return data_ == other.data_; }
bool operator==(std::nullptr_t o) const
{
(void)o;
return data_ == o;
}
bool operator!=(const Wrapper &other) const { return data_ != other.data_; }
bool operator!=(std::nullptr_t o) const { return data_ != o; }
CType *gobj_() { return this->data_; }
const CType *gobj_() const { return this->data_; }
};
class wrapper_tag
{};
template<typename CType, typename Funcs, GType GTYPE_>
class WrapperBase : public Wrapper<Funcs>, public wrapper_tag
{
typedef WrapperBase self;
typedef Wrapper<Funcs> super_type;
public:
typedef CType BaseObjectType;
BaseObjectType *gobj_() { return (BaseObjectType *)this->data_; }
const BaseObjectType *gobj_() const
{
return (const BaseObjectType *)this->data_;
}
BaseObjectType *gobj_copy_() const
{
return (BaseObjectType *)self::_ref(this->data_);
}
BaseObjectType *gobj_float_() const
{
return (BaseObjectType *)self::_float(this->data_);
}
BaseObjectType *release_()
{
void *r = nullptr;
std::swap(this->data_, r);
return (BaseObjectType *)r;
}
static GType get_type_() { return GTYPE_; }
GType gobj_type_() { return GTYPE_; }
WrapperBase(BaseObjectType *p = nullptr, bool own = true, bool argout = true)
: super_type(p, own, argout)
{}
WrapperBase(const self &other) = default;
WrapperBase(self &&other) = default;
self &operator=(const self &other) = default;
self &operator=(self &&other) = default;
// always arrange to sink by default nowadays
template<typename Cpp>
static Cpp wrap(
const typename Cpp::BaseObjectType *obj, bool own, bool argout = true)
{
static_assert(sizeof(Cpp) == sizeof(self), "type wrap not supported");
static_assert(std::is_base_of<self, Cpp>::value, "type wrap not supported");
WrapperBase w((self::BaseObjectType *)(obj), own, argout);
return std::move(*static_cast<Cpp *>(&w));
}
};
typedef WrapperBase<void, GObjectFuncs, G_TYPE_NONE> ObjectBase;
// foundation for Variant that will be generated
struct GVariantFuncs
{
static void *ref(void *data) { return g_variant_ref((GVariant *)data); }
static void *sink(void *data) { return g_variant_ref_sink((GVariant *)data); }
static void free(void *data) { g_variant_unref((GVariant *)data); }
static void *float_(void *data) { return data; }
};
typedef WrapperBase<GVariant, GVariantFuncs, G_TYPE_VARIANT> VariantWrapper;
} // namespace detail
} // namespace gi
#endif // GI_OBJECTBASE_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
#ifndef GI_PARAMSPEC_HPP
#define GI_PARAMSPEC_HPP
#include "objectbase.hpp"
#include "value.hpp"
GI_MODULE_EXPORT
namespace gi
{
// slightly nasty; will be generated
namespace repository
{
namespace GObject
{
enum class ParamFlags : std::underlying_type<::GParamFlags>::type;
}
} // namespace repository
namespace detail
{
struct GParamSpecFuncs
{
static void *ref(void *data) { return g_param_spec_ref((GParamSpec *)data); }
static void *sink(void *data)
{
return g_param_spec_ref_sink((GParamSpec *)data);
}
static void free(void *data) { g_param_spec_unref((GParamSpec *)data); }
static void *float_(void *data) { return data; }
};
using ParamFlags = repository::GObject::ParamFlags;
// helper paramspec type
template<typename T>
struct param_spec_constructor;
#define GI_DECLARE_PARAM_SPEC(cpptype, suffix) \
template<> \
struct param_spec_constructor<cpptype> \
{ \
typedef std::true_type range_type; \
static const constexpr decltype(&g_param_spec_##suffix) new_ = \
g_param_spec_##suffix; \
};
GI_DECLARE_PARAM_SPEC(char, char)
GI_DECLARE_PARAM_SPEC(unsigned char, uchar)
GI_DECLARE_PARAM_SPEC(int, int)
GI_DECLARE_PARAM_SPEC(unsigned int, uint)
GI_DECLARE_PARAM_SPEC(long, long)
GI_DECLARE_PARAM_SPEC(unsigned long, ulong)
GI_DECLARE_PARAM_SPEC(long long, int64)
GI_DECLARE_PARAM_SPEC(unsigned long long, uint64)
GI_DECLARE_PARAM_SPEC(float, float)
GI_DECLARE_PARAM_SPEC(double, double)
#undef GI_DECLARE_PARAM_SPEC
// specialize appropriately with static new_ member
template<typename T, typename Enable = void>
struct ParamSpecFactory;
template<typename T>
struct ParamSpecFactory<T,
typename std::enable_if<param_spec_constructor<T>::range_type::value>::type>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb, T min, T max, T _default,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return param_spec_constructor<T>::new_(name.c_str(), nick.c_str(),
blurb.c_str(), min, max, _default, (GParamFlags)flags);
}
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb, T min, T max,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return new_(name, nick, blurb, min, max, T{}, flags);
}
};
template<>
struct ParamSpecFactory<bool>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb, bool _default,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return g_param_spec_boolean(name.c_str(), nick.c_str(), blurb.c_str(),
_default, (GParamFlags)flags);
}
};
template<>
struct ParamSpecFactory<gpointer>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return g_param_spec_pointer(
name.c_str(), nick.c_str(), blurb.c_str(), (GParamFlags)flags);
}
};
template<>
struct ParamSpecFactory<std::string>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb, const gi::cstring_v _default,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return g_param_spec_string(name.c_str(), nick.c_str(), blurb.c_str(),
_default.c_str(), (GParamFlags)flags);
}
};
template<>
struct ParamSpecFactory<gi::cstring> : public ParamSpecFactory<std::string>
{};
template<typename T>
struct ParamSpecFactory<T,
typename std::enable_if<traits::is_object<T>::value>::type>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return g_param_spec_object(name.c_str(), nick.c_str(), blurb.c_str(),
traits::gtype<T>::get_type(), (GParamFlags)flags);
}
};
template<typename T>
struct ParamSpecFactory<T,
typename std::enable_if<traits::is_boxed<T>::value>::type>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
return g_param_spec_boxed(name.c_str(), nick.c_str(), blurb.c_str(),
traits::gtype<T>::get_type(), (GParamFlags)flags);
}
};
template<typename T>
struct ParamSpecFactory<T,
typename std::enable_if<traits::is_enum_or_bitfield<T>::value>::type>
{
static GParamSpec *new_(const gi::cstring_v name, const gi::cstring_v nick,
const gi::cstring_v blurb, guint _default = 0,
ParamFlags flags = (ParamFlags)G_PARAM_READWRITE)
{
GType t = traits::gtype<T>::get_type();
// FIXME compile-time determination rather than dynamic ??
return G_TYPE_IS_FLAGS(t)
? g_param_spec_flags(name.c_str(), nick.c_str(), blurb.c_str(),
t, _default, (GParamFlags)flags)
: g_param_spec_enum(name.c_str(), nick.c_str(), blurb.c_str(), t,
_default, (GParamFlags)flags);
}
};
} // namespace detail
namespace repository
{
// slightly nasty
namespace GObject
{
class ParamSpec;
}
template<>
struct declare_cpptype_of<GParamSpec>
{
typedef GObject::ParamSpec type;
};
namespace GObject
{
class ParamSpec : public detail::WrapperBase<GParamSpec,
detail::GParamSpecFuncs, G_TYPE_PARAM>
{
typedef WrapperBase<GParamSpec, detail::GParamSpecFuncs, G_TYPE_PARAM> super;
public:
ParamSpec(std::nullptr_t = nullptr) {}
template<typename T, typename... Args>
static ParamSpec new_(Args &&...args)
{
return static_cast<ParamSpec &&>(
super(detail::ParamSpecFactory<T>::new_(std::forward<Args>(args)...)));
}
// special override case
static ParamSpec new_(const gi::cstring_v name, ParamSpec overridden)
{
return static_cast<ParamSpec &&>(
super(g_param_spec_override(name.c_str(), overridden.gobj_())));
}
gi::cstring_v get_blurb() { return g_param_spec_get_blurb(gobj_()); }
gi::cstring_v get_nick() { return g_param_spec_get_nick(gobj_()); }
gi::cstring_v get_name() { return g_param_spec_get_name(gobj_()); }
GQuark get_name_quark() { return g_param_spec_get_name_quark(gobj_()); }
repository::GObject::Value get_default_value()
{
return gi::wrap(g_param_spec_get_default_value(gobj_()), transfer_none);
}
ParamSpec get_redirect_target()
{
return gi::wrap(g_param_spec_get_redirect_target(gobj_()), transfer_none);
}
// struct fields
const gchar *name_() const { return gobj_()->name; }
ParamFlags value_type() const { return (ParamFlags)gobj_()->flags; }
GType value_type_() const { return gobj_()->value_type; }
GType owner_type_() const { return gobj_()->owner_type; }
};
} // namespace GObject
} // namespace repository
} // namespace gi
#endif // GI_PARAMSPEC_HPP

874
cmake/external/glib/cppgir/gi/string.hpp vendored Normal file
View File

@@ -0,0 +1,874 @@
#ifndef GI_STRING_HPP
#define GI_STRING_HPP
#include "base.hpp"
#ifdef __has_builtin
#define GI_HAS_BUILTIN(x) __has_builtin(x)
#else
#define GI_HAS_BUILTIN(x) 0
#endif
GI_MODULE_EXPORT
namespace gi
{
namespace convert
{
// generic template; specialize as needed where appropriate
template<typename From, typename To, class Enable = void>
struct converter
{
// fail in explanatory way if we end up here
static To convert(const From &)
{
static_assert(!std::is_void<Enable>::value, "unknown type conversion");
return To();
}
};
// implementation should provide some types
template<typename From, typename To, class Enable = void>
struct converter_base : public std::true_type
{
typedef From from_type;
typedef To to_type;
};
// conversion check for complete type
template<typename From, typename To, typename Enable = void>
struct is_convertible_impl : public std::false_type
{};
template<typename From, typename To>
struct is_convertible_impl<From, To,
typename std::enable_if<std::is_base_of<To, From>::value>::type>
: public std::false_type
{};
template<typename From, typename To>
struct is_convertible_impl<From, To,
typename std::enable_if<!std::is_base_of<To, From>::value &&
std::is_pointer<typename converter<From,
To>::from_type *>::value>::type>
: public std::true_type
{};
template<typename From, typename To, bool complete>
struct is_convertible_pre
{
using type = std::false_type;
};
template<typename From, typename To>
struct is_convertible_pre<From, To, true>
{
using type = is_convertible_impl<From, To>;
};
// check whether conversion possible
// reject incomplete forward declared types
template<typename From, typename To, typename Enable = void>
struct is_convertible : public is_convertible_pre<From, To,
gi::traits::is_type_complete<To>::value>::type
{};
} // namespace convert
namespace detail
{
// tag
struct String
{};
template<typename Transfer>
struct StringFuncs
{
static void _deleter(char *&p)
{
if (Transfer().value)
g_free(p);
p = nullptr;
}
static void _copy(const char *p)
{
return Transfer().value ? g_strdup(p) : p;
}
};
template<typename Transfer>
class cstr : public String
{
using self_type = cstr;
protected:
using _member_type =
typename std::conditional<std::is_same<Transfer, transfer_full_t>::value,
char, const char>::type;
_member_type *data_ = nullptr;
void clear()
{
if (Transfer().value && data_)
g_free((char *)data_);
data_ = nullptr;
}
public:
using traits_type = std::char_traits<char>;
using value_type = char;
using pointer = char *;
using const_pointer = const char *;
using reference = char &;
using const_reference = const char &;
using const_iterator = const char *;
using iterator = const_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
using view_type = cstr<transfer_none_t>;
static constexpr bool is_view_type =
!std::is_same<Transfer, transfer_full_t>::value;
static constexpr size_type npos = static_cast<size_type>(-1);
constexpr cstr() noexcept : data_(nullptr) {}
constexpr cstr(std::nullptr_t) noexcept : data_(nullptr) {}
// const usually means none, so only on view type
// also, no arbitrary size is accepted here
template<typename Enable = void,
typename std::enable_if<std::is_same<Enable, void>::value &&
is_view_type>::type * = nullptr>
constexpr cstr(const char *data) : data_(data)
{}
// a single pointer, optionally specify ownership
// behave like string by default, assume no ownership of incoming pointer
template<typename LTransfer = transfer_none_t,
typename std::enable_if<
(std::is_same<LTransfer, transfer_full_t>::value ||
std::is_same<LTransfer, transfer_none_t>::value) &&
!is_view_type>::type * = nullptr>
cstr(char *data, const LTransfer &t)
: data_((t.value == transfer_full.value) || !data ? data : g_strdup(data))
{}
// pointer along with size
// always behave like string and make a copy
template<typename Enable = void,
typename std::enable_if<std::is_same<Enable, void>::value &&
!is_view_type>::type * = nullptr>
cstr(const char *data, size_t len = npos)
: data_(!data ? nullptr
: (len == npos ? g_strdup(data) : g_strndup(data, len)))
{}
// construct from any transfer variant
template<typename OTransfer>
cstr(const cstr<OTransfer> &s) : cstr(s.data())
{}
// accept from string, but NOT string_view as that may not be null-terminated
template<typename Allocator>
cstr(const std::basic_string<char, std::char_traits<char>, Allocator>
&s) noexcept
: cstr(s.data())
{}
#if __cplusplus >= 201703L
// some optional variants of above
constexpr cstr(std::nullopt_t) noexcept : data_(nullptr) {}
template<typename Allocator>
cstr(const std::optional<
std::basic_string<char, std::char_traits<char>, Allocator>> &s) noexcept
: cstr(s ? s.value().data() : nullptr)
{}
#endif
// hook extensible conversion
// (avoid instantiation and confusion with self and base types)
template<typename From,
typename NoBase = typename std::enable_if<
!std::is_base_of<self_type, From>::value>::type,
typename Enable = typename std::enable_if<
convert::is_convertible<From, self_type>::value>::type>
cstr(const From &f) : cstr(convert::converter<From, self_type>::convert(f))
{}
// custom
constexpr const_pointer gobj_() const { return data_; }
explicit constexpr operator bool() const { return data_; }
_member_type *release_()
{
auto tmp = this->data_;
this->data_ = nullptr;
return tmp;
}
// destruct / copy / assign
~cstr() { clear(); }
cstr(const cstr &other) { *this = other; }
cstr(cstr &&other) { *this = std::move(other); }
cstr &operator=(const cstr &other)
{
if (this != &other) {
clear();
data_ = Transfer().value ? g_strdup(other.data_) : other.data_;
}
return *this;
}
cstr &operator=(cstr &&other)
{
if (this != &other) {
clear();
data_ = other.data_;
other.data_ = nullptr;
}
return *this;
}
// deduced conversion to string(_view)
template<typename Destination,
typename Check = typename std::enable_if<
convert::is_convertible<self_type, Destination>::value>::type>
operator Destination() const
{
return convert::converter<self_type, Destination>::convert(*this);
}
#if __cplusplus >= 201703L
// unfortunately, conversion to std::optional picks Destination = std::string
// (which can obviously not be excluded above, or as specialization)
std::optional<std::string> opt_()
{
using To = std::optional<std::string>;
return c_str() ? To({c_str(), size()}) : To(std::nullopt);
}
#endif
// usual string(view) stuff
// iterators
constexpr const_iterator begin() const noexcept { return data_; }
constexpr const_iterator end() const noexcept
{
return data_ ? data_ + size() : nullptr;
}
constexpr const_iterator cbegin() const noexcept { return begin(); }
constexpr const_iterator cend() const noexcept { return end(); }
const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
const_reverse_iterator crend() const noexcept { return rend(); }
// capacity
constexpr size_type size() const noexcept
{
#if GI_HAS_BUILTIN(__builtin_strlen) || \
(defined(__GNUC__) && !defined(__clang__))
return __builtin_strlen(data_);
#else
return data_ ? strlen(data_) : 0;
#endif
}
constexpr size_type length() const noexcept { return size(); }
constexpr size_type max_size() const noexcept
{
return std::numeric_limits<size_type>::max();
}
constexpr bool empty() const noexcept { return !data_ || *data_ == 0; }
// access
constexpr const_reference operator[](size_type i) const { return data_[i]; }
constexpr const_reference at(size_type i) const
{
return i < size() ? data_[i]
: (try_throw(std::out_of_range("cstr::at")), data_[i]);
}
constexpr const_reference front() const { return data_[0]; }
constexpr const_reference back() const { return data_[size() - 1]; }
constexpr const_pointer data() const noexcept { return data_; }
constexpr const_pointer c_str() const noexcept { return data_; }
// modifiers
// view only
template<typename Enable = void,
typename std::enable_if<std::is_same<Enable, void>::value &&
is_view_type>::type * = nullptr>
constexpr void remove_prefix(size_type n)
{
data_ += n;
}
constexpr void swap(self_type &s) noexcept
{
std::swap(this->data_, s.data_);
}
// operations
size_type copy(char *buf, size_type n, size_type pos = 0) const
{
auto s = size();
if (pos > s)
try_throw(std::out_of_range("cstr::copy"));
size_type rlen = (std::min)(s - pos, n);
if (rlen > 0) {
const char *start = data_ + pos;
traits_type::copy(buf, start, rlen);
}
return rlen;
}
int compare(view_type x) const noexcept
{
return g_strcmp0(data_, x.c_str());
}
int compare(const char *s) const { return compare(view_type(s)); }
// find
size_type find(view_type n, size_type pos = 0) const noexcept
{
auto s = size();
auto os = n.size();
if (os > s || pos > s - os)
return npos;
if (!os)
return pos <= s ? pos : npos;
auto loc = strstr(data_ + pos, n.data());
return loc ? loc - data_ : npos;
}
size_type find(char c, size_type pos = 0) const noexcept
{
if (pos >= size())
return npos;
auto loc = strchr(data_ + pos, c);
return loc ? loc - data_ : npos;
}
size_type find(const char *s, size_type pos = 0) const
{
return find(view_type(s), pos);
}
size_type rfind(view_type n, size_type pos = npos) const noexcept
{
auto s = size();
if (!s)
return npos;
auto os = n.size();
if (!os)
return pos == npos ? s : pos;
auto loc = g_strrstr_len(c_str(), pos, n.c_str());
return loc ? loc - data_ : npos;
}
size_type rfind(char c, size_type pos = npos) const noexcept
{
// not quite efficient, but anyways
char str[] = {c, 0};
return rfind(str, pos);
}
size_type rfind(const char *s, size_type pos = npos) const
{
return rfind(view_type(s), pos);
}
// find_first_of variants
// only provide those if std helps us out
#if __cplusplus >= 201703L
size_type find_first_of(view_type s, size_type pos = 0) const noexcept
{
return std::string_view(data_).find_first_of(s.data(), pos);
}
size_type find_first_of(char c, size_type pos = 0) const noexcept
{
return find(c, pos);
}
size_type find_first_of(const char *s, size_type pos = 0) const
{
return find_first_of(view_type(s), pos);
}
size_type find_last_of(view_type s, size_type pos = npos) const noexcept
{
return std::string_view(data_).find_last_of(s.data(), pos);
}
size_type find_last_of(char c, size_type pos = npos) const noexcept
{
return rfind(c, pos);
}
size_type find_last_of(const char *s, size_type pos = npos) const
{
return find_last_of(view_type(s), pos);
}
size_type find_first_not_of(view_type s, size_type pos = 0) const noexcept
{
return std::string_view(data_).find_first_not_of(s.data(), pos);
}
size_type find_first_not_of(char c, size_type pos = 0) const noexcept
{
return std::string_view(data_).find_first_not_of(c, pos);
}
size_type find_first_not_of(const char *s, size_type pos = 0) const
{
return find_first_not_of(view_type(s), pos);
}
size_type find_last_not_of(view_type s, size_type pos = npos) const noexcept
{
return std::string_view(data_).find_last_not_of(s.data(), pos);
}
size_type find_last_not_of(char c, size_type pos = npos) const noexcept
{
return std::string_view(data_).find_last_not_of(c, pos);
}
size_type find_last_not_of(const char *s, size_type pos = npos) const
{
return find_last_not_of(view_type(s), pos);
}
#endif
};
using _string_view = cstr<transfer_none_t>;
inline bool
operator==(_string_view x, _string_view y) noexcept
{
return x.compare(y) == 0;
}
inline bool
operator!=(_string_view x, _string_view y) noexcept
{
return !(x == y);
}
inline bool
operator<(_string_view x, _string_view y) noexcept
{
return x.compare(y) < 0;
}
inline bool
operator>(_string_view x, _string_view y) noexcept
{
return y < x;
}
inline bool
operator<=(_string_view x, _string_view y) noexcept
{
return !(y < x);
}
inline bool
operator>=(_string_view x, _string_view y) noexcept
{
return !(x < y);
}
#ifndef GI_NO_STRING_IOS
inline std::ostream &
operator<<(std::ostream &o, _string_view sv)
{
// backwards compatibility; behave similar to empty string
return o << (sv ? sv.c_str() : "");
}
#endif
// purpose of silly Delay is to avoid gcc premature instantiation of converter
// (in the hook constructor of base class with cstring as From ??)
template<typename Delay = void>
class cstring_d : public cstr<transfer_full_t>
{
using self_type = cstring_d;
using super_type = cstr<transfer_full_t>;
public:
using super_type::super_type;
// if all other construction fails, try to pass through string
template<typename... Args,
typename NoConvert = typename std::enable_if<
!std::is_constructible<super_type, Args...>::value>::type,
typename Enable = typename std::enable_if<
std::is_constructible<std::string, Args...>::value>::type>
cstring_d(Args &&...args)
: super_type(std::string(std::forward<Args>(args)...))
{}
constexpr pointer gobj_() { return data_; }
// re-use string
template<typename... Args>
self_type &assign(Args &&...t)
{
return *this = std::string().assign(std::forward<Args>(t)...);
}
// access
using super_type::at;
constexpr reference at(size_type i)
{
return i < size() ? data_[i]
: (try_throw(std::out_of_range("cstr::at")), data_[i]);
}
using super_type::front;
constexpr reference front() { return data_[0]; }
using super_type::back;
constexpr reference back() { return data_[size() - 1]; }
using super_type::data;
constexpr pointer data() noexcept { return data_; }
// iterators
using super_type::begin;
constexpr iterator begin() noexcept { return data_; }
using super_type::end;
constexpr iterator end() noexcept { return data_ ? data_ + size() : nullptr; }
using super_type::rbegin;
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
using super_type::rend;
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
// operations
using super_type::clear;
void push_back(char c)
{
char str[] = {c, 0};
self_type n{
g_strconcat(c_str() ? c_str() : "", str, (char *)NULL), transfer_full};
swap(n);
}
void pop_back()
{
auto s = size();
if (s)
at(s - 1) = 0;
}
self_type &append(const char *str)
{
self_type n{
g_strconcat(c_str() ? c_str() : "", str, (char *)NULL), transfer_full};
swap(n);
return *this;
}
template<typename Transfer>
self_type &append(const cstr<Transfer> &str)
{
return append(str.data());
}
// otherwise delegate
template<typename... Args>
self_type &append(Args &&...args)
{
std::string s;
s.append(std::forward<Args>(args)...);
// make sure to select the desired variant
return append((const char *)s.c_str());
}
// likewise for +=
self_type &operator+=(char c)
{
push_back(c);
return *this;
}
template<typename T>
self_type &operator+=(const T &o)
{
return append(o);
}
self_type substr(size_type pos = 0, size_type n = npos) const
{
auto l = size();
return (pos > l)
? (try_throw(std::out_of_range("cstr::substr")), self_type())
: self_type(data_ + pos, std::min(n, l - pos));
}
// indeed some are lacking/skipped
// only minimal compatibility
// custom; align with other cases
self_type copy_() { return substr(0); }
};
using cstring = cstring_d<>;
// likewise; (delayed) view type
template<typename Delay = void>
class cstring_v_d : public cstr<transfer_none_t>
{
using self_type = cstring_v_d;
using super_type = cstr<transfer_none_t>;
public:
using super_type::super_type;
// primary reason for subtype
// provide copy/upgrade to owning variant
cstring copy_() { return {g_strdup(this->data()), transfer_full}; }
};
using cstring_v = cstring_v_d<>;
inline cstring
operator+(const _string_view x, const _string_view y) noexcept
{
if (!x)
return {g_strdup(y.c_str()), transfer_full};
if (!y)
return {g_strdup(x.c_str()), transfer_full};
return {g_strconcat(x.c_str(), y.c_str(), (char *)NULL), transfer_full};
}
inline cstring
operator+(const _string_view x, char y) noexcept
{
if (!x)
return {1, y};
char str[] = {y, 0};
return x + str;
}
inline cstring
operator+(char x, const _string_view y) noexcept
{
if (!y)
return {1, x};
char str[] = {x, 0};
return str + y;
}
// add convenient conversions
// local helper traits used below
namespace trait
{
template<typename T, std::size_t SIZE, typename Enable = void>
struct has_data_member : std::false_type
{};
template<typename T, std::size_t SIZE>
struct has_data_member<T, SIZE,
typename std::enable_if<
std::is_pointer<decltype(std::declval<T>().c_str())>::value>::type>
: std::integral_constant<bool, sizeof(*std::declval<T>().c_str()) == SIZE>
{};
template<typename T, typename Enable = void>
struct has_size_member : std::false_type
{};
template<typename T>
struct has_size_member<T, typename std::enable_if<std::is_integral<
decltype(std::declval<T>().size())>::value>::type>
: std::true_type
{};
template<typename T>
struct is_string_type
: public std::integral_constant<bool,
has_data_member<T, 1>::value && has_size_member<T>::value>
{};
} // namespace trait
} // namespace detail
namespace convert
{
template<typename From>
struct converter<From, detail::cstr<transfer_full_t>,
typename std::enable_if<detail::trait::is_string_type<From>::value>::type>
: public converter_base<From, detail::cstr<transfer_full_t>>
{
static detail::cstring convert(const From &v)
{
return {(char *)v.c_str(), v.size()};
}
};
// to a typical string(_view) case
// (avoid conflict with above)
// the traits_type (tries to) restricts this to std::string(_view)
// not doing so might conveniently allow conversion to other types as well.
// however, this would also allow e.g. QByteArray, which comes with an operator+
// in global namespace (also selected by non-ADL lookup),
// which then results in ambiguous overload
// (with the operator+ that is provided above)
template<typename Transfer, typename To>
struct converter<detail::cstr<Transfer>, To,
typename std::enable_if<
!std::is_base_of<detail::String, To>::value &&
!std::is_same<typename To::traits_type, void>::value &&
std::is_constructible<To, const char *, size_t>::value>::type>
: public converter_base<detail::cstr<Transfer>, To>
{
static To convert(const detail::cstr<Transfer> &v)
{
return v.c_str() ? To{(char *)v.c_str(), v.size()} : To();
}
};
#if __cplusplus >= 201703L
// to an std::optional string(_view) case
template<typename Transfer, typename To>
struct converter<detail::cstr<Transfer>, To,
typename std::enable_if<std::is_constructible<To, std::in_place_t,
const char *, size_t>::value>::type>
: public converter_base<detail::cstr<Transfer>, To>
{
static To convert(const detail::cstr<Transfer> &v)
{
abort();
return v.c_str() ? To{std::in_place, (char *)v.c_str(), v.size()} : To();
}
};
#endif
} // namespace convert
using detail::cstring;
using detail::cstring_v;
// sanity check; match C counterpart
static_assert(sizeof(cstring) == sizeof(char *), "");
static_assert(sizeof(cstring_v) == sizeof(char *), "");
namespace traits
{
template<>
struct ctype<const gi::cstring, void>
{
typedef const char *type;
};
template<>
struct ctype<gi::cstring, void>
{
typedef char *type;
};
template<>
struct ctype<const gi::cstring_v, void>
{
typedef const char *type;
};
template<>
struct ctype<gi::cstring_v, void>
{
typedef char *type;
};
template<>
struct cpptype<char *, transfer_full_t>
{
using type = gi::cstring;
};
template<>
struct cpptype<char *, transfer_none_t>
{
using type = gi::cstring_v;
};
} // namespace traits
} // namespace gi
// specialize std::hash for suitable use
GI_MODULE_EXPORT
namespace std
{
template<>
struct hash<gi::cstring>
{
typedef gi::cstring argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const &s) const
{
return s.c_str() ? g_str_hash(s.c_str()) : 0;
}
};
template<>
struct hash<gi::cstring_v>
{
typedef gi::cstring_v argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const &s) const
{
return s.c_str() ? g_str_hash(s.c_str()) : 0;
}
};
} // namespace std
#endif // GI_STRING_HPP

545
cmake/external/glib/cppgir/gi/value.hpp vendored Normal file
View File

@@ -0,0 +1,545 @@
#ifndef GI_VALUE_HPP
#define GI_VALUE_HPP
#include "exception.hpp"
#include "wrap.hpp"
GI_MODULE_EXPORT
namespace gi
{
namespace repository
{
// specialize to declare gtype info
// if not within class get_type()
// gvalue info can also be included this way
// to inject support into Value wrapper
template<typename T>
struct declare_gtype_of : public std::false_type
{};
} // namespace repository
namespace traits
{
namespace detail
{
template<typename T, class Enable = void>
struct gtype : public std::false_type
{
static GType get_type()
{
// dummy test to trigger (almost) always
static_assert(std::is_void<T>::value, "type is not a registered GType");
return 0;
}
};
// gboxed/gobject (or otherwise class) cases
template<typename T>
struct gtype<T, typename if_valid_type<decltype(T::get_type_())>::type>
: public std::true_type
{
typedef typename std::remove_reference<T>::type CppType;
static GType get_type() { return CppType::get_type_(); }
};
// otherwise externally declared
template<typename T>
struct gtype<T, typename if_valid_type<decltype(repository::declare_gtype_of<
T>::get_type())>::type> : public std::true_type
{
static constexpr GType (
*get_type)() = repository::declare_gtype_of<T>::get_type;
};
// gvalue helper info
template<typename T, class Enable = void>
struct gvalue : public std::false_type
{};
// as declared (both of set_value and get_value or neither)
template<typename T>
struct gvalue<T,
typename if_valid_type<decltype(repository::declare_gtype_of<T>::get_value(
nullptr))>::type> : public std::true_type
{
static T get(const GValue *val)
{
return repository::declare_gtype_of<T>::get_value(val);
}
static void set(GValue *val, T t)
{
repository::declare_gtype_of<T>::set_value(val, t);
}
};
template<typename T>
using is_enum_or_bitfield =
typename std::conditional<std::is_enum<T>::value && gtype<T>::value,
std::true_type, std::false_type>::type;
// handle enum/flags cases, rather than many declares
template<typename T>
struct gvalue<T, typename std::enable_if<is_enum_or_bitfield<T>::value>::type>
: public std::true_type
{
static T get(const GValue *val)
{
GType t = gtype<T>::get_type();
if (G_TYPE_IS_FLAGS(t))
return static_cast<T>(g_value_get_flags(val));
// assume enum, let glib complain otherwise
return static_cast<T>(g_value_get_enum(val));
}
static void set(GValue *val, T v)
{
GType t = gtype<T>::get_type();
if (G_TYPE_IS_FLAGS(t)) {
g_value_set_flags(val, (guint)v);
} else {
// assume enum, let glib complain otherwise
g_value_set_enum(val, (gint)v);
}
}
};
} // namespace detail
template<typename T>
using gtype = detail::gtype<
typename std::decay<typename std::remove_reference<T>::type>::type>;
template<typename T>
using gvalue = detail::gvalue<
typename std::decay<typename std::remove_reference<T>::type>::type>;
template<typename T>
using is_enum_or_bitfield = detail::is_enum_or_bitfield<
typename std::decay<typename std::remove_reference<T>::type>::type>;
#if 0
template<typename T, typename Enable = void>
struct is_flag : public std::false_type {};
// TODO extend fundamental type stuff ??
template<typename T>
struct is_flag<T,
typename std::enable_if<std::is_enum<T>::value &&
repository::declare_gtype_of<T>::get_fundamental_type() == G_TYPE_FLAGS>::type> :
public std::true_type {};
#endif
} // namespace traits
// C++ types are used below (e.g. int) instead of e.g. gint
// since gint64 might map to same as glong (or not)
// so some of the int types are "best approximation" from C++ type to GType
// instead; use the following types to guide to the right overload
typedef char vchar;
typedef long vlong;
typedef int vint;
typedef long long vint64;
typedef bool vboolean;
typedef unsigned char vuchar;
typedef unsigned long vulong;
typedef unsigned int vuint;
typedef unsigned long long vuint64;
typedef float vfloat;
typedef double vdouble;
// NOTE: (plain) char might be signed or unsigned depending on platform
// but gchar == char always anyway
static_assert(std::is_same<gchar, char>::value, "now what");
namespace repository
{
template<>
struct declare_gtype_of<void>
{
static GType get_type() { return G_TYPE_NONE; }
};
#define GI_DECLARE_GTYPE(cpptype, g_type) \
template<> \
struct declare_gtype_of<cpptype> \
{ \
static constexpr GType get_type() { return g_type; } \
};
#define GI_DECLARE_GTYPE_VALUE(cpptype, g_type, value_suffix) \
template<> \
struct declare_gtype_of<cpptype> \
{ \
static constexpr GType get_type() { return g_type; } \
static void set_value(GValue *val, cpptype v) \
{ \
g_value_set_##value_suffix(val, v); \
} \
static cpptype get_value(const GValue *val) \
{ \
return g_value_get_##value_suffix(val); \
} \
};
// declare non-cv qualified type
GI_DECLARE_GTYPE_VALUE(gpointer, G_TYPE_POINTER, pointer)
GI_DECLARE_GTYPE_VALUE(bool, G_TYPE_BOOLEAN, boolean)
GI_DECLARE_GTYPE_VALUE(char, G_TYPE_CHAR, schar)
GI_DECLARE_GTYPE_VALUE(unsigned char, G_TYPE_UCHAR, uchar)
GI_DECLARE_GTYPE_VALUE(int, G_TYPE_INT, int)
GI_DECLARE_GTYPE_VALUE(unsigned int, G_TYPE_UINT, uint)
GI_DECLARE_GTYPE_VALUE(long, G_TYPE_LONG, long)
GI_DECLARE_GTYPE_VALUE(unsigned long, G_TYPE_ULONG, ulong)
GI_DECLARE_GTYPE_VALUE(long long, G_TYPE_INT64, int64)
GI_DECLARE_GTYPE_VALUE(unsigned long long, G_TYPE_UINT64, uint64)
GI_DECLARE_GTYPE_VALUE(float, G_TYPE_FLOAT, float)
GI_DECLARE_GTYPE_VALUE(double, G_TYPE_DOUBLE, double)
// some custom set/get for these
// remember; the pointer is non-const
GI_DECLARE_GTYPE(const char *, G_TYPE_STRING)
GI_DECLARE_GTYPE(char *, G_TYPE_STRING)
GI_DECLARE_GTYPE(std::string, G_TYPE_STRING)
GI_DECLARE_GTYPE(gi::cstring, G_TYPE_STRING)
GI_DECLARE_GTYPE(gi::cstring_v, G_TYPE_STRING)
#undef GI_DECLARE_GTYPE_VALUE
#undef GI_DECLARE_GTYPE
} // namespace repository
namespace detail
{
// GValue helpers
// set_value
template<typename T,
typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
inline void
set_value(GValue *val, T v)
{
traits::gvalue<T>::set(val, v);
}
inline void
set_value(GValue *val, const std::string &s)
{
g_value_set_string(val, s.c_str());
}
inline void
set_value(GValue *val, gi::cstring_v s)
{
g_value_set_string(val, s.c_str());
}
inline void
set_value(GValue *val, const char *s)
{
g_value_set_string(val, s);
}
template<typename T,
typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
inline void
set_value(GValue *val, T v)
{
// set might not handle NULL case
g_value_take_object(val, v.gobj_copy_());
}
template<typename T,
typename std::enable_if<traits::is_gboxed<T>::value>::type * = nullptr>
inline void
set_value(GValue *val, T v)
{
// set might not handle NULL case
g_value_take_boxed(val, v.gobj_copy_());
}
// container case
template<typename T, typename T::_detail::DataType * = nullptr>
inline void
set_value(GValue *val, T v)
{
v._set_value(val);
}
// get_value
template<typename T,
typename std::enable_if<traits::is_object<T>::value>::type * = nullptr>
inline T
get_value(const GValue *val)
{
// ensure sanity
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
"non cv-qualified class type required");
auto cv =
static_cast<typename traits::ctype<T>::type>(g_value_dup_object(val));
if (cv && !g_type_is_a(G_OBJECT_TYPE(cv), traits::gtype<T>::get_type()))
detail::try_throw(transform_error(G_OBJECT_TYPE(cv)));
return gi::wrap(cv, transfer_full);
}
template<typename T,
typename std::enable_if<traits::is_gboxed<T>::value &&
!traits::is_reftype<T>::value>::type * = nullptr>
inline T
get_value(const GValue *val)
{
// no way to know whether boxed type is correct
// ensure sanity
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
"non cv-qualified class type required");
auto cv =
static_cast<typename traits::ctype<T>::type>(g_value_dup_boxed(val));
return gi::wrap(cv, transfer_full);
}
template<typename T,
typename std::enable_if<traits::is_gboxed<T>::value &&
traits::is_reftype<T>::value>::type * = nullptr>
inline T
get_value(const GValue *val)
{
// no way to know whether boxed type is correct
// ensure sanity
static_assert(std::is_class<T>::value && !std::is_const<T>::value,
"non cv-qualified class type required");
auto cv =
static_cast<typename traits::ctype<T>::type>(g_value_get_boxed(val));
return gi::wrap(cv, transfer_none);
}
template<typename T,
typename std::enable_if<traits::gvalue<T>::value>::type * = nullptr>
inline T
get_value(const GValue *val)
{
return traits::gvalue<T>::get(val);
}
// sigh ...
template<typename T,
typename std::enable_if<std::is_same<T, std::string>::value ||
std::is_base_of<detail::String, T>::value>::type * =
nullptr>
inline T
get_value(const GValue *val)
{
return gi::wrap(g_value_get_string(val), transfer_none);
}
// container case
template<typename T, typename T::_detail::DataType * = nullptr>
inline T
get_value(const GValue *val)
{
static_assert(traits::is_decayed<T>::value, "");
return T::template _get_value<T>(val);
}
// convenience helper ...
template<typename T,
typename std::enable_if<std::is_same<T, void>::value>::type * = nullptr>
inline T
get_value(const GValue * /*val*/)
{}
// simple (RAII) Value wrapper for (internal) use
struct Value : public GValue, noncopyable
{
void clear() { memset((void *)this, 0, sizeof(*this)); }
Value() { clear(); }
template<typename T, typename Enable = disable_if_same_or_derived<T, Value>>
explicit Value(T &&v)
{
clear();
g_value_init(this, traits::gtype<T>::get_type());
set_value(this, std::forward<T>(v));
}
template<typename T>
void init()
{
// handle no-op void (return value) corner case
const GType tp = traits::gtype<T>::get_type();
if (tp != G_TYPE_NONE)
g_value_init(this, tp);
}
// let's not copy, but ok to move around
Value(Value &&other)
{
memcpy((void *)this, &other, sizeof(*this));
other.clear();
}
Value &operator=(Value &&other)
{
if (this != &other) {
memcpy((void *)this, &other, sizeof(*this));
other.clear();
}
return *this;
}
~Value()
{
if (G_VALUE_TYPE(this))
g_value_unset(this);
}
};
// we really rely upon this as part of the ABI
// justifies operations above and some explicit casts above
// (to avoid -Wclass-memaccess)
static_assert(sizeof(Value) == sizeof(GValue), "unsupported compiler");
template<typename R>
inline R
transform_value(const GValue *val)
{
detail::Value dest;
dest.init<R>();
if (!g_value_transform(val, &dest))
detail::try_throw(detail::transform_error(G_VALUE_TYPE(&dest)));
return detail::get_value<R>(&dest);
}
// hand-crafted Value wrapper with an interface as it would be generated
// and used by generated code, along with additional convenience
class ValueBase : public gi::detail::GBoxedWrapperBase<ValueBase, GValue>
{
using self_type = ValueBase;
public:
static GType get_type_() G_GNUC_CONST { return G_TYPE_VALUE; }
void copy(self_type dest) const { g_value_copy(gobj_(), dest.gobj_()); }
void reset() { g_value_reset(gobj_()); }
void unset() { g_value_unset(gobj_()); }
bool transform(self_type dest) const
{
return g_value_transform(gobj_(), dest.gobj_());
}
static bool type_compatible(GType src_type, GType dest_type)
{
return g_value_type_compatible(src_type, dest_type);
}
static bool type_transformable(GType src_type, GType dest_type)
{
return g_value_type_transformable(src_type, dest_type);
}
template<typename T>
self_type &set_value(T &&v)
{
detail::set_value(gobj_(), std::forward<T>(v));
return *this;
}
template<typename T>
T get_value() const
{
return detail::get_value<T>(gobj_());
}
template<typename R>
R transform_value()
{
return detail::transform_value<R>(gobj_());
}
};
} // namespace detail
namespace repository
{
namespace GObject
{
// build on above base with additional convenience (in owning case)
class Value_Ref;
class Value : public gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase,
Value_Ref>
{
typedef gi::detail::GBoxedWrapper<Value, GValue, detail::ValueBase, Value_Ref>
super_type;
typedef Value self_type;
public:
using detail::ValueBase::copy;
using super_type::copy;
// hybrid GBoxed/CBoxed
void allocate_()
{
if (this->data_)
return;
// make sure we match GValue boxed allocation with boxed free
// (though last kown implementation uses g_new0/g_free)
detail::Value tmp;
this->data_ = (::GValue *)g_boxed_copy(G_TYPE_VALUE, &tmp);
}
// convenience
Value() { allocate_(); }
// allow non-explicit use for convenient calling
// but avoid copy/move construct use
template<typename T,
typename std::enable_if<!std::is_base_of<Value,
typename std::remove_reference<T>::type>::value>::type * = nullptr>
Value(T &&t)
{
allocate_();
init<T>(std::forward<T>(t));
}
Value &init(GType tp)
{
// no-op void corner case
if (tp != G_TYPE_NONE)
g_value_init(gobj_(), tp);
return *this;
}
template<typename T>
Value &init(T &&v)
{
g_value_init(gobj_(), traits::gtype<T>::get_type());
set_value(std::forward<T>(v));
return *this;
}
};
class Value_Ref
: public gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
{
typedef gi::detail::GBoxedRefWrapper<Value, ::GValue, detail::ValueBase>
super_type;
using super_type::super_type;
};
} // namespace GObject
template<>
struct declare_cpptype_of<GValue>
{
typedef GObject::Value type;
};
} // namespace repository
} // namespace gi
#endif // GI_VALUE_HPP

369
cmake/external/glib/cppgir/gi/wrap.hpp vendored Normal file
View File

@@ -0,0 +1,369 @@
#ifndef GI_WRAP_HPP
#define GI_WRAP_HPP
#include "base.hpp"
#include "string.hpp"
GI_MODULE_EXPORT
namespace gi
{
// object/wrapper conversion
template<typename CType, typename TransferType,
typename CppType = typename traits::cpptype<CType *>::type,
typename Enable =
typename std::enable_if<traits::is_wrapper<CppType>::value>::type>
inline typename std::remove_const<CppType>::type
wrap(CType *v, const TransferType &t)
{
// should be called with a concrete transfer subtype
static_assert(!std::is_same<TransferType, transfer_t>::value, "");
// the class wrap only has to deal with non-const class type
typedef typename std::remove_const<CppType>::type TNC;
return CppType::template wrap<TNC>(v, t.value);
}
// special case; wrap an owned box with full transfer
template<typename CType,
typename CppType = typename traits::cpptype<CType *>::type,
typename Enable =
typename std::enable_if<traits::is_boxed<CppType>::value>::type,
typename TNC = typename std::remove_const<CppType>::type>
inline TNC
wrap(CType *v, const transfer_full_t &)
{
return CppType::template wrap<TNC>(v);
}
// special case; wrap a unowned box (that is, transfer none) to the Ref type
template<typename CType,
typename CppType = typename traits::cpptype<CType *>::type,
typename Enable =
typename std::enable_if<traits::is_boxed<CppType>::value>::type,
typename RefType = typename traits::reftype<
typename std::remove_const<CppType>::type>::type>
inline RefType
wrap(CType *v, const transfer_none_t &)
{
// unowned and no copy in all cases
return RefType::template wrap<RefType>(v);
}
template<typename T,
typename std::remove_reference<T>::type::BaseObjectType * = nullptr>
inline typename traits::ctype<T>::type
unwrap(T &&v, const transfer_none_t &)
{
using DT = typename std::decay<T>::type;
// test convenience
#ifndef GI_TEST
static constexpr bool ALLOW_ALL = false;
#else
static constexpr bool ALLOW_ALL = true;
#endif
static_assert(ALLOW_ALL || traits::is_wrapper<DT>::value ||
traits::is_reftype<DT>::value,
"transfer none expects refcnt wrapper or reftype (not owning box)");
return v.gobj_();
}
namespace detail
{
// lvalue
template<typename T,
typename std::enable_if<!traits::is_boxed<T>::value>::type * = nullptr>
inline typename traits::ctype<T>::type
unwrap(const T &v, const transfer_full_t &, std::true_type)
{
// no implicit copy for boxed; should end up in other case
static_assert(!traits::is_boxed<T>::value, "boxed copy");
return v.gobj_copy_();
}
// rvalue
template<typename T,
typename std::enable_if<!traits::is_reftype<T>::value>::type * = nullptr>
inline typename traits::ctype<T>::type
unwrap(T &&v, const transfer_full_t &, std::false_type)
{
// in case of wrapper/object;
// release only provided on base case with void* return
return (typename traits::ctype<T>::type)v.release_();
}
} // namespace detail
template<typename T, typename std::decay<T>::type::BaseObjectType * = nullptr>
inline typename traits::ctype<T>::type
unwrap(T &&v, const transfer_full_t &t)
{
// universal reference dispatch
return detail::unwrap(std::forward<T>(v), t, std::is_lvalue_reference<T>());
}
// container types
template<typename T, typename Transfer,
typename std::decay<T>::type::_detail::DataType * = nullptr>
inline typename std::decay<T>::type::_detail::DataType
unwrap(T &&v, const Transfer &t)
{
// universal reference dispatch
return std::forward<T>(v)._unwrap(t);
}
// to wrap a container, the target wrapped type needs to be explicitly specified
// (in particular the contained element type)
// (target type should be decay'ed type)
// generic case, let wrap take care of it (usually no target type is needed)
template<typename TargetType, typename CType, typename Transfer,
decltype(wrap(std::declval<typename std::decay<CType>::type>(),
Transfer())) * = nullptr>
TargetType
wrap_to(CType v, const Transfer &t)
{
static_assert(traits::is_decayed<TargetType>::value, "");
return wrap(v, t);
}
// container case
template<typename TargetType, typename CType, typename Transfer,
typename TargetType::_detail::DataType * = nullptr>
TargetType
wrap_to(CType v, const Transfer &t)
{
static_assert(traits::is_decayed<TargetType>::value, "");
return TargetType::template _wrap<TargetType>(v, t);
}
// container size case
template<typename TargetType, typename CType, typename Transfer,
typename TargetType::_detail::DataType * = nullptr>
TargetType
wrap_to(CType v, int s, const Transfer &t)
{
static_assert(traits::is_decayed<TargetType>::value, "");
return TargetType::template _wrap<TargetType>(v, s, t);
}
#if 0
// string conversion
inline std::string
wrap(const char *v, const transfer_none_t &,
const direction_t & = direction_dummy)
{
return detail::make_string(v);
}
// actually should not accept const input (as it makes no sense for full
// transfer) but let's go the runtime way and not mind that too much (code
// generation will warn though)
inline std::string
wrap(const char *v, const transfer_full_t &,
const direction_t & = direction_dummy)
{
// a custom type that would allow direct mem transfer might be nice
// but that might be too nifty and create yet-another-string-type
std::string s;
if (v) {
s = v;
g_free((char *)v);
}
return s;
}
#else
// string conversion
inline gi::cstring_v
wrap(const char *v, const transfer_none_t &)
{
return cstring_v(v);
}
// actually should not accept const input (as it makes no sense for full
// transfer) but let's go the runtime way and not mind that too much (code
// generation will warn though)
inline gi::cstring
wrap(const char *v, const transfer_full_t &)
{
// as said, never mind const
return cstring{(char *)v, transfer_full};
}
#endif
// return const here, as somewhat customary, also
// wrapped function call is force-casted anyway (to const char* parameter)
// FIXME ?? though const is generally rare and it breaks consistency that way
inline const gchar *
unwrap(const std::string &v, const transfer_none_t &)
{
return v.c_str();
}
inline const gchar *
unwrap(const detail::optional_string &v, const transfer_none_t &)
{
return v.empty() ? nullptr : v.c_str();
}
template<typename Transfer>
inline const gchar *
unwrap(const detail::cstr<Transfer> &v, const transfer_none_t &)
{
return v.c_str();
}
// R-value variants of the above, akin to transfer_none from an owning R-value
// bad dangling things would happen
inline const gchar *unwrap(std::string &&v, const transfer_none_t &) = delete;
inline const gchar *unwrap(
detail::optional_string &&v, const transfer_none_t &) = delete;
template<typename Transfer>
inline const gchar *
unwrap(detail::cstr<Transfer> &&v, const transfer_none_t &)
{
static_assert(std::is_same<Transfer, transfer_none_t>::value,
"transfer none expects non-owning type");
return v.c_str();
}
inline gchar *
unwrap(const std::string &v, const transfer_full_t &)
{
return g_strdup(v.c_str());
}
inline gchar *
unwrap(const detail::optional_string &v, const transfer_full_t &)
{
return v.empty() ? nullptr : g_strdup(v.c_str());
}
template<typename Transfer>
inline gchar *
unwrap(const gi::detail::cstr<Transfer> &v, const transfer_full_t &)
{
return g_strdup(v.c_str());
}
inline gchar *
unwrap(gi::cstring &&v, const transfer_full_t &)
{
return v.release_();
}
// enum conversion
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
inline typename traits::cpptype<T>::type
wrap(T v, const transfer_t & = transfer_dummy)
{
return (typename traits::cpptype<T>::type)v;
}
template<typename T,
typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
inline typename traits::ctype<T>::type
unwrap(T v, const transfer_t & = transfer_dummy)
{
return (typename traits::ctype<T>::type)v;
}
// plain basic pass along
template<typename T,
typename std::enable_if<traits::is_basic<T>::value>::type * = nullptr>
inline T
wrap(T v, const transfer_t & = transfer_dummy)
{
return v;
}
template<typename T,
typename std::enable_if<traits::is_basic<T>::value>::type * = nullptr>
inline T
unwrap(T v, const transfer_t & = transfer_dummy)
{
return v;
}
// callback conversion
// async or destroy-notify;
// signature forces copy, and std::move is used in unwrap call
template<typename T,
typename std::remove_reference<T>::type::CallbackWrapperType * = nullptr>
inline typename std::remove_reference<T>::type::CallbackWrapperType
unwrap(T &&v, const transfer_t & = transfer_dummy)
{
return typename std::remove_reference<T>::type::CallbackWrapperType(
std::forward<T>(v));
}
// call or destroy-notify scope
template<typename T>
inline typename std::remove_reference<T>::type::template wrapper_type<false> *
unwrap(T &&v, const scope_t &)
{
return new
typename std::remove_reference<T>::type::template wrapper_type<false>(
std::forward<T>(v));
}
// async scope
template<typename T>
inline typename std::remove_reference<T>::type::template wrapper_type<true> *
unwrap(T &&v, const scope_async_t &)
{
return new
typename std::remove_reference<T>::type::template wrapper_type<true>(
std::forward<T>(v));
}
// dynamic GType casting within GObject/interface hierarchy
template<typename T, typename I,
typename std::enable_if<
traits::is_object<T>::value &&
traits::is_object<typename std::decay<I>::type>::value>::type * =
nullptr>
inline T
object_cast(I &&t)
{
if (!t || !g_type_is_a(t.gobj_type_(), T::get_type_())) {
return T();
} else {
return wrap((typename T::BaseObjectType *)t.gobj_copy_(), transfer_full);
}
}
// this utility can be used to arrange for pointer-like const-ness
// that is, a const shared_ptr<T> is still usable like (non-const) T*
// in a way, any wrapper object T acts much like a smart-pointer,
// but as the (code generated) methods are non-const, they are not usable
// if the wrapper object is const (e.g. captured in a lambda)
// this helper object/class can be wrapped around the wrapper (phew)
// to absorb/shield the (outer) `const` (as it behaves as other smart pointers)
template<typename T>
class cs_ptr
{
T t;
public:
// rough check; T is expected to be a pointer wrapper
static_assert(sizeof(T) == sizeof(void *), "");
// if T not copy-able, argument may need to be move'd
cs_ptr(T _t) : t(std::move(_t)) {}
operator T() const & { return t; }
operator T() && { return std::move(t); }
T *get() const { return &t; }
// C++ sacrilege,
// but the const of pointer in T does not extend to pointee anyway
T *operator*() const { return const_cast<T *>(&t); }
T *operator->() const { return const_cast<T *>(&t); }
};
} // namespace gi
#endif // GI_WRAP_HPP