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

546 lines
14 KiB
C++

#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