| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964 |
- #ifndef GI_CONTAINER_HPP
- #define GI_CONTAINER_HPP
- #include "base.hpp"
- #include "wrap.hpp"
- #include <iterator>
- #include <map>
- #include <vector>
- #include <assert.h>
- #include <string.h>
- namespace gi
- {
- namespace detail
- {
- // helpers
- // element unwrap cast helper;
- // element reference type depends on container reference type
- // lvalue
- template<typename T>
- T
- cast_ref(T &&el, std::true_type)
- {
- return el;
- }
- // rvalue
- template<typename T>
- typename std::remove_reference<T>::type &&
- cast_ref(T &&el, std::false_type)
- {
- return std::move(el);
- }
- // std::iterator may be deprecated, but it is still handy
- // so replicate it here to avoid deprecation warnings
- template<typename Category, typename T, typename Distance = std::ptrdiff_t,
- typename Pointer = T *, typename Reference = T &>
- struct iterator
- {
- using iterator_category = Category;
- using value_type = T;
- using difference_type = Distance;
- using pointer = Pointer;
- using reference = Reference;
- };
- namespace _traits
- {
- template<typename T>
- struct hash
- {};
- template<typename T>
- struct equal
- {};
- template<typename CType>
- void
- _destroy(CType v)
- {
- // destroy by wrapping a temporary with full transfer
- // and then simply dropping that temporary
- wrap(v, transfer_full);
- }
- // C type case
- template<typename CType, typename Enable = void>
- struct destroy
- {
- static constexpr auto func = _destroy<CType>;
- };
- // Cpp type case
- template<typename CppType>
- struct destroy<CppType, typename traits::if_valid_type<typename traits::ctype<
- typename std::decay<CppType>::type>::type>::type>
- {
- using CType =
- typename traits::ctype<typename std::decay<CppType>::type>::type;
- static constexpr auto func = _destroy<CType>;
- };
- // let's simply stick to the following for now
- // since others are not so likely and not always clear semantics
- template<>
- struct hash<std::string>
- {
- static constexpr GHashFunc func = g_str_hash;
- };
- template<>
- struct hash<gi::cstring>
- {
- static constexpr GHashFunc func = g_str_hash;
- };
- template<>
- struct hash<gi::cstring_v>
- {
- static constexpr GHashFunc func = g_str_hash;
- };
- template<>
- struct equal<std::string>
- {
- static constexpr GEqualFunc func = g_str_equal;
- };
- template<>
- struct equal<gi::cstring>
- {
- static constexpr GEqualFunc func = g_str_equal;
- };
- template<>
- struct equal<gi::cstring_v>
- {
- static constexpr GEqualFunc func = g_str_equal;
- };
- } // namespace _traits
- /////////////////
- //
- ////////////////
- // helper check/trait
- namespace trait
- {
- std::false_type is_initializer_list_dispatch(...);
- template<typename T>
- std::true_type is_initializer_list_dispatch(std::initializer_list<T> *);
- template<typename T>
- struct is_initializer_list
- : decltype(is_initializer_list_dispatch(static_cast<T *>(nullptr)))
- {};
- } // namespace trait
- // helper; combine some traits
- template<typename T, typename Transfer>
- using elcpptype = traits::cpptype<T, typename element_transfer<Transfer>::type>;
- // hard convert/cast of a pointer to a C type to its wrapper type
- // (which is expected of same size)
- template<typename Transfer, typename CType,
- typename CppType = typename elcpptype<CType, Transfer>::type>
- CppType *
- wrap_cast(CType *c)
- {
- static_assert(sizeof(*c) == sizeof(std::declval<CppType>()), "");
- return reinterpret_cast<CppType *>(c);
- }
- // snippet to disable a method for transfer none case
- #define GI_DISABLE_METHOD_NONE \
- template<typename Enable = void, \
- typename Check = typename std::enable_if< \
- std::is_void<Enable>::value && \
- !std::is_same<Transfer, transfer_none_t>::value>::type>
- template<typename ListType, typename ElType, typename Transfer,
- typename Enable = void>
- class CollectionBase
- {};
- template<typename ListType, typename ElCType, typename Transfer>
- struct list_ops;
- struct GPtrArrayFuncs
- {
- constexpr static bool refcnt = true;
- using value_type = gpointer;
- static GType get_type_() { return G_TYPE_PTR_ARRAY; }
- static ::GPtrArray *ref(::GPtrArray *data)
- {
- return data ? g_ptr_array_ref(data) : data;
- }
- static ::GPtrArray *sink(::GPtrArray *data)
- {
- return data ? g_ptr_array_ref(data) : data;
- }
- static void free(::GPtrArray *&data)
- {
- if (data) {
- g_ptr_array_unref(data);
- data = nullptr;
- }
- }
- static ::GPtrArray *float_(::GPtrArray *data) { return data; }
- };
- template<typename ElCType, typename Transfer>
- struct list_ops<::GPtrArray, ElCType, Transfer> : public GPtrArrayFuncs
- {};
- template<typename ElCType, typename Transfer>
- class CollectionBase<::GPtrArray, ElCType, Transfer>
- : public Wrapper<GPtrArrayFuncs, ::GPtrArray>
- {
- protected:
- using list_ops = GPtrArrayFuncs;
- using state_type = int; // dummy
- state_type _create()
- {
- if (!this->data_) {
- auto &l = this->data_;
- l = g_ptr_array_new();
- if (std::is_same<Transfer, transfer_full_t>::value)
- g_ptr_array_set_free_func(
- l, (GDestroyNotify)_traits::destroy<ElCType>::func);
- }
- return 0;
- }
- void _finish() {}
- state_type _push(state_type s, list_ops::value_type item)
- {
- g_ptr_array_add(this->data_, item);
- return s;
- }
- struct iterator_type
- {
- CollectionBase *self;
- int index;
- bool next(list_ops::value_type &val)
- {
- if (self->data_ && (index < (int)self->data_->len)) {
- val = g_ptr_array_index(self->data_, index++);
- return true;
- }
- return false;
- }
- };
- iterator_type _iterator() { return iterator_type{this, 0}; }
- void _steal()
- {
- g_free(g_ptr_array_steal(this->data_, nullptr));
- g_ptr_array_unref(this->data_);
- this->data_ = nullptr;
- }
- public:
- using value_type = typename elcpptype<ElCType, Transfer>::type;
- protected:
- value_type *_cast() const
- {
- return wrap_cast<Transfer>((ElCType *)(this->data_->pdata));
- }
- GI_DISABLE_METHOD_NONE
- void _ensure_array() { _create(); }
- public:
- // assume same layout of wrapper and wrappee
- static_assert(sizeof(value_type) == sizeof(gpointer), "");
- static_assert(sizeof(value_type[2]) == sizeof(gpointer[2]), "");
- using iterator = value_type *;
- using const_iterator = const value_type *;
- const_iterator cbegin() const { return this->data_ ? _cast() : nullptr; }
- const_iterator cend() const
- {
- return this->data_ ? begin() + this->data_->len : nullptr;
- }
- const_iterator begin() const { return cbegin(); }
- const_iterator end() const { return cend(); }
- GI_DISABLE_METHOD_NONE
- iterator begin() { return this->data_ ? _cast() : nullptr; }
- GI_DISABLE_METHOD_NONE iterator end()
- {
- return this->data_ ? begin() + this->data_->len : nullptr;
- }
- size_t size() const noexcept { return this->data_ ? this->data_->len : 0; }
- bool empty() const noexcept { return size() == 0; }
- GI_DISABLE_METHOD_NONE
- void push_front(const value_type &v) { insert(begin(), v); }
- GI_DISABLE_METHOD_NONE
- void push_front(value_type &&v) { insert(begin(), std::move(v)); }
- GI_DISABLE_METHOD_NONE
- void pop_front() { erase(begin()); }
- GI_DISABLE_METHOD_NONE
- void push_back(const value_type &v) { insert(end(), v); }
- GI_DISABLE_METHOD_NONE
- void push_back(value_type &&v) { insert(end(), std::move(v)); }
- GI_DISABLE_METHOD_NONE
- void pop_back()
- {
- if (!empty())
- erase(end() - 1);
- }
- GI_DISABLE_METHOD_NONE
- iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
- GI_DISABLE_METHOD_NONE
- iterator erase(const_iterator first, const_iterator last)
- {
- if (this->data_ && first < last) {
- auto index = first - begin();
- auto cnt = last - first;
- // erase from back to front to minimize (or even avoid) data moves
- while (cnt) {
- --cnt;
- auto v = g_ptr_array_steal_index(this->data_, index + cnt);
- _traits::destroy<ElCType>::func(ElCType(v));
- }
- return begin() + index;
- }
- return begin();
- }
- GI_DISABLE_METHOD_NONE
- iterator insert(const_iterator pos, const value_type &v)
- {
- auto cv = unwrap(v, Transfer());
- return _insert(pos, cv);
- }
- GI_DISABLE_METHOD_NONE
- iterator insert(const_iterator pos, value_type &&v)
- {
- auto cv = unwrap(std::move(v), Transfer());
- return _insert(pos, cv);
- }
- // erase to avoid memory free
- GI_DISABLE_METHOD_NONE
- void clear() { erase(begin(), end()); }
- protected:
- iterator _insert(const_iterator pos, gpointer v)
- {
- // establish index before potential create
- auto index = pos - begin();
- _ensure_array();
- g_ptr_array_insert(this->data_, index, v);
- return begin() + index;
- }
- };
- struct GHashTableFuncs
- {
- constexpr static bool refcnt = true;
- using value_type = std::pair<gpointer, gpointer>;
- static GType get_type_() { return G_TYPE_HASH_TABLE; }
- static ::GHashTable *ref(::GHashTable *data)
- {
- return data ? g_hash_table_ref(data) : data;
- }
- static ::GHashTable *sink(::GHashTable *data)
- {
- return data ? g_hash_table_ref(data) : data;
- }
- static void free(::GHashTable *&data)
- {
- if (data) {
- g_hash_table_unref(data);
- data = nullptr;
- }
- }
- static ::GHashTable *float_(::GHashTable *data) { return data; }
- };
- template<typename ElCType, typename Transfer>
- struct list_ops<::GHashTable, ElCType, Transfer> : public GHashTableFuncs
- {};
- template<typename ElCType, typename Transfer>
- class CollectionBase<::GHashTable, ElCType, Transfer>
- : public Wrapper<GHashTableFuncs, ::GHashTable>
- {
- protected:
- using list_ops = GHashTableFuncs;
- using ElCKeyType = typename std::tuple_element<0, ElCType>::type;
- using ElCMappedType = typename std::tuple_element<1, ElCType>::type;
- using ElCppKeyType = typename elcpptype<ElCKeyType, Transfer>::type;
- using ElCppMappedType = typename elcpptype<ElCMappedType, Transfer>::type;
- static_assert(sizeof(ElCKeyType) == sizeof(gpointer), "");
- static_assert(sizeof(ElCMappedType) == sizeof(gpointer), "");
- using key_type = const ElCppKeyType;
- using mapped_type = ElCppMappedType;
- using value_type = std::pair<ElCppKeyType, mapped_type>;
- using const_value_type = std::pair<const ElCppKeyType, const mapped_type>;
- using state_type = int; // dummy
- state_type _create()
- {
- if (!this->data_) {
- if (std::is_same<Transfer, transfer_full_t>::value) {
- this->data_ = g_hash_table_new_full(_traits::hash<ElCppKeyType>::func,
- _traits::equal<ElCppKeyType>::func,
- GDestroyNotify(_traits::destroy<ElCppKeyType>::func),
- GDestroyNotify(_traits::destroy<ElCppMappedType>::func));
- } else {
- this->data_ = g_hash_table_new(_traits::hash<ElCppKeyType>::func,
- _traits::equal<ElCppKeyType>::func);
- }
- }
- return 0;
- }
- state_type _push(state_type s, list_ops::value_type item)
- {
- g_hash_table_replace(this->data_, item.first, item.second);
- return s;
- }
- void _finish() {}
- struct iterator_type
- {
- CollectionBase &self;
- GHashTableIter iter;
- int index{};
- iterator_type(CollectionBase &_self) : self(_self)
- {
- if (self.data_)
- g_hash_table_iter_init(&iter, self.data_);
- }
- bool next(list_ops::value_type &val)
- {
- if (!self.data_)
- return false;
- gpointer key, value;
- auto more = g_hash_table_iter_next(&iter, &key, &value);
- val = {key, value};
- return more;
- }
- };
- iterator_type _iterator() { return iterator_type{*this}; }
- void _steal()
- {
- g_hash_table_steal_all(this->data_);
- g_hash_table_unref(this->data_);
- this->data_ = nullptr;
- }
- public:
- class const_iterator
- : public detail::iterator<std::input_iterator_tag, const_value_type>
- {
- using self_type = const_iterator;
- enum State { NONE, INIT, ITERATING, DONE };
- ::GHashTableIter iter;
- std::pair<gpointer, gpointer> kv;
- State state;
- const_value_type *_cast()
- {
- // sanity check in view of rough cast
- static_assert(sizeof(kv) == sizeof(const_value_type), "");
- return reinterpret_cast<const_value_type *>(&kv);
- }
- ::GHashTable *_table() const
- {
- return state == NONE || state == DONE
- ? nullptr
- : g_hash_table_iter_get_hash_table(
- const_cast<::GHashTableIter *>(&iter));
- }
- public:
- const_iterator(
- ::GHashTable *table, gpointer key = nullptr, gpointer value = nullptr)
- : state(NONE)
- {
- if (table) {
- g_hash_table_iter_init(&iter, table);
- state = INIT;
- // if iterator is created as a result of a find,
- // then we arrange for lazy sync of hash table iterator
- if (G_LIKELY(!key && !value)) {
- state = ITERATING;
- ++(*this);
- } else {
- kv = {key, value};
- }
- }
- }
- const_value_type &operator*() { return *_cast(); }
- const_value_type *operator->() { return _cast(); }
- bool operator==(const self_type &other)
- {
- auto t = _table();
- return (t == other._table()) && (!t || (kv.first == other.kv.first));
- }
- bool operator!=(const self_type &other) { return !(*this == other); }
- self_type &operator++()
- {
- bool has_more = true;
- // if needed, sync iterator with key
- if (G_UNLIKELY(state != ITERATING)) {
- gpointer key{}, value{};
- while (has_more && key != kv.first) {
- has_more = g_hash_table_iter_next(&iter, &key, &value);
- }
- }
- if (G_LIKELY(has_more))
- has_more = g_hash_table_iter_next(&iter, &kv.first, &kv.second);
- state = has_more ? ITERATING : DONE;
- return *this;
- }
- };
- // consistency
- using iterator = const_iterator;
- const_iterator cbegin() const { return {this->data_}; }
- const_iterator cend() const { return {nullptr}; }
- const_iterator begin() const { return cbegin(); };
- const_iterator end() const { return cend(); };
- size_t size() const
- {
- return this->data_ ? g_hash_table_size(this->data_) : 0;
- }
- bool empty() const { return size() == 0; }
- GI_DISABLE_METHOD_NONE
- void clear()
- {
- if (this->data_)
- g_hash_table_remove_all(this->data_);
- };
- size_t count(key_type &key) const
- {
- if (!this->data_)
- return 0;
- auto v = unwrap(key, transfer_none);
- return g_hash_table_contains(this->data_, v) ? 1 : 0;
- }
- const_iterator find(key_type &key) const
- {
- if (this->data_) {
- auto v = unwrap(key, transfer_none);
- gpointer lv;
- if (g_hash_table_lookup_extended(this->data_, v, nullptr, &lv))
- return {this->data_, gpointer(v), gpointer(lv)};
- }
- return end();
- }
- typename elcpptype<ElCMappedType, transfer_none_t>::type lookup(
- key_type &key) const
- {
- auto it = find(key);
- if (it == end())
- return {};
- // decay copy should be possible
- return it->second;
- }
- GI_DISABLE_METHOD_NONE
- bool replace(ElCppKeyType &&key, ElCppMappedType &&value)
- {
- _create();
- auto kv = unwrap(std::move(key), Transfer());
- auto vv = unwrap(std::move(value), Transfer());
- return g_hash_table_replace(this->data_, kv, vv);
- }
- GI_DISABLE_METHOD_NONE
- size_t erase(key_type &key)
- {
- if (this->data_) {
- auto v = unwrap(key, transfer_none);
- return g_hash_table_remove(this->data_, gpointer(v)) ? 1 : 0;
- }
- return 0;
- }
- };
- template<typename ListType>
- struct ListFuncs
- {
- constexpr static bool refcnt = false;
- using value_type = gpointer;
- // this value type is used in e.g. signal argument
- static GType get_type_() { return G_TYPE_POINTER; }
- static ListType *ref(ListType *data) { return data; }
- };
- template<typename ElCType, typename Transfer>
- struct list_ops<::GList, ElCType, Transfer> : public ListFuncs<::GList>
- {};
- template<typename ElCType, typename Transfer>
- struct list_ops<::GSList, ElCType, Transfer> : public ListFuncs<::GSList>
- {};
- template<typename ListType>
- struct LinkedListOps;
- template<>
- struct LinkedListOps<::GSList>
- {
- static void free(::GSList *l) { g_slist_free(l); }
- static void free_full(::GSList *l, GDestroyNotify func)
- {
- g_slist_free_full(l, func);
- }
- static ::GSList *append(::GSList *l, gpointer data)
- {
- return g_slist_append(l, data);
- }
- static ::GSList *prepend(::GSList *l, gpointer data)
- {
- return g_slist_prepend(l, data);
- }
- static ::GSList *last(::GSList *l) { return g_slist_last(l); }
- static ::GSList *insert_before(::GSList *l, ::GSList *link, gpointer data)
- {
- return g_slist_insert_before(l, link, data);
- }
- static ::GSList *delete_link(::GSList *l, ::GSList *link)
- {
- return g_slist_delete_link(l, link);
- }
- static ::GSList *reverse(::GSList *l) { return g_slist_reverse(l); }
- };
- template<>
- struct LinkedListOps<::GList>
- {
- static void free(::GList *l) { g_list_free(l); }
- static void free_full(::GList *l, GDestroyNotify func)
- {
- return g_list_free_full(l, func);
- }
- static ::GList *append(::GList *l, gpointer data)
- {
- return g_list_append(l, data);
- }
- static ::GList *prepend(::GList *l, gpointer data)
- {
- return g_list_prepend(l, data);
- }
- static ::GList *last(::GList *l) { return g_list_last(l); }
- static ::GList *insert_before(::GList *l, ::GList *link, gpointer data)
- {
- return g_list_insert_before(l, link, data);
- }
- static ::GList *delete_link(::GList *l, ::GList *link)
- {
- return g_list_delete_link(l, link);
- }
- static ::GList *reverse(::GList *l) { return g_list_reverse(l); }
- };
- template<typename ListType, typename ElType, typename Transfer>
- struct LinkedListBase;
- template<typename ListType, typename ElCType>
- struct LinkedListBase<ListType, ElCType, transfer_none_t>
- : public SharedWrapper<ListType>
- {
- protected:
- static void _deleter(ListType *&d, ...) { d = nullptr; }
- static ListType *_copy(ListType *d) { return d; }
- };
- template<typename ListType, typename ElCType>
- struct LinkedListBase<ListType, ElCType, transfer_container_t>
- : public SharedWrapper<ListType>
- {
- protected:
- static void _deleter(ListType *&l, ...)
- {
- LinkedListOps<ListType>::free(l);
- l = nullptr;
- }
- };
- template<typename ListType, typename ElCType>
- struct LinkedListBase<ListType, ElCType, transfer_full_t>
- : public SharedWrapper<ListType>
- {
- protected:
- static void _deleter(ListType *&l, ...)
- {
- LinkedListOps<ListType>::free_full(
- l, (GDestroyNotify)_traits::destroy<ElCType>::func);
- l = nullptr;
- }
- };
- // enable copy constructor on none transfer cases
- // compiler otherwise always selects the deleted one, even if other options
- template<typename Transfer, typename Base>
- using SelectBaseWrapper =
- typename std::conditional<std::is_same<Transfer, transfer_none_t>::value,
- CopyWrapper<Base>, MoveWrapper<Base>>::type;
- template<typename ListType, typename ElCType, typename Transfer>
- class CollectionBase<ListType, ElCType, Transfer,
- typename std::enable_if<std::is_same<ListType, ::GSList>::value ||
- std::is_same<ListType, ::GList>::value>::type>
- : public SelectBaseWrapper<Transfer,
- LinkedListBase<ListType, ElCType, Transfer>>
- {
- protected:
- using list_ops = ListFuncs<ListType>;
- using state_type = ListType *;
- state_type _create() { return nullptr; }
- state_type _push(state_type, typename list_ops::value_type item)
- {
- this->data_ = LinkedListOps<ListType>::prepend(this->data_, item);
- return nullptr;
- }
- void _finish()
- {
- this->data_ = LinkedListOps<ListType>::reverse(this->data_);
- }
- struct iterator_type
- {
- ListType *it;
- bool next(typename list_ops::value_type &val)
- {
- if (it) {
- val = it->data;
- it = it->next;
- return true;
- }
- return false;
- }
- };
- iterator_type _iterator() { return {this->data_}; }
- void _steal()
- {
- LinkedListOps<ListType>::free(this->data_);
- this->data_ = nullptr;
- }
- static void _destroy(gpointer value)
- {
- _traits::destroy<ElCType>::func(ElCType(value));
- }
- public:
- using value_type = typename elcpptype<ElCType, Transfer>::type;
- static constexpr bool is_glist = std::is_same<ListType, ::GList>::value;
- template<typename value_t>
- class iterator_t : public detail::iterator<std::forward_iterator_tag, value_t>
- {
- using self_type = iterator_t;
- friend class CollectionBase;
- ListType *it = nullptr;
- public:
- iterator_t(ListType *l = nullptr) : it(l){};
- // construct const from non-const
- template<typename other_value_t,
- typename Enable = typename std::enable_if<
- std::is_convertible<other_value_t *, value_t *>::value>::type>
- iterator_t(const iterator_t<other_value_t> &oi) : iterator_t(oi.it)
- {}
- value_t &operator*() { return *operator->(); }
- value_t *operator->()
- {
- return wrap_cast<Transfer>((ElCType *)(&it->data));
- }
- bool operator==(const self_type &other) { return it == other.it; }
- bool operator!=(const self_type &other) { return !(*this == other); }
- self_type &operator++()
- {
- if (it)
- it = it->next;
- return *this;
- }
- self_type operator++(int)
- {
- auto ret = *this;
- ++(*this);
- return ret;
- }
- };
- using const_iterator = iterator_t<const value_type>;
- using iterator = iterator_t<value_type>;
- const_iterator begin() const { return cbegin(); }
- const_iterator end() const { return cend(); }
- const_iterator cbegin() const { return {this->data_}; }
- const_iterator cend() const { return {}; }
- GI_DISABLE_METHOD_NONE
- iterator begin() { return {this->data_}; }
- GI_DISABLE_METHOD_NONE
- iterator end() { return {}; }
- bool empty() const noexcept { return !this->data_; }
- GI_DISABLE_METHOD_NONE
- void push_front(const value_type &v)
- {
- auto cv = unwrap(v, Transfer());
- this->data_ = LinkedListOps<ListType>::prepend(this->data_, cv);
- }
- GI_DISABLE_METHOD_NONE
- void push_front(value_type &&v)
- {
- auto cv = unwrap(std::move(v), Transfer());
- this->data_ = LinkedListOps<ListType>::prepend(this->data_, cv);
- }
- GI_DISABLE_METHOD_NONE
- void pop_front()
- {
- if (this->data_)
- _destroy(this->data_->data);
- this->data_ =
- LinkedListOps<ListType>::delete_link(this->data_, this->data_);
- }
- GI_DISABLE_METHOD_NONE
- void push_back(const value_type &v)
- {
- auto cv = unwrap(v, Transfer());
- this->data_ = LinkedListOps<ListType>::append(this->data_, cv);
- }
- GI_DISABLE_METHOD_NONE
- void push_back(value_type &&v)
- {
- auto cv = unwrap(std::move(v), Transfer());
- this->data_ = LinkedListOps<ListType>::append(this->data_, cv);
- }
- GI_DISABLE_METHOD_NONE
- void pop_back()
- {
- auto last = LinkedListOps<ListType>::last(this->data_);
- if (last)
- _destroy(last->data);
- this->data_ = LinkedListOps<ListType>::delete_link(this->data_, last);
- }
- GI_DISABLE_METHOD_NONE
- iterator erase(const_iterator pos)
- {
- ListType *next = nullptr;
- if (pos.it) {
- next = pos.it->next;
- _destroy(pos.it->data);
- }
- this->data_ = LinkedListOps<ListType>::delete_link(this->data_, pos.it);
- return {next};
- }
- template<typename Enable = void,
- typename Check = typename std::enable_if<
- std::is_void<Enable>::value && Transfer().value && is_glist>::type>
- iterator insert(const_iterator pos, const value_type &v)
- {
- auto cv = unwrap(v, Transfer());
- return _insert(pos, cv);
- }
- template<typename Enable = void,
- typename Check = typename std::enable_if<
- std::is_void<Enable>::value && Transfer().value && is_glist>::type>
- iterator insert(const_iterator pos, value_type &&v)
- {
- auto cv = unwrap(std::move(v), Transfer());
- return _insert(pos, cv);
- }
- GI_DISABLE_METHOD_NONE
- void reverse()
- {
- this->data_ = LinkedListOps<ListType>::reverse(this->data_);
- }
- GI_DISABLE_METHOD_NONE
- void clear()
- {
- this->_deleter(this->data_);
- this->data_ = nullptr;
- }
- protected:
- iterator _insert(const_iterator pos, gpointer v)
- {
- this->data_ =
- LinkedListOps<ListType>::insert_before(this->data_, pos.it, v);
- return {pos.it ? pos.it->prev : LinkedListOps<ListType>::last(this->data_)};
- }
- };
- enum SpanType {
- DYNAMIC = -1,
- ZT = 0,
- };
- template<int SIZE>
- struct Span
- {};
- template<typename ElCType, typename Transfer, int SIZE>
- struct SpanBase : public SharedWrapper<ElCType>
- {
- protected:
- // extra members needed in this case
- // only > 0 if data locally allocated (as opposed to from elsewhere)
- size_t capacity_ = 0;
- size_t size_ = 0;
- static void _deleter(decltype(SpanBase::data_) &d, SpanBase *self)
- {
- assert(self);
- if (std::is_same<Transfer, transfer_full_t>::value && d) {
- auto end = d + self->size_;
- for (auto p = d; p != end; ++p) {
- _traits::destroy<ElCType>::func(*p);
- }
- }
- if (!std::is_same<Transfer, transfer_none_t>::value)
- g_free(d);
- d = nullptr;
- }
- template<typename Enable = void>
- static decltype(SpanBase::data_) _copy(decltype(SpanBase::data_) d)
- {
- // should only end up used in none case
- static_assert(std::is_same<Transfer, transfer_none_t>::value, "");
- return d;
- }
- };
- template<typename ElCType, typename Transfer, int SIZE>
- struct list_ops<Span<SIZE>, ElCType, Transfer> : public ListFuncs<ElCType>
- {};
- template<typename ElCType, typename Transfer, int SIZE>
- class CollectionBase<Span<SIZE>, ElCType, Transfer>
- : public SelectBaseWrapper<Transfer, SpanBase<ElCType, Transfer, SIZE>>
- {
- protected:
- static auto constexpr SPAN_SIZE = SIZE;
- struct list_ops : public ListFuncs<ElCType>
- {
- using value_type = ElCType;
- };
- using state_type = ElCType *;
- // used by wrap
- CollectionBase(ElCType *p = nullptr, int s = 0)
- {
- // NOTE no copy needed;
- // either transfer is full/container (and ownership is assumed)
- // either transfer is none (and will never free/release)
- this->data_ = p;
- this->capacity_ = 0;
- // normalize -1 from C side to avoid potential surprises elsewhere
- if (s < 0 || SIZE == ZT) {
- s = 0;
- if (p) {
- while (*p) {
- ++s;
- ++p;
- }
- }
- }
- this->size_ = s;
- }
- ElCType *_create()
- {
- if (!this->data_) {
- this->capacity_ = SIZE > 0 ? SIZE : 2;
- this->data_ = (ElCType *)g_malloc0(this->capacity_ * sizeof(ElCType));
- this->size_ = 0;
- }
- return this->data_;
- }
- void _create_from(ElCType *d, size_t lsize)
- {
- using T = ElCType;
- // try to re-use incoming data
- // otherwise fall back to plain copy
- // (if transfer or zero-termination fiddling is needed)
- bool allocate = (Transfer().value != transfer_none.value) || (SIZE == ZT);
- auto size = SIZE == ZT ? lsize + 1 : lsize;
- auto *data = d;
- if (allocate) {
- data = (T *)g_malloc(size * sizeof(T));
- this->capacity_ = size;
- memcpy(data, d, lsize * sizeof(T));
- if (SIZE == ZT)
- data[lsize] = T{};
- }
- // track data
- assert(!this->data_);
- g_free(this->data_);
- this->data_ = data;
- this->size_ = lsize;
- }
- void _reserve(size_t capacity)
- {
- if (this->capacity_ < capacity + (SIZE == ZT)) {
- this->capacity_ =
- std::max(std::max(2 * this->capacity_, size_t(2)), capacity);
- this->data_ =
- (ElCType *)g_realloc_n(this->data_, this->capacity_, sizeof(ElCType));
- }
- }
- state_type _push(state_type s, ElCType item)
- {
- // should only get here if previously allocated
- assert(this->capacity_ > 0);
- auto offset = s - this->data_;
- _reserve(offset + 1);
- s = this->data_ + offset;
- *s = item;
- ++this->size_;
- ++s;
- // ensure ZT if applicable
- if (SIZE == ZT) {
- assert(size_t(s - this->data_) < this->capacity_);
- *s = ElCType{};
- }
- return s;
- }
- void _finish()
- {
- if ((SIZE > 0) && (this->size_ != (size_t)SIZE))
- g_error("fixed list size mismatch");
- }
- struct iterator_type
- {
- CollectionBase &self;
- ElCType *p;
- iterator_type(CollectionBase &s) : self(s), p(self.data_) {}
- explicit operator bool()
- {
- return p && (ZT ? !!*p : (p != self.data_ + self.size_));
- };
- bool next(ElCType &val)
- {
- if (*this) {
- val = *(p++);
- return true;
- }
- return false;
- }
- };
- iterator_type _iterator() { return {*this}; }
- void _steal()
- {
- // if Holder is trying to destroy, check if we really allocated
- // (may have optimized array case)
- if (!std::is_same<Transfer, transfer_none_t>::value || this->capacity_)
- g_free(this->data_);
- this->data_ = nullptr;
- this->size_ = this->capacity_ = 0;
- };
- public:
- // this one is used by code generation (the unwrap size part)
- // so it should only be used in specific situations
- // let's add checks to make it so
- size_t _size() const
- {
- static_assert(SIZE != ZT, "");
- return this->size_;
- }
- using value_type = typename elcpptype<ElCType, Transfer>::type;
- // assume same layout of wrapper and wrappee
- static_assert(sizeof(value_type) == sizeof(ElCType), "");
- static_assert(sizeof(value_type[2]) == sizeof(ElCType[2]), "");
- using iterator = value_type *;
- using const_iterator = const value_type *;
- const_iterator cbegin() const
- {
- return this->data_ ? wrap_cast<Transfer>(this->data_) : nullptr;
- }
- const_iterator cend() const
- {
- return this->data_ ? begin() + this->size_ : nullptr;
- }
- const_iterator begin() const { return cbegin(); }
- const_iterator end() const { return cend(); }
- GI_DISABLE_METHOD_NONE
- iterator begin()
- {
- return this->data_ ? wrap_cast<Transfer>(this->data_) : nullptr;
- }
- GI_DISABLE_METHOD_NONE
- iterator end() { return this->data_ ? begin() + this->size_ : nullptr; }
- size_t size() const { return this->size_; }
- bool empty() const { return size() == 0; }
- GI_DISABLE_METHOD_NONE
- void push_front(const value_type &v) { insert(begin(), v); }
- GI_DISABLE_METHOD_NONE
- void push_front(value_type &&v) { insert(begin(), std::move(v)); }
- GI_DISABLE_METHOD_NONE
- void pop_front() { erase(begin()); }
- GI_DISABLE_METHOD_NONE
- void push_back(const value_type &v) { insert(end(), v); }
- GI_DISABLE_METHOD_NONE
- void push_back(value_type &&v) { insert(end(), std::move(v)); }
- GI_DISABLE_METHOD_NONE
- void pop_back()
- {
- if (!empty())
- erase(end() - 1);
- }
- GI_DISABLE_METHOD_NONE
- iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
- GI_DISABLE_METHOD_NONE
- iterator erase(const_iterator first, const_iterator last)
- {
- if (this->data_ && this->size_ > 0 && first < last) {
- size_t index = first - begin();
- size_t cnt = last - first;
- size_t lindex = std::min(index + cnt, this->size_);
- for (auto i = index; i < lindex; ++i)
- _traits::destroy<ElCType>::func(this->data_[index]);
- if (lindex < this->size_)
- memmove(this->data_ + index, this->data_ + lindex,
- sizeof(*this->data_) * (this->size_ - lindex));
- this->size_ -= cnt;
- if (SIZE == ZT)
- this->data_[this->size_] = ElCType{};
- return begin() + index;
- }
- return begin();
- }
- GI_DISABLE_METHOD_NONE
- iterator insert(const_iterator pos, const value_type &v)
- {
- auto cv = unwrap(v, Transfer());
- return _insert(pos, cv);
- }
- GI_DISABLE_METHOD_NONE
- iterator insert(const_iterator pos, value_type &&v)
- {
- auto cv = unwrap(std::move(v), Transfer());
- return _insert(pos, cv);
- }
- size_t capacity() const { return this->capacity_; }
- GI_DISABLE_METHOD_NONE
- void reserve(size_t capacity) { _reserve(capacity); }
- // erase to avoid memory free
- GI_DISABLE_METHOD_NONE
- void clear() { erase(begin(), end()); }
- protected:
- iterator _insert(const_iterator pos, ElCType v)
- {
- // establish index before potential create
- size_t index = pos - begin();
- // grow if needed
- _reserve(this->size_ + 1);
- // move if needed
- if (index < this->size_)
- memmove(this->data_ + index + 1, this->data_ + index,
- sizeof(*this->data_) * (this->size_ - index));
- // insert
- ++this->size_;
- this->data_[index] = v;
- // ensure ZT if applicable
- if (SIZE == ZT) {
- assert(this->size_ < this->capacity_);
- this->data_[this->size_] = ElCType{};
- }
- return begin() + index;
- }
- };
- // class tag
- class Container
- {};
- // minor helper, essentially compile-time signal/notification
- template<typename T>
- struct Notifier
- {
- static constexpr bool construct_none = false;
- static void _updated(T *, bool /*create*/) {}
- };
- // considered owning for container or full
- // unwrap will have to release_()
- template<typename ListType, typename T, typename Transfer = transfer_full_t,
- typename Notify = Notifier<void>, typename ExtraBase = Container>
- struct Collection : public CollectionBase<ListType, T, Transfer>,
- public ExtraBase
- {
- protected:
- using self_type = Collection;
- using super_type = CollectionBase<ListType, T, Transfer>;
- public:
- using list_ops = typename super_type::list_ops;
- protected:
- using ElTransfer = typename element_transfer<Transfer>::type;
- // decompose pair in case of map
- template<typename G>
- struct get_types
- {
- // map to harmless type if no key applicable
- using key_type = bool;
- using mapped_type = G;
- using cpp_type = typename traits::cpptype<G, ElTransfer>::type;
- };
- template<typename T1, typename T2>
- struct get_types<std::pair<T1, T2>>
- {
- using key_type = T1;
- using mapped_type = T2;
- using cpp_type = std::pair<typename traits::cpptype<T1, ElTransfer>::type,
- typename traits::cpptype<T2, ElTransfer>::type>;
- };
- using KeyCType = typename get_types<T>::key_type;
- using ElCType = typename get_types<T>::mapped_type;
- using KeyCppType = typename traits::cpptype<KeyCType, ElTransfer>::type;
- using ElCppType = typename traits::cpptype<ElCType, ElTransfer>::type;
- using ValueCppType = typename get_types<T>::cpp_type;
- // sanity/consistency checks
- // expect normalized template types
- static_assert(
- std::is_same<ListType, typename std::decay<ListType>::type>::value, "");
- static_assert(
- std::is_same<KeyCppType, typename std::decay<KeyCppType>::type>::value,
- "");
- static_assert(
- std::is_same<ElCppType, typename std::decay<ElCppType>::type>::value, "");
- // internal wrap of list-case
- Collection(ListType *l, bool own)
- {
- this->data_ = ((!own && l) ? list_ops::ref(l) : l);
- }
- // internal wrap of span case
- // (also avoid conflict with a public constructor below)
- template<typename Enable = void>
- Collection(ElCType *d, size_t s, std::nullptr_t) : super_type(d, s)
- {}
- template<typename T1, typename T2, typename LVR>
- std::pair<gpointer, gpointer> unwrap_item(std::pair<T1, T2> &el, LVR)
- {
- // NOTE if compiler error occurs here, perhaps std::move() input
- auto item1 = unwrap(cast_ref(el.first, LVR()), ElTransfer());
- static_assert(std::is_pointer<decltype(item1)>::value, "expected pointer");
- auto item2 = unwrap(cast_ref(el.second, LVR()), ElTransfer());
- static_assert(std::is_pointer<decltype(item2)>::value, "expected pointer");
- return {(gpointer)item1, (gpointer)item2};
- };
- template<typename T1, typename LVR>
- typename list_ops::value_type unwrap_item(T1 &el, LVR)
- {
- // NOTE if compiler error occurs here, perhaps std::move() input
- auto item = unwrap(cast_ref(el, LVR()), ElTransfer());
- // only force pointers or same size to same size
- using item_type = decltype(item);
- static_assert(
- std::is_pointer<item_type>::value ==
- std::is_pointer<typename list_ops::value_type>::value &&
- sizeof(item_type) == sizeof(typename list_ops::value_type),
- "expected pointer or matched size");
- return (typename list_ops::value_type)item;
- }
- template<typename Iterator, typename LVR>
- void create_from(Iterator first, Iterator last, LVR)
- {
- // empty container is not null, so always init/create
- // NOTE should also add leading/initial zero if needed
- Notify::_updated(this, true);
- auto s = this->_create();
- while (first != last) {
- auto &el = *first;
- auto item = unwrap_item(el, LVR());
- s = this->_push(s, item);
- ++first;
- }
- // possible (size) check might be done here
- this->_finish();
- }
- // array as surrogate generic container
- template<typename InputT>
- void create_from_array(InputT *d, size_t s, std::false_type)
- {
- create_from(d, d + s, std::true_type());
- }
- // array shortcut case; from plain array to matching span
- template<typename InputT>
- void create_from_array(InputT *d, size_t s, std::true_type)
- {
- // should be around as this should be Span case
- // add cast to avoid const issues etc
- using v_type = typename list_ops::value_type;
- static_assert(sizeof(v_type) == sizeof(InputT), "");
- Notify::_updated(this, true);
- this->_create_from((v_type *)d, s);
- }
- template<typename Out, typename Enable = void>
- struct get_value_type
- {
- using type = typename Out::container_type::value_type;
- };
- template<typename Tp>
- struct get_value_type<Tp *>
- {
- using type = typename std::decay<Tp>::type;
- };
- template<typename OutIterator, typename T1, typename T2>
- void wrap_item_add(std::pair<T1, T2> &el, OutIterator &out)
- {
- using DestType = typename get_value_type<OutIterator>::type;
- // expected to be a pair
- using DestType1 = typename std::tuple_element<0, DestType>::type;
- using DestType2 = typename std::tuple_element<1, DestType>::type;
- static_assert(!(std::is_same<Transfer, transfer_full_t>::value &&
- (traits::is_reftype<DestType1>::value ||
- traits::is_reftype<DestType2>::value)),
- "full transfer to reftype");
- auto item1 = wrap((KeyCType)el.first, ElTransfer());
- auto item2 = wrap((ElCType)el.second, ElTransfer());
- *out = std::make_pair(std::move(item1), std::move(item2));
- };
- template<typename OutIterator, typename T1>
- void wrap_item_add(T1 &el, OutIterator &out)
- {
- // validation check; no transfer full to a reftype
- using DestType = typename get_value_type<OutIterator>::type;
- // sanity check that we probably got the right type
- static_assert(std::is_convertible<ElCppType, DestType>::value, "");
- static_assert(!(std::is_same<Transfer, transfer_full_t>::value &&
- traits::is_reftype<DestType>::value),
- "full transfer to reftype");
- using item_type = typename std::decay<decltype(el)>::type;
- // would also lead to cast failure below, but make it more expressive
- static_assert(
- std::is_pointer<item_type>::value == std::is_pointer<ElCType>::value &&
- sizeof(item_type) == sizeof(ElCType),
- "expected pointer or matched size");
- // could have const char** or so, so let's (const) cast the hard way
- *out = wrap((ElCType)el, ElTransfer());
- }
- public:
- // some common type blurbs
- using value_type = typename super_type::value_type;
- using size_type = std::size_t;
- using difference_type = std::ptrdiff_t;
- using reference = value_type &;
- using const_reference = const value_type &;
- static GType get_type_() { return list_ops::get_type_(); }
- // used by code generation (for non-plain array case)
- // but could be of general use
- template<typename OutIterator>
- void move_to(OutIterator out) &&
- {
- // NOTE assumption;
- // out creates as it goes (e.g. back_inserter)
- // or there is enough output space
- typename list_ops::value_type item;
- auto it = this->_iterator();
- while (it.next(item)) {
- wrap_item_add(item, out);
- ++out;
- }
- // clear out elements without destroying them if needed
- // (if we pulled their ownership with full transfer)
- constexpr bool steal = std::is_same<ElTransfer, transfer_full_t>::value;
- // provide for equally cleared state in either case
- Notify::_updated(this, false);
- if (steal && this->data_) {
- this->_steal();
- } else if (this->data_) {
- this->~self_type();
- this->data_ = nullptr;
- }
- }
- public:
- // group a few helpers
- struct _detail
- {
- using DataType = decltype(std::declval<super_type>().gobj_());
- // check if it is possible to create a container
- // a none variant does not assume ownership
- // so it is not in a position to create a collection (with no other owner)
- // however, as usual, the ref types can always own
- // also, allow tweaking this restriction (e.g. for holder subclass below)
- static constexpr bool constructible =
- !std::is_same<Transfer, transfer_none_t>::value || list_ops::refcnt ||
- Notify::construct_none;
- static constexpr bool is_span = !std::is_class<typename std::decay<
- decltype(*std::declval<super_type>().gobj_())>::type>::value;
- template<bool B, typename Enable = void>
- struct span_size_impl : public std::integral_constant<int, 0>
- {};
- template<typename Enable>
- struct span_size_impl<true, Enable>
- : public std::integral_constant<int,
- super_type::SPAN_SIZE >= 0 ? super_type::SPAN_SIZE : 0>
- {};
- static constexpr int span_size = span_size_impl<is_span>::value;
- static constexpr bool is_fixed_span = is_span && span_size > 0;
- // check if this is Span<> collection of plain V elements
- // (the deref of data_ is so we only end up matching a Span<> case)
- template<typename V>
- using is_plain = typename std::conditional<
- is_span && traits::is_plain<typename std::decay<V>::type>::value &&
- std::is_same<typename list_ops::value_type,
- typename std::decay<V>::type>::value,
- std::true_type, std::false_type>::type;
- // check Cpp type compatibility
- // this could be triggered on non-complete V, in which case
- // std::is_convertible is undefined, so guard that approach
- template<typename V, bool Complete = false>
- struct is_acceptable_helper : public std::integral_constant<bool, false>
- {};
- template<typename V>
- struct is_acceptable_helper<V, true>
- : public std::is_convertible<V, ValueCppType>
- {};
- template<typename V>
- using is_acceptable = typename is_acceptable_helper<V,
- traits::is_type_complete<V>::value>::type;
- // likewise, deal with const variations on e.g. char**
- // (as char* can be converted to const char*)
- template<typename CType>
- using is_compatible =
- typename std::conditional<std::is_convertible<ElCType, CType>::value,
- std::true_type, std::false_type>::type;
- };
- Collection(std::nullptr_t = nullptr) {}
- // a non-owning accepts from all other types
- // (either the other owns or there is another non-owner already)
- template<typename OtherTransfer,
- typename Enable = typename std::enable_if<
- !std::is_same<void, OtherTransfer>::value &&
- std::is_same<Transfer, transfer_none_t>::value>::type>
- Collection(const Collection<ListType, T, OtherTransfer> &cother)
- {
- // never mind any const, as usual
- auto &other = const_cast<Collection<ListType, T, OtherTransfer> &>(cother);
- this->data_ = other.gobj_() ? list_ops::ref(other.gobj_()) : nullptr;
- }
- // construct from reasonably matching container
- // NOTE may need r-value input for non-movable (content) types
- template<typename Container,
- typename Check = typename std::enable_if<
- _detail::template is_acceptable<
- typename std::decay<Container>::type::value_type>::value &&
- _detail::constructible>::type,
- typename Enable =
- typename detail::disable_if_same_or_derived<Collection, Container>>
- Collection(Container &&c)
- {
- create_from(
- std::begin(c), std::end(c), std::is_lvalue_reference<Container>());
- }
- // construct from initializer list
- template<typename InitElement,
- typename Check = typename std::enable_if<
- _detail::template is_acceptable<InitElement>::value &&
- _detail::constructible>::type,
- typename Enable =
- typename detail::disable_if_same_or_derived<Collection, Container>>
- Collection(std::initializer_list<InitElement> c)
- {
- // initializer_list iterator is a const iterator, so no moving from
- create_from(std::begin(c), std::end(c), std::true_type());
- }
- // construct from array
- // (these arguments could come from a vector, or span, or ...)
- // NOTE no evident way to mark as r-value/movable-from,
- // so it will just have to do and work out
- template<typename InputT,
- typename Check = typename std::enable_if<
- _detail::template is_acceptable<InputT>::value &&
- _detail::constructible>::type>
- Collection(InputT *d, size_t s)
- {
- create_from_array(d, s, typename _detail::template is_plain<InputT>());
- }
- // construct from fixed-size array if this is a fixed-size Span<> case
- // NOTE other notes as above apply
- template<typename InputT,
- typename Check = typename std::enable_if<
- _detail::template is_acceptable<InputT>::value &&
- _detail::constructible>::type,
- typename Enable = typename std::enable_if<
- !std::is_same<InputT, void>::value && _detail::is_fixed_span>::type>
- Collection(InputT (&d)[_detail::span_size])
- {
- create_from_array(d, super_type::SPAN_SIZE,
- typename _detail::template is_plain<InputT>());
- }
- // Avoid std::allocator type; check for const_iterator member.
- // Disable conversion to an std::initializer_list (also has const_iterator).
- // (in C++11 std::vector<T>::operator= is overloaded to take either a
- // std::vector<T> or an std::initializer_list<T>).
- // move to container
- // (clear/empty this one)
- template<typename Container,
- typename Check = typename std::enable_if<
- _detail::template is_acceptable<
- typename std::decay<Container>::type::value_type>::value &&
- sizeof(typename std::decay<Container>::type::const_iterator) &&
- !trait::is_initializer_list<
- typename std::remove_reference<Container>::type>::value>::type>
- operator Container() &&
- {
- Container result;
- std::move(*this).move_to(std::inserter(result, result.end()));
- return result;
- }
- // generic method(s)
- GI_DISABLE_METHOD_NONE
- value_type &front() { return *this->begin(); }
- const value_type &front() const { return *this->begin(); }
- #define GI_ENABLE_METHOD_ARRAY_NOT_NONE \
- template<typename Enable = void, \
- typename Check = typename std::enable_if< \
- std::is_void<Enable>::value && \
- std::is_pointer<typename super_type::const_iterator>::value && \
- !std::is_same<Transfer, transfer_none_t>::value>::type>
- #define GI_ENABLE_METHOD_ARRAY \
- template<typename Enable = void, \
- typename Check = typename std::enable_if< \
- std::is_void<Enable>::value && \
- std::is_pointer<typename super_type::const_iterator>::value>::type>
- GI_ENABLE_METHOD_ARRAY_NOT_NONE
- value_type &back() { return *(this->end() - 1); }
- GI_ENABLE_METHOD_ARRAY
- const value_type &back() const { return *(this->end() - 1); }
- GI_ENABLE_METHOD_ARRAY_NOT_NONE
- value_type *data() { return this->begin(); }
- GI_ENABLE_METHOD_ARRAY
- const value_type *data() const { return this->begin(); }
- GI_ENABLE_METHOD_ARRAY_NOT_NONE
- value_type &operator[](size_type pos) { return *(this->begin() + pos); }
- GI_ENABLE_METHOD_ARRAY
- const value_type &operator[](size_type pos) const
- {
- return *(this->begin() + pos);
- }
- GI_ENABLE_METHOD_ARRAY_NOT_NONE
- value_type &at(size_type pos)
- {
- if (pos >= this->size())
- try_throw(std::out_of_range("Collection::at"));
- return *(this->begin() + pos);
- }
- GI_ENABLE_METHOD_ARRAY
- const value_type &at(size_type pos) const
- {
- if (pos >= this->size())
- try_throw(std::out_of_range("Collection::at"));
- return *(this->begin() + pos);
- }
- GI_ENABLE_METHOD_ARRAY
- void resize(size_type s)
- {
- auto size = this->size();
- if (s < size) {
- this->erase(this->begin() + s, this->end());
- } else if (s > size) {
- auto cnt = s - size;
- while (cnt) {
- this->push_back({});
- --cnt;
- }
- }
- }
- GI_DISABLE_METHOD_NONE
- void swap(Collection &other)
- {
- std::swap((super_type &)*this, (super_type &)other);
- }
- #undef GI_ENABLE_METHOD_ARRAY
- #undef GI_ENABLE_METHOD_ARRAY_NOT_NONE
- // in unwrap below;
- // refcnt based cases support all sorts of transfer,
- // otherwise transfer must match the Transfer encoded in type
- // r-value case; data can be snatched from this instance
- template<typename ReqTransfer,
- typename std::enable_if<std::is_same<Transfer, ReqTransfer>::value ||
- list_ops::refcnt>::type * = nullptr>
- typename _detail::DataType _unwrap(const ReqTransfer &t) &&
- {
- auto l = this->data_;
- // be nice to subtype below
- // avoid yanking away possibly owned value
- if (t.value != transfer_none.value)
- this->data_ = nullptr;
- return l;
- }
- // l-value case; no snatching, so only transfer none (if not refcnt)
- template<typename ReqTransfer,
- typename std::enable_if<
- list_ops::refcnt ||
- (std::is_same<Transfer, ReqTransfer>::value &&
- std::is_same<ReqTransfer, transfer_none_t>::value)>::type * =
- nullptr>
- typename _detail::DataType _unwrap(const ReqTransfer &t) &
- {
- if (list_ops::refcnt)
- return ((t.value != transfer_none.value) && this->data_)
- ? list_ops::ref(this->data_)
- : this->data_;
- // must be none transfer in this case
- return this->data_;
- }
- template<typename CppType, typename ReqTransfer,
- typename std::enable_if<std::is_same<Transfer, ReqTransfer>::value &&
- !_detail::is_span>::type * = nullptr>
- static CppType _wrap(const ListType *obj, const ReqTransfer &t)
- {
- static_assert(sizeof(CppType) == sizeof(self_type), "invalid wrap");
- static_assert(std::is_base_of<self_type, CppType>::value, "invalid wrap");
- self_type w(const_cast<ListType *>(obj), t.value);
- return std::move(*static_cast<CppType *>(&w));
- }
- // special case; wrap of 2 arguments (ptr and size)
- // (could be combined in a single span,
- // but it would have to be unpacked anyway)
- template<typename CppType, typename CType, typename ReqTransfer,
- typename std::enable_if<
- std::is_same<Transfer, ReqTransfer>::value && _detail::is_span &&
- _detail::template is_compatible<CType>::value>::type * = nullptr>
- static CppType _wrap(CType *obj, int s, const ReqTransfer &)
- {
- static_assert(sizeof(CppType) == sizeof(self_type), "invalid wrap");
- static_assert(std::is_base_of<self_type, CppType>::value, "invalid wrap");
- // select internal protected constructor
- self_type w(const_cast<ElCType *>(obj), s, nullptr);
- return std::move(*static_cast<CppType *>(&w));
- }
- // likewise, ZT
- template<typename CppType, typename CType, typename ReqTransfer,
- typename std::enable_if<
- std::is_same<Transfer, ReqTransfer>::value && _detail::is_span &&
- _detail::span_size == SpanType::ZT>::type * = nullptr>
- static CppType _wrap(CType *obj, const ReqTransfer &)
- {
- return _wrap<CppType>(obj, -1, ReqTransfer());
- }
- template<typename CppType,
- typename std::enable_if<!std::is_void<CppType>::value &&
- list_ops::refcnt>::type * = nullptr>
- static CppType _get_value(const GValue *v)
- {
- auto wv = std::is_same<Transfer, transfer_none_t>::value()
- ? g_value_get_boxed(v)
- : g_value_dup_boxed(v);
- return _wrap<CppType>((ListType *)(wv), Transfer());
- }
- template<typename CppType,
- typename std::enable_if<!std::is_void<CppType>::value &&
- !list_ops::refcnt>::type * = nullptr>
- static CppType _get_value(const GValue *v)
- {
- // tracked as raw gpointer
- auto wv = g_value_get_pointer(v);
- return _wrap<CppType>((ElCType *)(wv), Transfer());
- }
- void _set_value(GValue *v)
- {
- if (list_ops::refcnt) {
- g_value_set_boxed(v, this->gobj_());
- } else {
- g_value_set_pointer(v, this->gobj_());
- }
- }
- };
- // add additional owner tracking data
- // (see below for reason of simple/silly separate type)
- struct OwnerData : public Container
- {
- bool own_ = false;
- };
- template<typename... Args>
- struct NotifierThunk;
- // should only end up used with transfer_none
- template<typename ListType, typename T, typename Transfer = transfer_none_t>
- class CollectionHolder : public Collection<ListType, T, Transfer,
- NotifierThunk<ListType, T, Transfer>, OwnerData>
- {
- // ownership tracked by OwnerData
- // as notified by helper which needs access
- friend struct NotifierThunk<ListType, T, Transfer>;
- // to call here
- void _updated(bool create)
- {
- if (create) {
- this->own_ = true;
- } else {
- this->~CollectionHolder();
- }
- }
- public:
- // NOTE OwnerData is inserted as a lower base class to ensure it is
- // initialized soon enough (before the using'ed constructor will notify
- // of creation)
- using super_type = Collection<ListType, T, Transfer,
- NotifierThunk<ListType, T, Transfer>, OwnerData>;
- using super_type::super_type;
- // only allowed (limited) move
- // (even if some parents might allow more)
- CollectionHolder(CollectionHolder &&other) : super_type(other)
- {
- this->own_ = other.own_;
- other.own_ = false;
- }
- CollectionHolder &operator=(CollectionHolder &&other)
- {
- if (this != &other) {
- this->~CollectionHolder();
- (super_type &)(*this) = std::move(other);
- this->own_ = other.own_;
- other.own_ = false;
- }
- return *this;
- }
- ~CollectionHolder()
- {
- // only need to act if super type does not handle (owns) anyway
- // only free/clear list, items not owned
- if (std::is_same<Transfer, transfer_none_t>::value && this->own_ &&
- this->data_) {
- this->_steal();
- this->own_ = false;
- }
- }
- };
- #undef GI_DISABLE_METHOD_NONE
- template<typename... Args>
- struct NotifierThunk
- {
- static constexpr bool construct_none = true;
- static void _updated(Container *c, bool create)
- {
- using CT = CollectionHolder<Args...>;
- return static_cast<CT *>(c)->_updated(create);
- }
- };
- struct glib_deleter
- {
- template<typename T>
- void operator()(T *p)
- {
- g_free(p);
- }
- };
- template<typename T>
- using unique_ptr = std::unique_ptr<T, glib_deleter>;
- template<typename ListType, typename ElType, typename Transfer,
- typename Enable = void>
- struct ListAcceptorImpl
- {
- using type = Collection<ListType, ElType, Transfer>;
- };
- template<typename ListType, typename ElType, typename Transfer>
- struct ListAcceptorImpl<ListType, ElType, Transfer,
- typename std::enable_if<
- (std::is_same<ListType, ::GList>::value ||
- std::is_same<ListType, ::GSList>::value) &&
- std::is_same<Transfer, transfer_none_t>::value>::type>
- {
- using type = CollectionHolder<ListType, ElType, Transfer>;
- };
- } // namespace detail
- using detail::unwrap;
- using ZTSpan = detail::Span<detail::SpanType::ZT>;
- using DSpan = detail::Span<detail::SpanType::DYNAMIC>;
- template<int S>
- using FSpan = detail::Span<S>;
- // only the full_transfer version is really safe for general use
- // but let's export it all
- using detail::Collection;
- // type to use in (input) parameter declaration
- // use basic type for ref-cases, otherwise need Holder subtype
- // NOTE add a workaround hack;
- // use full transfer for a none transfer HashTable parameter
- // (as annotation there might be bogus; looking at add gst_uri_set_query_table)
- // (and if the annotiation is not bogus, the full should work out ok-ish)
- // NOTE list_ops is used directly hereto avoid instantiation of Collection
- // (as that might otherwise occur during declaration)
- template<typename ListType, typename T, typename Transfer>
- using CollectionParameter = typename std::conditional<
- std::is_same<ListType, ::GHashTable>::value &&
- std::is_same<Transfer, transfer_none_t>::value,
- detail::Collection<ListType, T, transfer_full_t>,
- typename std::conditional<detail::list_ops<ListType, T, Transfer>::refcnt,
- detail::Collection<ListType, T, Transfer>,
- detail::CollectionHolder<ListType, T, Transfer>>::type>::type;
- namespace traits
- {
- // (many to one) map from Cpp to C type (so no other way around)
- template<typename ListType, typename El, typename Transfer>
- struct ctype<detail::Collection<ListType, El, Transfer>>
- {
- using type =
- typename detail::Collection<ListType, El, Transfer>::_detail::DataType;
- };
- template<typename ListType, typename El, typename Transfer>
- struct ctype<const detail::Collection<ListType, El, Transfer>>
- {
- using type = const typename detail::Collection<ListType, El,
- Transfer>::_detail::DataType;
- };
- } // namespace traits
- } // namespace gi
- #endif // GI_CONTAINER_HPP
|