| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- #ifndef GI_VALUE_HPP
- #define GI_VALUE_HPP
- #include "exception.hpp"
- #include "wrap.hpp"
- #include <gobject/gvaluecollector.h>
- #include <string.h>
- #include <type_traits>
- 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
|