#ifndef GI_VALUE_HPP #define GI_VALUE_HPP #include "exception.hpp" #include "wrap.hpp" #include #include #include 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 struct declare_gtype_of : public std::false_type {}; } // namespace repository namespace traits { namespace detail { template struct gtype : public std::false_type { static GType get_type() { // dummy test to trigger (almost) always static_assert(std::is_void::value, "type is not a registered GType"); return 0; } }; // gboxed/gobject (or otherwise class) cases template struct gtype::type> : public std::true_type { typedef typename std::remove_reference::type CppType; static GType get_type() { return CppType::get_type_(); } }; // otherwise externally declared template struct gtype::get_type())>::type> : public std::true_type { static constexpr GType ( *get_type)() = repository::declare_gtype_of::get_type; }; // gvalue helper info template struct gvalue : public std::false_type {}; // as declared (both of set_value and get_value or neither) template struct gvalue::get_value( nullptr))>::type> : public std::true_type { static T get(const GValue *val) { return repository::declare_gtype_of::get_value(val); } static void set(GValue *val, T t) { repository::declare_gtype_of::set_value(val, t); } }; template using is_enum_or_bitfield = typename std::conditional::value && gtype::value, std::true_type, std::false_type>::type; // handle enum/flags cases, rather than many declares template struct gvalue::value>::type> : public std::true_type { static T get(const GValue *val) { GType t = gtype::get_type(); if (G_TYPE_IS_FLAGS(t)) return static_cast(g_value_get_flags(val)); // assume enum, let glib complain otherwise return static_cast(g_value_get_enum(val)); } static void set(GValue *val, T v) { GType t = gtype::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 using gtype = detail::gtype< typename std::decay::type>::type>; template using gvalue = detail::gvalue< typename std::decay::type>::type>; template using is_enum_or_bitfield = detail::is_enum_or_bitfield< typename std::decay::type>::type>; #if 0 template struct is_flag : public std::false_type {}; // TODO extend fundamental type stuff ?? template struct is_flag::value && repository::declare_gtype_of::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::value, "now what"); namespace repository { template<> struct declare_gtype_of { static GType get_type() { return G_TYPE_NONE; } }; #define GI_DECLARE_GTYPE(cpptype, g_type) \ template<> \ struct declare_gtype_of \ { \ static constexpr GType get_type() { return g_type; } \ }; #define GI_DECLARE_GTYPE_VALUE(cpptype, g_type, value_suffix) \ template<> \ struct declare_gtype_of \ { \ 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::value>::type * = nullptr> inline void set_value(GValue *val, T v) { traits::gvalue::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::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::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 inline void set_value(GValue *val, T v) { v._set_value(val); } // get_value template::value>::type * = nullptr> inline T get_value(const GValue *val) { // ensure sanity static_assert(std::is_class::value && !std::is_const::value, "non cv-qualified class type required"); auto cv = static_cast::type>(g_value_dup_object(val)); if (cv && !g_type_is_a(G_OBJECT_TYPE(cv), traits::gtype::get_type())) detail::try_throw(transform_error(G_OBJECT_TYPE(cv))); return gi::wrap(cv, transfer_full); } template::value && !traits::is_reftype::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::value && !std::is_const::value, "non cv-qualified class type required"); auto cv = static_cast::type>(g_value_dup_boxed(val)); return gi::wrap(cv, transfer_full); } template::value && traits::is_reftype::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::value && !std::is_const::value, "non cv-qualified class type required"); auto cv = static_cast::type>(g_value_get_boxed(val)); return gi::wrap(cv, transfer_none); } template::value>::type * = nullptr> inline T get_value(const GValue *val) { return traits::gvalue::get(val); } // sigh ... template::value || std::is_base_of::value>::type * = nullptr> inline T get_value(const GValue *val) { return gi::wrap(g_value_get_string(val), transfer_none); } // container case template inline T get_value(const GValue *val) { static_assert(traits::is_decayed::value, ""); return T::template _get_value(val); } // convenience helper ... template::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> explicit Value(T &&v) { clear(); g_value_init(this, traits::gtype::get_type()); set_value(this, std::forward(v)); } template void init() { // handle no-op void (return value) corner case const GType tp = traits::gtype::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 inline R transform_value(const GValue *val) { detail::Value dest; dest.init(); if (!g_value_transform(val, &dest)) detail::try_throw(detail::transform_error(G_VALUE_TYPE(&dest))); return detail::get_value(&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 { 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 self_type &set_value(T &&v) { detail::set_value(gobj_(), std::forward(v)); return *this; } template T get_value() const { return detail::get_value(gobj_()); } template R transform_value() { return detail::transform_value(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 { typedef gi::detail::GBoxedWrapper 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::type>::value>::type * = nullptr> Value(T &&t) { allocate_(); init(std::forward(t)); } Value &init(GType tp) { // no-op void corner case if (tp != G_TYPE_NONE) g_value_init(gobj_(), tp); return *this; } template Value &init(T &&v) { g_value_init(gobj_(), traits::gtype::get_type()); set_value(std::forward(v)); return *this; } }; class Value_Ref : public gi::detail::GBoxedRefWrapper { typedef gi::detail::GBoxedRefWrapper super_type; using super_type::super_type; }; } // namespace GObject template<> struct declare_cpptype_of { typedef GObject::Value type; }; } // namespace repository } // namespace gi #endif // GI_VALUE_HPP