| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660 |
- #include <array>
- #include <iostream>
- #include <memory>
- #include <unordered_map>
- #include <vector>
- #include "assert.h"
- #define GI_CLASS_IMPL_PRAGMA
- #define GI_TEST 1
- #include "gi.hpp"
- #include "test/test_boxed.h"
- #include "test/test_object.h"
- using namespace gi;
- namespace GLib = gi::repository::GLib;
- namespace GObject_ = gi::repository::GObject;
- #define BOXED_COPY 0
- #if BOXED_COPY
- namespace gi
- {
- namespace repository
- {
- GI_ENABLE_BOXED_COPY(GBExample)
- }
- } // namespace gi
- #endif
- // test examples;
- // GBoxed
- class GBoxedExampleBase
- : public detail::GBoxedWrapperBase<GBoxedExampleBase, GBExample>
- {
- typedef detail::GBoxedWrapperBase<GBoxedExampleBase, GBExample> super_type;
- public:
- static GType get_type_() { return GI_CPP_TYPE_BOXED_EXAMPLE; }
- GBoxedExampleBase(std::nullptr_t = nullptr) : super_type() {}
- };
- class GBoxedExampleRef;
- class GBoxedExample : public detail::GBoxedWrapper<GBoxedExample, GBExample,
- GBoxedExampleBase, GBoxedExampleRef>
- {
- typedef detail::GBoxedWrapper<GBoxedExample, GBExample, GBoxedExampleBase,
- GBoxedExampleRef>
- super_type;
- using super_type::super_type;
- };
- class GBoxedExampleRef : public detail::GBoxedRefWrapper<GBoxedExample,
- GBExample, GBoxedExampleBase>
- {
- typedef detail::GBoxedRefWrapper<GBoxedExample, GBExample, GBoxedExampleBase>
- super_type;
- using super_type::super_type;
- };
- // plain C
- class CBoxedExampleBase
- : public detail::CBoxedWrapperBase<CBoxedExampleBase, CBExample>
- {
- typedef detail::CBoxedWrapperBase<CBoxedExampleBase, CBExample> super_type;
- public:
- CBoxedExampleBase(std::nullptr_t = nullptr) : super_type() {}
- };
- class CBoxedExampleRef;
- class CBoxedExample : public detail::CBoxedWrapper<CBoxedExample, CBExample,
- CBoxedExampleBase, CBoxedExampleRef>
- {
- typedef detail::CBoxedWrapper<CBoxedExample, CBExample, CBoxedExampleBase,
- CBoxedExampleRef>
- super_type;
- using super_type::super_type;
- };
- class CBoxedExampleRef : public detail::CBoxedRefWrapper<CBoxedExample,
- CBExample, CBoxedExampleBase>
- {
- typedef detail::CBoxedRefWrapper<CBoxedExample, CBExample, CBoxedExampleBase>
- super_type;
- using super_type::super_type;
- };
- enum class CppEnum { VALUE_0 = ENUM_VALUE_0, VALUE_1 = ENUM_VALUE_1 };
- enum class CppFlags { VALUE_0 = FLAG_VALUE_0, VALUE_1 = FLAG_VALUE_1 };
- GI_FLAG_OPERATORS(CppFlags)
- namespace gi
- {
- namespace repository
- {
- template<>
- struct declare_cpptype_of<GBExample>
- {
- typedef GBoxedExample type;
- };
- template<>
- struct declare_cpptype_of<CBExample>
- {
- typedef CBoxedExample type;
- };
- template<>
- struct declare_ctype_of<CppEnum>
- {
- typedef CEnum type;
- };
- template<>
- struct declare_cpptype_of<CEnum>
- {
- typedef CppEnum type;
- };
- template<>
- struct declare_gtype_of<CppEnum>
- {
- static GType get_type() { return GI_CPP_TYPE_ENUM; }
- };
- template<>
- struct is_enumeration<CppEnum> : public std::true_type
- {};
- template<>
- struct declare_ctype_of<CppFlags>
- {
- typedef CFlags type;
- };
- template<>
- struct declare_cpptype_of<CFlags>
- {
- typedef CppFlags type;
- };
- template<>
- struct declare_gtype_of<CppFlags>
- {
- static GType get_type() { return GI_CPP_TYPE_FLAGS; }
- };
- template<>
- struct is_bitfield<CppFlags> : public std::true_type
- {};
- } // namespace repository
- } // namespace gi
- #define DECLARE_PROPERTY(pname, ptype) \
- property_proxy<ptype> property_##pname() \
- { \
- return property_proxy<ptype>(*this, #pname); \
- } \
- const property_proxy<ptype> property_##pname() const \
- { \
- return property_proxy<ptype>(*this, #pname); \
- }
- #define DECLARE_SIGNAL(name, sig) \
- signal_proxy<sig> signal_##name() { return signal_proxy<sig>(*this, #name); }
- // simulate override construction
- class Derived;
- typedef GICppExample CDerived;
- namespace base
- {
- class DerivedBase : public GObject_::Object
- {
- typedef Object super_type;
- public:
- typedef Derived self;
- typedef CDerived BaseObjectType;
- DerivedBase(std::nullptr_t = nullptr) : super_type() {}
- BaseObjectType *gobj_() { return (BaseObjectType *)super_type::gobj_(); }
- const BaseObjectType *gobj_() const
- {
- return (const BaseObjectType *)super_type::gobj_();
- }
- BaseObjectType *gobj_copy_() const
- {
- return (BaseObjectType *)super_type::gobj_copy_();
- }
- static GType get_type_() { return GI_CPP_TYPE_EXAMPLE; }
- DECLARE_PROPERTY(number, int)
- DECLARE_PROPERTY(fnumber, double)
- DECLARE_PROPERTY(data, std::string)
- DECLARE_PROPERTY(present, gboolean)
- DECLARE_PROPERTY(object, Object)
- DECLARE_PROPERTY(choice, CppEnum)
- DECLARE_PROPERTY(flags, CppFlags)
- DECLARE_PROPERTY(error, GLib::Error)
- DECLARE_SIGNAL(
- to_int, int(Derived, Object, bool, gboolean, const std::string &))
- DECLARE_SIGNAL(to_string, std::string(Derived, int, gint64))
- DECLARE_SIGNAL(to_void, void(Derived, double, CppEnum, CppFlags))
- DECLARE_SIGNAL(to_output_int,
- void(Derived, unsigned &,
- gi::Collection<::GPtrArray, char *, gi::transfer_none_t>,
- gi::Collection<::GSList, char *, gi::transfer_none_t>))
- };
- } // namespace base
- class Derived : public base::DerivedBase
- {
- typedef base::DerivedBase super;
- using super::super;
- };
- namespace gi
- {
- namespace repository
- {
- template<>
- struct declare_cpptype_of<CDerived>
- {
- typedef Derived type;
- };
- } // namespace repository
- } // namespace gi
- void
- test_trait()
- {
- static_assert(traits::is_basic<int>::value, "");
- static_assert(traits::is_basic<float>::value, "");
- static_assert(traits::is_basic<void *>::value, "");
- static_assert(!traits::is_basic<char *>::value, "");
- static_assert(traits::is_boxed<GBoxedExample>::value, "");
- static_assert(traits::is_object<Derived>::value, "");
- static_assert(!traits::is_object<int>::value, "");
- static_assert(traits::is_reftype<GBoxedExampleRef>::value, "");
- static_assert(!traits::is_reftype<GBoxedExample>::value, "");
- static_assert(!traits::is_reftype<int>::value, "");
- static_assert(traits::has_ctype_member<Derived>::value, "");
- static_assert(!traits::has_ctype_member<int>::value, "");
- static_assert(
- std::is_same<traits::ctype<Derived>::type, CDerived *>::value, "");
- static_assert(
- std::is_same<traits::ctype<const Derived>::type, const CDerived *>::value,
- "");
- static_assert(
- std::is_same<traits::cpptype<CDerived *>::type, Derived>::value, "");
- static_assert(std::is_same<traits::cpptype<const CDerived *>::type,
- const Derived>::value,
- "");
- static_assert(std::is_same<traits::ctype<CppEnum>::type, CEnum>::value, "");
- static_assert(std::is_same<traits::cpptype<CEnum>::type, CppEnum>::value, "");
- static_assert(
- std::is_same<traits::ctype<CBoxedExample>::type, CBExample *>::value, "");
- static_assert(
- std::is_same<traits::cpptype<CBExample>::type, CBoxedExample>::value, "");
- static_assert(std::is_same<traits::reftype<CBoxedExample>::type,
- CBoxedExampleRef>::value,
- "");
- static_assert(
- std::is_same<traits::ctype<GBoxedExample>::type, GBExample *>::value, "");
- static_assert(
- std::is_same<traits::cpptype<GBExample>::type, GBoxedExample>::value, "");
- static_assert(std::is_same<traits::reftype<GBoxedExample>::type,
- GBoxedExampleRef>::value,
- "");
- static_assert(
- std::is_same<traits::cpptype<GBExample *, transfer_none_t>::type,
- GBoxedExampleRef>::value,
- "");
- static_assert(
- std::is_same<traits::ctype<GObject_::Object>::type, _GObject *>::value,
- "");
- static_assert(
- std::is_same<traits::cpptype<_GObject>::type, GObject_::Object>::value,
- "");
- static_assert(std::is_same<traits::cpptype<bool>::type, bool>::value, "");
- static_assert(traits::gtype<int>::value, "");
- static_assert(traits::gtype<std::string>::value, "");
- static_assert(traits::gvalue<int>::value, "");
- static_assert(traits::gvalue<CppEnum>::value, "");
- static_assert(traits::is_enumeration<CppEnum>::value, "");
- static_assert(!traits::is_bitfield<CppEnum>::value, "");
- static_assert(traits::is_bitfield<CppFlags>::value, "");
- static_assert(
- std::is_pointer<GLib::DestroyNotify::cfunction_type>::value, "");
- static_assert(!detail::allocator<int>::value, "");
- static_assert(detail::allocator<GObject_::Value>::value, "");
- static_assert(detail::allocator<GBoxedExample>::value, "");
- static_assert(traits::is_type_complete<GBExample>::value, "");
- struct BlackBox;
- static_assert(!traits::is_type_complete<BlackBox>::value, "");
- static_assert(
- std::is_copy_constructible<GBoxedExample>::value == BOXED_COPY, "");
- static_assert(
- std::is_copy_assignable<GBoxedExample>::value == BOXED_COPY, "");
- static_assert(std::is_move_constructible<GBoxedExample>::value, "");
- static_assert(std::is_move_assignable<GBoxedExample>::value, "");
- static_assert(std::is_copy_constructible<GBoxedExampleRef>::value, "");
- static_assert(std::is_copy_assignable<GBoxedExampleRef>::value, "");
- static_assert(
- std::is_copy_constructible<CBoxedExample>::value == BOXED_COPY, "");
- static_assert(
- std::is_copy_assignable<CBoxedExample>::value == BOXED_COPY, "");
- static_assert(std::is_move_constructible<CBoxedExample>::value, "");
- static_assert(std::is_move_assignable<CBoxedExample>::value, "");
- static_assert(std::is_copy_constructible<CBoxedExampleRef>::value, "");
- static_assert(std::is_copy_assignable<CBoxedExampleRef>::value, "");
- // internal
- static_assert(detail::_traits::is_basic_argument<int>::value, "");
- static_assert(!detail::_traits::is_basic_argument<int &>::value, "");
- static_assert(!detail::_traits::is_basic_argument<std::string>::value, "");
- static_assert(
- detail::_traits::is_basic_argument<char *const *const>::value, "");
- static_assert(
- detail::_traits::is_const_lvalue<const std::string &>::value, "");
- static_assert(!detail::_traits::is_const_lvalue<GBoxedExample &>::value, "");
- }
- // test helper
- template<typename T>
- int
- refcount(T *obj)
- {
- return (((GObject *)(obj))->ref_count);
- }
- void
- test_wrap()
- {
- { // plain boxed
- CBExample *ex = gi_cpp_cbexample_new();
- auto cb = wrap(ex, transfer_full);
- assert(cb.gobj_() == ex);
- auto cb2 = wrap(ex, transfer_none);
- assert(cb2 == cb);
- assert(cb.gobj_() == cb2.gobj_());
- auto cb3 = cb2;
- assert(cb3 == cb2);
- assert(cb2.gobj_() == cb3.gobj_());
- auto cbn = CBoxedExample::new_();
- assert(cbn.gobj_() != nullptr);
- assert(cbn.gobj_()->data == 0);
- // a peek
- auto ex2 = unwrap(cbn, transfer_none);
- assert(ex2 == cbn.gobj_());
- // a fresh copy
- auto ex3 = unwrap(std::move(cbn), transfer_full);
- assert(ex3 != cbn.gobj_());
- gi_cpp_cbexample_free(ex3);
- cbn = CBoxedExample();
- assert(cbn == nullptr);
- assert(cbn.gobj_() == nullptr);
- assert(!cbn);
- auto ex4 = unwrap(std::move(cbn), transfer_full);
- assert(ex4 == nullptr);
- // various basic operations
- CBoxedExample cbm(std::move(cbn));
- cbm = std::move(cbn);
- assert(!cbn);
- std::swap(cbm, cbn);
- assert(!cbm);
- // compile check
- CBoxedExample f = CBoxedExample::new_();
- f = nullptr;
- // ref
- CBoxedExampleRef r(f);
- assert(r.gobj_() == f.gobj_());
- r = nullptr;
- assert(r.gobj_() == nullptr);
- r = f;
- assert(r.gobj_() == f.gobj_());
- // ref wrap
- auto r2 = wrap(ex, transfer_none);
- static_assert(std::is_same<decltype(r2), CBoxedExampleRef>::value, "");
- r = r2;
- assert(r.gobj_() == ex);
- auto ro = unwrap(r, transfer_none);
- auto ri = unwrap(r, transfer_none);
- assert(ri == ro);
- }
- { // similarly so gboxed
- GBExample *exc = gi_cpp_gbexample_new();
- GBoxedExample w1 = wrap(exc, transfer_full);
- GBoxedExample w2 = std::move(w1);
- assert(!w1);
- assert(w2.gobj_() == exc);
- GBoxedExample w3 = w2.copy_();
- assert(w3);
- assert(w3 != w2);
- assert(w3.gobj_() != w2.gobj_());
- w3 = nullptr;
- // ref
- GBoxedExampleRef r(w2);
- assert(r.gobj_() == w2.gobj_());
- r = nullptr;
- assert(r.gobj_() == nullptr);
- r = w2;
- assert(r.gobj_() == w2.gobj_());
- // ref wrap
- r = wrap(exc, transfer_none);
- assert(r.gobj_() == exc);
- auto ro = unwrap(r, transfer_none);
- auto ri = unwrap(r, transfer_none);
- assert(ri == ro);
- auto rc = r.copy_();
- assert(rc != r);
- #if BOXED_COPY
- {
- GBoxedExample ww{w3};
- ww = w3;
- r = w3;
- ww = r;
- ww = std::move(r);
- }
- #endif
- }
- { // string
- const gchar *str{};
- std::string s1 = wrap(str, transfer_none);
- assert(s1.empty());
- str = "TEST";
- std::string s2 = wrap(str, transfer_none);
- assert(s2 == str);
- auto *cs = unwrap(s2, transfer_none);
- assert(g_strcmp0(cs, str) == 0);
- // now something we should be able to free
- cs = unwrap(s2, transfer_full);
- g_free((gpointer)cs);
- // unwrap empty optional
- const std::string &emptytest = "";
- auto cempty = unwrap(emptytest, transfer_none);
- assert(*cempty == 0);
- auto ocempty = unwrap(
- static_cast<const detail::optional_string &>(emptytest), transfer_none);
- assert(ocempty == nullptr);
- }
- { // some more plain cases
- wrap(5);
- unwrap(5, transfer_none);
- const double ex = 5.0;
- auto w = wrap(ex);
- auto ex2 = unwrap(w);
- assert(ex == ex2);
- CEnum ec{};
- CppEnum ecpp{};
- ecpp = wrap(ec);
- unwrap(ecpp, transfer_none);
- }
- { // object
- CDerived *ob = gi_cpp_example_new();
- assert(refcount(ob) == 1);
- auto wo = wrap(ob, transfer_none);
- assert(refcount(ob) == 2);
- assert(wo.gobj_() == ob);
- {
- auto wo2 = wo;
- assert(wo2 == wo);
- assert(wo2.gobj_() == ob);
- assert(refcount(ob) == 3);
- // cast to own type should work fine
- auto wo3 = object_cast<Derived>(wo2);
- assert(wo3);
- assert(refcount(ob) == 4);
- }
- assert(refcount(ob) == 2);
- auto ob2 = unwrap(wo, transfer_none);
- assert(ob2 == ob);
- assert(refcount(ob) == 2);
- ob2 = unwrap(wo, transfer_full);
- assert(ob2 == ob);
- assert(refcount(ob) == 3);
- g_object_unref(ob2);
- ob2 = nullptr;
- assert(refcount(ob) == 2);
- // no change for full transfer
- auto wo2 = wrap(ob, transfer_full);
- assert(wo2.gobj_() == ob);
- assert(refcount(ob) == 2);
- // compensate
- g_object_ref(ob);
- wo2 = Derived();
- assert(wo2.gobj_() == nullptr);
- assert(refcount(ob) == 2);
- wo2 = std::move(wo);
- assert(wo.gobj_() == nullptr);
- assert(wo2.gobj_() == ob);
- wo2 = wo;
- assert(refcount(ob) == 1);
- g_object_unref(ob);
- Derived wo3(wo2);
- if (wo3)
- wo3 = nullptr;
- }
- {
- // object creation
- Derived ob = GObject_::Object::new_<Derived>();
- ob = GObject_::Object::new_<Derived>(
- NAME_NUMBER, (int)5, NAME_PRESENT, true);
- assert(ob.property_number().get() == 5);
- assert(ob.property_present().get() == true);
- Derived ob2 = GObject_::Object::new_<Derived>();
- std::swap(ob2, ob);
- }
- {
- // paramspec
- auto pspec = GObject_::ParamSpec::new_<int>("p", "p", "p", 0, 10);
- // is otherwise similar to property and is tested there further
- }
- { // wrap_to
- int x = 5;
- auto y = wrap_to<int>(x, transfer_none);
- assert(x == y);
- wrap_to<std::string>("x", transfer_none);
- }
- { // unwrap_maybe_float
- CDerived *ob = gi_cpp_example_new();
- assert(refcount(ob) == 1);
- auto wo = wrap(ob, transfer_full);
- assert(refcount(ob) == 1);
- CDerived *uwo = detail::unwrap_maybe_float(std::move(wo), transfer_none);
- assert(uwo == ob);
- assert(!wo);
- assert(refcount(ob) == 1);
- assert(g_object_is_floating(ob));
- wo = wrap(ob, transfer_none);
- assert(refcount(ob) == 1);
- assert(!g_object_is_floating(ob));
- uwo = detail::unwrap_maybe_float(std::move(wo), transfer_full);
- assert(!wo);
- assert(uwo == ob);
- assert(refcount(ob) == 1);
- wo = wrap(ob, transfer_full);
- assert(refcount(ob) == 1);
- // wrapper will clean up
- GBExample *exc = gi_cpp_gbexample_new();
- GBoxedExample w1 = wrap(exc, transfer_full);
- GBExample *uw1 = detail::unwrap_maybe_float(std::move(w1), transfer_full);
- assert(!w1);
- assert(uw1 == exc);
- w1 = wrap(exc, transfer_full);
- // wrapper will clean up
- }
- { // compile checks on cs_ptr helper
- using Object = gi::repository::GObject::Object;
- auto g = [](Object) {};
- auto f = [&g](const gi::cs_ptr<Object> &ob) {
- ob->handler_block(0);
- Object obx = ob;
- auto y = ob;
- auto x = [y]() { y->handler_block(1); };
- g(ob);
- };
- Object ob{};
- if (false)
- f(ob);
- }
- }
- using gi::detail::Collection;
- using gi::detail::CollectionHolder;
- namespace collection_helpers
- {
- struct LVHandler
- {
- template<typename T>
- T &f(T &t)
- {
- return t;
- }
- };
- struct RVHandler
- {
- template<typename T>
- T &&f(T &t)
- {
- return std::move(t);
- }
- };
- template<typename ListType, typename T, typename Transfer>
- using CollectionProxy =
- Collection<ListType, typename traits::ctype<T>::type, Transfer>;
- template<typename ListType, typename T, typename Transfer>
- using CollectionHolderProxy =
- CollectionHolder<ListType, typename traits::ctype<T>::type, Transfer>;
- std::string
- get_value(const std::string &x)
- {
- return x;
- }
- std::string
- get_value(const gi::cstring &x)
- {
- return x;
- }
- template<typename T>
- auto
- get_value(const T &x) -> decltype(unwrap(x, transfer_none))
- {
- return unwrap(const_cast<T &>(x), transfer_none);
- }
- template<template<typename, typename, typename> class CT = CollectionProxy,
- typename CppType, typename Transfer, typename LVRHandler>
- void
- check_collection(std::vector<CppType> &&v, const Transfer &t, LVRHandler h)
- {
- // collect size
- const auto VS = v.size();
- // and (internal) value for later check
- using VT = decltype(get_value(v.front()));
- std::vector<VT> baseline;
- auto get_values = [&v] {
- decltype(baseline) out;
- for (auto &val : v)
- out.push_back(get_value(val));
- return out;
- };
- baseline = get_values();
- auto do_check = [&get_values, &baseline]() {
- auto current = get_values();
- assert(current == baseline);
- };
- auto check_list_it = [&baseline](auto &list) {
- decltype(baseline) current;
- int cnt = 0;
- for (auto &e : list) {
- // e should be const & iff Transfer none
- using el_type = typename std::remove_reference<decltype(e)>::type;
- static_assert(std::is_const<el_type>::value ==
- std::is_same<Transfer, transfer_none_t>::value,
- "");
- assert(e);
- ++cnt;
- current.push_back(get_value(e));
- }
- assert(cnt == int(baseline.size()));
- assert(current == baseline);
- cnt = 0;
- current.clear();
- const auto &clist = list;
- for (auto &e : clist) {
- // e should always be const & here
- using el_type = typename std::remove_reference<decltype(e)>::type;
- static_assert(std::is_const<el_type>::value, "");
- assert(e);
- ++cnt;
- current.push_back(get_value(e));
- }
- assert(cnt == int(baseline.size()));
- assert(current == baseline);
- // assign from non-const to const
- auto cb = clist.begin();
- cb = list.begin();
- assert(!cnt || *cb);
- };
- // various trips through variations
- CT<::GList, CppType, Transfer> l{h.f(v)};
- static constexpr bool is_sub = std::is_same<decltype(l),
- CollectionHolderProxy<::GList, CppType, Transfer>>::value;
- static_assert(is_sub || (sizeof(l) == sizeof(::GList *)), "");
- check_list_it(l);
- v = std::move(l);
- assert(!l);
- assert(v.size() == VS);
- do_check();
- l = h.f(v);
- v = std::move(l);
- assert(!l);
- assert(v.size() == VS);
- do_check();
- CT<::GSList, CppType, Transfer> sl{h.f(v)};
- static_assert(is_sub || (sizeof(l) == sizeof(::GSList *)), "");
- check_list_it(sl);
- v = std::move(sl);
- assert(!sl);
- assert(v.size() == VS);
- do_check();
- sl = h.f(v);
- v = std::move(sl);
- assert(!sl);
- assert(v.size() == VS);
- do_check();
- CT<::GPtrArray, CppType, Transfer> pa{h.f(v)};
- static_assert(is_sub || (sizeof(l) == sizeof(::GPtrArray *)), "");
- check_list_it(pa);
- v = std::move(pa);
- assert(!pa);
- assert(v.size() == VS);
- do_check();
- pa = h.f(v);
- v = std::move(pa);
- assert(!pa);
- assert(v.size() == VS);
- do_check();
- CT<DSpan, CppType, Transfer> da{h.f(v)};
- check_list_it(da);
- v = std::move(da);
- assert(!da);
- assert(v.size() == VS);
- do_check();
- da = h.f(v);
- v = std::move(da);
- assert(!da);
- assert(v.size() == VS);
- do_check();
- CT<ZTSpan, CppType, Transfer> za{h.f(v)};
- check_list_it(za);
- v = std::move(za);
- assert(!za);
- za = h.f(v);
- v = std::move(za);
- assert(!za);
- assert(v.size() == VS);
- do_check();
- l = h.f(v);
- // temporarily use a separate sv
- // (so v can keep its elements alive, e.g. std::string case)
- std::vector<CppType> sv;
- sv.resize(VS);
- std::move(l).move_to(sv.data());
- assert(!l);
- v = std::move(sv);
- sv.clear();
- do_check();
- // in case of holder; l has ownership here (so none wrap/unwrap below ok)
- l = h.f(v);
- auto lu = unwrap(std::move(l), t);
- assert(!l || !t.value);
- // last part; always need to wrap to a basic wrapper
- using WrapTypeL = CollectionProxy<::GList, CppType, Transfer>;
- auto ln = wrap_to<WrapTypeL>(lu, t);
- assert(lu == ln.gobj_());
- v = std::move(ln);
- do_check();
- pa = h.f(v);
- auto pau = unwrap(std::move(pa), t);
- assert(!pa || !t.value);
- // ref'ed case, so it can always take ownership
- using WrapTypeA = CollectionProxy<::GPtrArray, CppType, Transfer>;
- auto pan = wrap_to<WrapTypeA>(pau, t);
- assert(pau == pan.gobj_());
- }
- template<template<typename, typename, typename> class CT = CollectionProxy,
- typename CppKey, typename CppType, typename Transfer, typename LVRHandler>
- void
- check_collection(std::map<CppKey, CppType> &&v, const Transfer &t, LVRHandler h)
- {
- const auto VS = v.size();
- using VT = decltype(get_value(v.begin()->second));
- std::map<CppKey, VT> baseline;
- auto get_values = [](auto &c) {
- decltype(baseline) out;
- for (auto &val : c)
- out[val.first] = get_value(val.second);
- return out;
- };
- baseline = get_values(v);
- auto do_check = [&get_values, &baseline](auto &c) {
- auto current = get_values(c);
- assert(current == baseline);
- };
- auto check_map_it = [&baseline](auto &list) {
- decltype(baseline) current;
- int cnt = 0;
- for (auto &e : list) {
- assert(e.first);
- assert(e.second);
- ++cnt;
- current[get_value(e.first)] = get_value(e.second);
- }
- assert(cnt == int(baseline.size()));
- assert(current == baseline);
- cnt = 0;
- current.clear();
- const auto &clist = list;
- for (auto &e : clist) {
- assert(e.first);
- assert(e.second);
- ++cnt;
- current[get_value(e.first)] = get_value(e.second);
- }
- assert(cnt == int(baseline.size()));
- assert(current == baseline);
- };
- CT<::GHashTable, std::pair<CppKey, CppType>, Transfer> l{h.f(v)};
- static constexpr bool is_sub = std::is_same<decltype(l),
- CollectionHolderProxy<::GHashTable, std::pair<CppKey, CppType>,
- Transfer>>::value;
- static_assert(is_sub || (sizeof(l) == sizeof(::GHashTable *)), "");
- check_map_it(l);
- v = std::move(l);
- assert(!l);
- assert(v.size() == VS);
- do_check(v);
- l = h.f(v);
- v = std::move(l);
- assert(!l);
- assert(v.size() == VS);
- do_check(v);
- l = h.f(v);
- std::unordered_map<std::string, CppType> w;
- w = std::move(l);
- assert(!l);
- assert(w.size() == VS);
- do_check(w);
- // in case of holder; l has ownership here (so none wrap/unwrap below ok)
- l = h.f(w);
- auto lu = unwrap(std::move(l), t);
- assert(!l || !t.value);
- // last part; always need to wrap to a basic wrapper
- using WrapType =
- CollectionProxy<::GHashTable, std::pair<CppKey, CppType>, Transfer>;
- auto ln = wrap_to<WrapType>(lu, t);
- assert(lu == ln.gobj_());
- }
- template<typename ListType, typename Transfer>
- static void
- check_collection_null()
- { // check nullptr cases
- // type not relevant in these cases
- using BT = CDerived *;
- std::vector<Derived> bv;
- Transfer t;
- {
- auto ln =
- wrap_to<Collection<ListType, BT, Transfer>>((ListType *)(nullptr), t);
- assert(!ln);
- ln = nullptr;
- assert(!ln);
- bv = std::move(ln);
- assert(bv.empty());
- }
- }
- } // namespace collection_helpers
- void
- test_collection()
- {
- using namespace collection_helpers;
- auto make_object = [] { return wrap(gi_cpp_example_new(), transfer_full); };
- auto make_box = []() -> GBoxedExample {
- return wrap(gi_cpp_gbexample_new(), transfer_full);
- };
- { // init list construction
- CollectionProxy<::GPtrArray, Derived, transfer_full_t> coll_pa{
- make_object(), make_object()};
- assert(coll_pa.size() == 2);
- CollectionProxy<gi::DSpan, int, transfer_full_t> coll_da{2, 3, 4};
- assert(coll_da.size() == 3);
- #if 0
- // will not work, since not copyable
- CollectionProxy<gi::ZTSpan, GBoxedExample, transfer_full_t> coll_za{
- make_box()};
- assert(coll_za.size() == 1);
- // but neither will the following
- std::vector<std::unique_ptr<int>> x{std::make_unique<int>(5)};
- #endif
- }
- // full
- { // object
- std::vector<Derived> v{make_object(), make_object()};
- check_collection(std::move(v), transfer_full, LVHandler());
- // map
- std::map<std::string, Derived> m{
- {"x", make_object()}, {"y", make_object()}};
- check_collection(std::move(m), transfer_full, LVHandler());
- }
- { // non-copyable boxed
- std::vector<GBoxedExample> v;
- v.push_back(make_box());
- v.push_back(make_box());
- check_collection(std::move(v), transfer_full, RVHandler());
- // map
- std::map<std::string, GBoxedExample> m;
- m["x"] = make_box();
- m["y"] = make_box();
- check_collection(std::move(m), transfer_full, RVHandler());
- }
- { // string
- std::vector<std::string> v{"foo", "bar"};
- check_collection(std::move(v), transfer_full, LVHandler());
- // map
- std::map<std::string, std::string> m{{"x", "foo"}, {"y", "bar"}};
- check_collection(std::move(m), transfer_full, LVHandler());
- }
- { // cstring
- std::vector<gi::cstring> v{"foo", "bar"};
- check_collection(std::move(v), transfer_full, LVHandler());
- // map
- std::map<gi::cstring, gi::cstring> m{{"x", "foo"}, {"y", "bar"}};
- check_collection(std::move(m), transfer_full, LVHandler());
- }
- { // plain
- std::vector<int> v{2, 3};
- auto w = v;
- Collection<gi::DSpan, int, transfer_full_t> da{v.data(), v.size()};
- v = std::move(da);
- assert(!da);
- assert(v == w);
- da = v;
- std::move(da).move_to(v.data());
- assert(v == w);
- }
- // container
- { // object
- std::vector<Derived> v{make_object(), make_object()};
- // hold item ownership as they are moved about
- auto t = v;
- check_collection(std::move(v), transfer_container, LVHandler());
- // map
- std::map<std::string, Derived> m{
- {"x", make_object()}, {"y", make_object()}};
- auto tm = m;
- check_collection(std::move(m), transfer_container, LVHandler());
- }
- { // copyable ref boxed
- std::vector<GBoxedExample> v;
- v.push_back(make_box());
- v.push_back(make_box());
- std::vector<GBoxedExampleRef> w{v.begin(), v.end()};
- check_collection(std::move(w), transfer_container, LVHandler());
- // map
- std::map<std::string, GBoxedExampleRef> m{
- {"x", v.front()}, {"y", v.back()}};
- check_collection(std::move(m), transfer_container, LVHandler());
- }
- { // string
- std::vector<std::string> v{"foo", "bar"};
- check_collection(std::move(v), transfer_container, LVHandler());
- // map
- std::map<std::string, std::string> m{{"x", "foo"}, {"y", "bar"}};
- check_collection(std::move(m), transfer_container, LVHandler());
- }
- { // cstring
- std::vector<gi::cstring> v{"foo", "bar"};
- std::vector<gi::cstring_v> w{v.begin(), v.end()};
- check_collection(std::move(w), transfer_container, LVHandler());
- // map
- std::map<gi::cstring, gi::cstring_v> m{{"x", v.front()}, {"y", v.back()}};
- check_collection(std::move(m), transfer_container, LVHandler());
- }
- { // plain
- std::vector<CppEnum> v{CppEnum::VALUE_0, CppEnum::VALUE_1};
- auto w = v;
- CollectionProxy<gi::DSpan, CppEnum, transfer_container_t> da{
- v.data(), v.size()};
- v = std::move(da);
- assert(!da);
- assert(v == w);
- da = v;
- std::move(da).move_to(v.data());
- assert(v == w);
- }
- // none
- {
- static_assert(!std::is_copy_constructible<
- CollectionHolder<::GList, CDerived *>>::value,
- "");
- static_assert(std::is_move_constructible<
- CollectionHolder<::GList, CDerived *>>::value,
- "");
- }
- // parameter helper should behave much as a dynamic container transfer
- { // object
- std::vector<Derived> v{make_object(), make_object()};
- // hold item ownership as they are moved about
- auto t = v;
- check_collection<CollectionHolderProxy>(
- std::move(v), transfer_none, LVHandler());
- // map
- std::map<std::string, Derived> m{
- {"x", make_object()}, {"y", make_object()}};
- auto tm = m;
- check_collection<CollectionHolderProxy>(
- std::move(m), transfer_none, LVHandler());
- }
- { // copyable ref boxed
- std::vector<GBoxedExample> v;
- v.push_back(make_box());
- v.push_back(make_box());
- std::vector<GBoxedExampleRef> w{v.begin(), v.end()};
- check_collection<CollectionHolderProxy>(
- std::move(w), transfer_none, LVHandler());
- // map
- std::map<std::string, GBoxedExampleRef> m{
- {"x", v.front()}, {"y", v.back()}};
- check_collection<CollectionHolderProxy>(
- std::move(m), transfer_none, LVHandler());
- }
- { // string
- std::vector<std::string> v{"foo", "bar"};
- check_collection<CollectionHolderProxy>(
- std::move(v), transfer_none, LVHandler());
- // map
- std::map<std::string, std::string> m{{"x", "foo"}, {"y", "bar"}};
- check_collection<CollectionHolderProxy>(
- std::move(m), transfer_none, LVHandler());
- }
- { // cstring
- std::vector<gi::cstring> v{"foo", "bar"};
- std::vector<gi::cstring_v> w{v.begin(), v.end()};
- check_collection<CollectionHolderProxy>(
- std::move(w), transfer_none, LVHandler());
- // map
- std::map<gi::cstring, gi::cstring_v> m{{"x", v.front()}, {"y", v.back()}};
- check_collection<CollectionHolderProxy>(
- std::move(m), transfer_none, LVHandler());
- }
- // some other array cases
- {
- std::array<const char *, 2> sa{{"x", "y"}};
- Collection<gi::DSpan, char *, transfer_none_t> sc;
- sc = wrap_to<decltype(sc)>(sa.data(), sa.size(), transfer_none);
- assert(sc.gobj_() == sa.data());
- auto usa = unwrap(sc, transfer_none);
- assert(usa == sa.data());
- }
- {
- std::array<int, 2> a{3, 4};
- CollectionHolderProxy<gi::DSpan, int, transfer_none_t> ac{
- a.data(), a.size()};
- // optimization applies
- assert(ac.gobj_() == a.data());
- assert(ac._size() == a.size());
- auto rc = ac.gobj_();
- auto uac = unwrap(std::move(ac), transfer_none);
- assert(rc == uac);
- for (int i : {0, 1})
- assert(uac[i] == a[i]);
- ac = wrap_to<decltype(ac)>(uac, a.size(), transfer_none);
- assert(ac.gobj_() == uac);
- }
- {
- std::array<int, 2> a{5, 6};
- // allocate new array
- Collection<gi::DSpan, int, transfer_full_t> ac{a.data(), a.size()};
- assert(ac.gobj_() != a.data());
- assert(ac._size() == a.size());
- auto rc = ac.gobj_();
- auto uac = unwrap(std::move(ac), transfer_full);
- // now owned by uac above
- assert(!ac);
- assert(rc == uac);
- for (int i : {0, 1})
- assert(uac[i] == a[i]);
- // transfer full to ac again
- ac = wrap_to<decltype(ac)>(uac, a.size(), transfer_full);
- assert(ac.gobj_() == uac);
- }
- { // check GHashTable transfer_none hack
- using VT = std::pair<char *, char *>;
- using TT = gi::CollectionParameter<::GHashTable, VT, transfer_none_t>;
- static_assert(
- std::is_same<TT,
- gi::detail::Collection<::GHashTable, VT, transfer_full_t>>::value,
- "");
- // should work for refcnt case
- TT t;
- unwrap(t, transfer_none);
- // also check null handling
- assert(!t);
- t = nullptr;
- assert(!t);
- std::map<std::string, std::string> m = std::move(t);
- assert(m.empty());
- }
- { // nullptr handling
- check_collection_null<::GList, transfer_full_t>();
- check_collection_null<::GList, transfer_none_t>();
- check_collection_null<::GSList, transfer_full_t>();
- check_collection_null<::GSList, transfer_none_t>();
- check_collection_null<::GPtrArray, transfer_full_t>();
- check_collection_null<::GPtrArray, transfer_none_t>();
- // array cases
- using BT = int;
- std::vector<int> bv;
- {
- auto ln = wrap_to<Collection<ZTSpan, BT, transfer_full_t>>(
- (BT *)(nullptr), transfer_full);
- assert(!ln);
- ln = nullptr;
- assert(!ln);
- bv = std::move(ln);
- assert(bv.empty());
- }
- {
- auto ln = wrap_to<Collection<DSpan, BT, transfer_none_t>>(
- (BT *)(nullptr), transfer_none);
- assert(!ln);
- ln = nullptr;
- assert(!ln);
- bv = std::move(ln);
- assert(bv.empty());
- }
- }
- { // transition to other transfer types
- auto check_equal = [](auto &l1, auto &l2) {
- assert(!l1.empty());
- assert(!l2.empty());
- assert(l1.front());
- assert(l2.front());
- auto it1 = l1.begin();
- auto it2 = l2.begin();
- for (; it1 != l1.end() && it2 != l2.end(); ++it1, ++it2) {
- assert(*it1 == *it2);
- }
- };
- CollectionProxy<::GList, GBoxedExample, transfer_full_t> lf;
- lf.push_back(make_box());
- lf.push_back(make_box());
- assert(!lf.empty());
- assert(++lf.begin() != lf.end());
- assert(++(++lf.begin()) == lf.end());
- // generic container creation
- CollectionProxy<::GList, GBoxedExample, transfer_container_t> lc{lf};
- check_equal(lf, lc);
- // special case to none
- CollectionProxy<::GList, GBoxedExample, transfer_none_t> ln{lf};
- check_equal(lf, ln);
- // also this way
- lc = lf;
- check_equal(lf, lc);
- ln = lc;
- check_equal(ln, lc);
- }
- auto uw = [](const auto &obj) { return unwrap(obj, transfer_none); };
- auto check_list = [&uw](auto &lf, auto factory) {
- lf.push_back(factory());
- assert(!lf.empty());
- auto p = uw(lf.front());
- lf.push_back(factory());
- assert(uw(lf.front()) == p);
- assert(std::next(lf.begin()) != lf.end());
- assert(std::next(lf.begin(), 2) == lf.end());
- lf.push_front(factory());
- assert(uw(lf.front()) != p);
- lf.pop_front();
- assert(uw(lf.front()) == p);
- lf.pop_back();
- assert(uw(lf.front()) == p);
- lf.pop_back();
- assert(lf.empty());
- lf.push_front(factory());
- assert(!lf.empty());
- lf.pop_front();
- assert(lf.empty());
- lf.push_front(factory());
- assert(!lf.empty());
- lf.clear();
- assert(lf.empty());
- auto it = lf.insert(lf.begin(), factory());
- p = uw(lf.front());
- assert(it == lf.begin());
- it = lf.insert(lf.begin(), factory());
- assert(it == lf.begin());
- assert(uw(lf.front()) != p);
- lf.insert(lf.end(), factory());
- assert(uw(*std::next(lf.begin())) == p);
- it = std::next(lf.begin());
- while (it != lf.end())
- it = lf.erase(it);
- assert(std::next(lf.begin()) == lf.end());
- it = lf.erase(lf.begin());
- assert(it == lf.end());
- assert(lf.empty());
- lf.push_front(factory());
- typename std::remove_reference<decltype(lf)>::type lfo;
- lf.swap(lfo);
- assert(lf.empty());
- assert(!lfo.empty());
- lfo.pop_front();
- assert(lfo.empty());
- };
- auto check_array = [&uw](auto &l, auto factory) {
- l.clear();
- l.push_back(factory());
- auto p = uw(l.front());
- assert(p);
- assert(l.data()[0] == l[0]);
- assert(l.data()[0] == l.at(0));
- assert(l.data()[0] == l.front());
- assert(l.at(0));
- l.resize(10);
- assert(l.size() == 10);
- assert(!l.back());
- l.resize(1);
- assert(l.size() == 1);
- assert(l.back() == l.front());
- assert(uw(l.back()) == p);
- };
- { // check common operations on lists
- CollectionProxy<::GList, GBoxedExample, transfer_full_t> lf;
- check_list(lf, make_box);
- // special list part
- lf.clear();
- lf.push_back(make_box());
- lf.push_back(make_box());
- auto p = lf.front().gobj_();
- lf.reverse();
- assert(lf.front().gobj_() != p);
- lf.reverse();
- assert(lf.front().gobj_() == p);
- // ptrarray
- CollectionProxy<::GPtrArray, GBoxedExample, transfer_full_t> pa;
- check_list(pa, make_box);
- check_array(pa, make_box);
- // dyn array
- CollectionProxy<gi::DSpan, GBoxedExample, transfer_full_t> da;
- check_list(da, make_box);
- check_array(da, make_box);
- // zt array
- CollectionProxy<gi::ZTSpan, GBoxedExample, transfer_full_t> za;
- check_list(za, make_box);
- check_array(za, make_box);
- // check ZT
- za.clear();
- za.push_back(make_box());
- za.push_back(make_box());
- assert(*za.end() == nullptr);
- p = za.front().gobj_();
- // auto d = za.data();
- auto cap = za.capacity();
- za.reserve(5 * cap);
- assert(za.capacity() >= 5 * cap);
- // NOTE new alloc might match old alloc
- // assert(d != za.data());
- assert(za.front().gobj_() == p);
- // plain array
- CollectionProxy<gi::DSpan, int, transfer_full_t> pda;
- int gen = 1;
- auto make_int = [&gen]() { return ++gen; };
- check_list(pda, make_int);
- check_array(pda, make_int);
- }
- { // map operations
- Collection<::GHashTable, std::pair<char *, GBExample *>, transfer_full_t>
- mf;
- assert(mf.empty());
- const char *KEY1 = "key1";
- const char *KEY2 = "key2";
- auto v1 = make_box();
- auto v2 = make_box();
- auto p1 = uw(v1);
- auto p2 = uw(v2);
- auto ret = mf.replace(KEY1, std::move(v1));
- assert(ret);
- assert(mf.size() == 1);
- ret = mf.replace(KEY2, std::move(v2));
- assert(ret);
- assert(mf.size() == 2);
- assert(std::next(std::next(mf.begin())) == mf.end());
- assert(mf.find("blah") == mf.end());
- assert(!mf.lookup("blah"));
- auto it = mf.find(KEY1);
- assert(it != mf.end());
- assert(uw(it->second) == p1);
- it = mf.find(KEY2);
- assert(it != mf.end());
- assert(uw(it->second) == p2);
- // transfer to variant container
- // generic to container
- Collection<::GHashTable, std::pair<char *, GBExample *>,
- transfer_container_t>
- mc{mf};
- assert(mc.size() == mf.size());
- mc.clear();
- assert(mc.empty());
- mc = mf;
- assert(mc.size() == mf.size());
- assert(uw(mc.lookup(KEY2)) == p2);
- // always down to none
- Collection<::GHashTable, std::pair<char *, GBExample *>, transfer_none_t>
- mn{mf};
- assert(mn.size() == mf.size());
- assert(uw(mn.lookup(KEY2)) == p2);
- mn = mc;
- assert(mn.size() == mf.size());
- assert(uw(mn.lookup(KEY1)) == p1);
- }
- }
- namespace string_helpers
- {
- struct MyView
- {
- const char *c_str() const { return nullptr; }
- size_t size() const { return 0; }
- };
- struct MyViewCustom
- {};
- using cstr = detail::cstring;
- using cstrv = detail::cstring_v;
- template<typename StringType>
- void
- check_string()
- {
- constexpr bool is_view = std::is_same<cstrv, StringType>::value;
- // construct
- const char *STR = "ab";
- StringType cs(STR);
- assert((cs.data() == STR) == is_view);
- std::string s("cd");
- StringType tcs(s);
- cs = s;
- assert((cs.data() == s.data()) == is_view);
- cstrv csv(cs);
- assert(csv.data() == cs.data());
- tcs = std::move(cs);
- assert(tcs.data() == csv.data());
- cs = std::move(tcs);
- assert(cs.data() == csv.data());
- // ops
- for (auto &c : cs)
- assert(c);
- assert(cs.size() == s.size());
- assert(cs.length() == cs.size());
- assert(!cs.empty());
- assert(cs.at(0) == s.at(0));
- assert(cs[0] == s[0]);
- assert(cs.front() == s.front());
- assert(cs.compare(cs) == 0);
- assert(cs.find(s) == 0);
- assert(cs.find(s, 1) == cs.npos);
- assert(cs.rfind(s) == 0);
- assert(cs.rfind(s, 1) == cs.npos);
- // assign/construct to string
- std::string ns{cs};
- assert(ns == s);
- ns = cs;
- assert(ns == s);
- assert(ns == cs);
- assert(cs == cs);
- ns = "xy";
- assert(cs <= ns);
- assert(cs < ns);
- assert(ns > cs);
- assert(ns >= cs);
- assert(ns != cs);
- // construct using convert
- {
- MyViewCustom cv;
- StringType cs(cv);
- assert(!cs);
- }
- cs.swap(tcs);
- assert(tcs);
- cs = nullptr;
- assert(!cs);
- assert(!s.empty());
- {
- std::map<StringType, int> m;
- cs = s;
- m[cs] = 5;
- assert(m[cs] == 5);
- }
- {
- std::unordered_map<StringType, int> m;
- cs = s;
- m[cs] = 5;
- assert(m[cs] == 5);
- }
- #if __cplusplus >= 201703L
- // C++17 specifics
- cs = std::nullopt;
- std::optional<std::string> os;
- cs = os;
- assert(!cs);
- os = s;
- cs = os;
- assert(cs == s);
- os = std::nullopt;
- os = cs;
- assert(os && os.value() == s);
- cs = nullptr;
- assert(!cs);
- os = cs.opt_();
- assert(!os);
- cs = STR;
- assert(cs.find_first_of("b") == 1);
- assert(cs.find_first_of('b') == 1);
- #endif
- }
- } // namespace string_helpers
- namespace gi
- {
- namespace convert
- {
- template<typename Transfer>
- struct converter<string_helpers::MyViewCustom, detail::cstr<Transfer>>
- : public converter_base<string_helpers::MyViewCustom,
- detail::cstr<Transfer>>
- {
- static detail::cstr<Transfer> convert(const string_helpers::MyViewCustom &v)
- {
- (void)v;
- return nullptr;
- }
- };
- } // namespace convert
- } // namespace gi
- void
- test_string()
- {
- using namespace string_helpers;
- { // generic
- check_string<cstr>();
- check_string<cstrv>();
- }
- std::string s("xy");
- { // string only
- cstr cs(s, 1, 1);
- {
- MyView cv;
- cstr cs(cv);
- assert(!cs);
- }
- {
- auto *s = (char *)g_malloc0(5);
- cstr cs(s, transfer_full);
- assert(cs.data() == s);
- }
- // ops
- cs = s;
- cs = cs + s;
- assert(cs == s + s);
- assert(cs.size() == 4);
- cs = s;
- cs = s + cs;
- assert(cs == s + s);
- cs.pop_back();
- assert(cs.size() == 3);
- cs.push_back(s.back());
- assert(cs.size() == 4);
- assert(cs == s + s);
- cs = cs.substr(0, 2);
- assert(cs == s);
- cs += s;
- assert(cs == s + s);
- cs.assign(2, 'x');
- assert(cs == "xx");
- cs = 'y' + cs;
- assert(cs == "yxx");
- cs = cs + 'y';
- assert(cs == "yxxy");
- cs.clear();
- assert(!cs);
- cs = s;
- auto sp = unwrap(std::move(cs), transfer_full);
- assert(s == sp);
- assert(sp);
- assert(!cs);
- cs = cstr{sp, transfer_full};
- assert(cs.data() == sp);
- sp = unwrap(std::move(cs), transfer_full);
- assert(!cs);
- cs = wrap(sp, transfer_full);
- assert(cs.data() == sp);
- // C++17 string view
- #if __cplusplus >= 201703L
- std::string_view sv(s);
- cs = sv;
- assert(cs.data() != sv.data());
- assert(sv == cs);
- assert(cs == sv);
- sv = "zz";
- assert(cs <= sv);
- assert(cs < sv);
- assert(sv > cs);
- assert(sv >= cs);
- assert(sv != cs);
- #endif
- }
- { // view only
- cstrv cv(s);
- assert(cv.data() == s.data());
- cv.remove_prefix(1);
- assert(cv.size() == s.size() - 1);
- assert(cv == s.substr(1));
- const cstrv ccv(s);
- std::string ns(ccv);
- }
- { // combination
- cstr cs("blah");
- cstrv cv(cs);
- assert(cv.data() == cs.data());
- cv = cs;
- assert(cv.data() == cs.data());
- cs = cv;
- assert(cs.data() != cv.data());
- }
- }
- void
- test_exception()
- {
- static_assert(traits::is_gboxed<GLib::Error>::value, "");
- GQuark domain = g_quark_from_string("test-domain");
- const char *msg = "exception_test";
- const int code = 42;
- GError *err = g_error_new_literal(domain, code, msg);
- auto w = wrap(err, transfer_full);
- assert(w.matches(domain, code));
- auto what = w.what();
- assert(strstr(what, msg) != NULL);
- auto wr = wrap(err, transfer_none);
- assert(wr.matches(domain, code));
- GLib::Error e{std::move(w)};
- assert(!w);
- assert(e.matches(domain, code));
- check_error(nullptr);
- err = g_error_new_literal(domain, code, msg);
- detail::make_unexpected(err);
- bool value = true;
- auto r = make_result<bool>(value, nullptr);
- static_assert(detail::is_result<decltype(r)>::value, "");
- static_assert(!detail::is_result<bool>::value, "");
- // make sure we have bool result
- auto s = expect(std::move(r));
- static_assert(std::is_same<bool, decltype(s)>::value, "");
- auto &e2 = expect(e);
- assert(&e2 == &e);
- }
- void
- test_enumflag()
- {
- const char *name = "EnumValue1";
- const char *nick = "v1";
- auto v = CppEnum::VALUE_1;
- auto w = value_info(v);
- auto w1 = EnumValue<CppEnum>::get_by_name(name);
- auto w2 = EnumValue<CppEnum>::get_by_nick(nick);
- assert(w == w1);
- assert(w1 == w2);
- assert(w1.value_name() == name);
- assert(w1.value_nick() == nick);
- // convenient operations
- auto f = CppFlags::VALUE_0 | CppFlags::VALUE_1;
- f = f & CppFlags::VALUE_1;
- f = ~f;
- f ^= CppFlags::VALUE_0;
- }
- typedef int(CCallback)(int, float);
- typedef detail::callback<int(int, float), transfer_none_t,
- std::tuple<transfer_none_t, transfer_none_t>>
- CppCallback;
- void
- test_callback()
- {
- { // argument selection helper
- auto f = [](int a, int b) { return a - b; };
- using Y = detail::args_index<1, 0>;
- auto rr = detail::apply_with_args<Y>(f, 7, 3, 5);
- assert(rr == -4);
- }
- {
- const char *t = "";
- auto f = [](const char *s) -> decltype(auto) { return *s; };
- static_assert(std::is_reference<decltype(f(t))>::value, "");
- using Y = detail::args_index<0>;
- auto &rr = detail::apply_with_args<Y>(f, t);
- assert(&rr == t);
- }
- {
- using T = typename detail::arg_traits<transfer_none_t>::transfer_type;
- static_assert(std::is_same<transfer_none_t, T>::value, "");
- static_assert(detail::is_simple_cb<std::tuple<transfer_none_t>>::value, "");
- using TC = std::tuple<
- detail::arg_info<transfer_full_t, false, void, detail::args_index<0>>,
- detail::arg_info<transfer_full_t, false, void, detail::args_index<1>>>;
- static_assert(!detail::is_simple_cb<TC>::value, "");
- }
- { // exception helpers
- detail::report_exception<true>(std::runtime_error("fail"), 5, nullptr);
- detail::report_exception(
- std::runtime_error("fail"), 5, (::GError **)(nullptr));
- GError *error = nullptr;
- detail::report_exception(std::runtime_error("fail"), 5, &error);
- assert(error);
- g_error_free(error);
- error = nullptr;
- }
- int calls = 0;
- auto l = [&](int a, float b) {
- ++calls;
- return a * b;
- };
- detail::transform_callback_wrapper<int(int, float)>::with_transfer<false,
- transfer_full_t, transfer_none_t, transfer_none_t>
- x{l};
- x.wrapper(1, 2, &x);
- assert(calls == 1);
- x.take_data(std::make_shared<int>(0));
- auto m = [&](int a, CBoxedExample /*b*/, int &x, int *y) {
- x = 1;
- *y = 2;
- ++calls;
- return a;
- };
- detail::transform_callback_wrapper<void(
- int, CBoxedExample, int &, int *)>::with_transfer<false, transfer_full_t,
- transfer_full_t, transfer_full_t, transfer_none_t, transfer_none_t>
- y{m};
- int p = 0, q = 0;
- y.wrapper(1, gi_cpp_cbexample_new(), &p, &q, &y);
- assert(calls == 2);
- assert(p == 1);
- assert(q == 2);
- auto w = wrap(gi_cpp_example_new(), transfer_full);
- auto w2 = wrap(gi_cpp_example_new(), transfer_full);
- char str[] = "blah";
- auto n = [&](GBoxedExample /*b*/, Derived &ob, CppEnum *e, gi::cstring_v &s,
- gpointer &vb, GLib::Error * /*error*/) {
- ++calls;
- if (e)
- *e = CppEnum::VALUE_1;
- ob = w2;
- s = str;
- vb = str;
- return w;
- };
- detail::transform_callback_wrapper<Derived(GBoxedExample, Derived &,
- CppEnum *, gi::cstring_v &, gpointer &, GLib::Error *)>::
- with_transfer<false, transfer_full_t, transfer_full_t, transfer_full_t,
- transfer_none_t, transfer_none_t, transfer_none_t, transfer_full_t>
- z{n};
- CDerived *ob{};
- CEnum e{};
- char *s{};
- gpointer vb{};
- auto r = z.wrapper(gi_cpp_gbexample_new(), &ob, &e, &s, &vb, nullptr, &z);
- assert(calls == 3);
- assert(r == w.gobj_());
- assert(refcount(r) == 2);
- g_object_unref(r);
- assert(e == CEnum::ENUM_VALUE_1);
- assert(s == str);
- assert(vb == s);
- assert(ob == w2.gobj_());
- assert(refcount(ob) == 2);
- g_object_unref(ob);
- // extended case
- {
- using CLocalCallback_CF_CType = int (*)(int, gpointer);
- struct CLocalCallback_CF_Trait
- {
- using handler_cb_type = CLocalCallback_CF_CType;
- static auto handler(int x, handler_cb_type cb, gpointer ud)
- {
- return cb(x, ud);
- };
- };
- using CppLocalCallback = detail::callback<int(int), transfer_none_t,
- std::tuple<transfer_none_t>>;
- int xx = 0;
- auto nx = [&](GBoxedExample /*b*/, Derived &ob, CppEnum *e, int x,
- CppLocalCallback cb, GLib::Error * /*error*/) {
- ++calls;
- xx = x;
- ob = w2;
- if (e)
- *e = CppEnum::VALUE_1;
- auto r = cb(x);
- // supplied cb below is identity function
- assert(r == x);
- return w;
- };
- detail::transform_callback_wrapper<Derived(GBoxedExample, Derived &,
- CppEnum *, int, CppLocalCallback,
- GLib::Error *),
- CDerived *(GBExample *, CDerived **, CEnum *, int,
- CLocalCallback_CF_CType, gpointer,
- ::GError **)>::with_transfer<false, transfer_none_t,
- detail::arg_info<transfer_full_t, false, void, detail::args_index<0>>,
- detail::arg_info<transfer_none_t, false, void, detail::args_index<1>>,
- detail::arg_info<transfer_full_t, false, void, detail::args_index<2>>,
- detail::arg_info<transfer_full_t, false, void, detail::args_index<3>>,
- detail::arg_info<transfer_full_t, false, CLocalCallback_CF_Trait,
- detail::args_index<4, 5>>,
- detail::arg_info<transfer_full_t, false, void, detail::args_index<6>>>
- zx{nx};
- CDerived *obx{};
- CEnum ex{};
- auto cb = [](int v, gpointer ud) {
- if (ud)
- *(int *)(ud) = v;
- return v;
- };
- int oi = 0;
- auto r = zx.wrapper(
- gi_cpp_gbexample_new(), &obx, &ex, 5, cb, (gpointer)&oi, nullptr, &zx);
- // cpp callback should have been called
- assert(calls == 4);
- assert(xx == 5);
- assert(r == w.gobj_());
- assert(obx != nullptr);
- assert(obx == w2.gobj_());
- assert(e == CEnum::ENUM_VALUE_1);
- // which in turn should have called the supplied callback
- assert(oi == 5);
- //
- // void return case, and sized array out
- char *sx{};
- gpointer vbx{};
- const int V = 56;
- int *a_data{};
- int a_size = 0;
- using TestColType = gi::Collection<gi::DSpan, int, transfer_full_t>;
- auto nnx = [&](gi::cstring_v &s, gpointer &vb, CppLocalCallback cb,
- TestColType &d) {
- ++calls;
- s = str;
- vb = str;
- auto r = cb(V);
- // supplied cb below is identity function
- assert(r == V);
- d.push_back(4);
- };
- detail::transform_callback_wrapper<void(gi::cstring_v & s, gpointer & vb,
- CppLocalCallback, TestColType &),
- void(char **, gpointer *, CLocalCallback_CF_CType, gpointer, int **,
- int *)>::with_transfer<false, transfer_full_t,
- detail::arg_info<transfer_none_t, false, void, detail::args_index<0>>,
- detail::arg_info<transfer_none_t, false, void, detail::args_index<1>>,
- detail::arg_info<transfer_full_t, false, CLocalCallback_CF_Trait,
- detail::args_index<2, 3>>,
- detail::arg_info<transfer_full_t, false, void,
- detail::args_index<4, 5>>>
- zzx{nnx};
- zzx.wrapper(&sx, &vbx, cb, (gpointer)&oi, &a_data, &a_size, &zzx);
- assert(calls == 5);
- assert(sx == str);
- assert(vbx == str);
- // cpp callback should have been called
- assert(oi == V);
- // handle returned array
- assert(a_size == 1);
- assert(*a_data == 4);
- g_free(a_data);
- }
- { // compilation checks
- const CppCallback cppcb(l);
- auto uw{unwrap(cppcb, gi::scope_async)};
- delete uw;
- auto uw2{unwrap(cppcb, gi::scope_notified)};
- delete uw2;
- }
- }
- void
- test_value()
- {
- using GObject_::Value;
- static_assert(traits::gtype<Value>::value, "");
- assert(g_type_is_a(GI_CPP_TYPE_ENUM, G_TYPE_ENUM));
- assert(g_type_is_a(GI_CPP_TYPE_FLAGS, G_TYPE_FLAGS));
- // detail helper
- {
- detail::Value v(5);
- auto vs = detail::transform_value<std::string>(&v);
- assert(vs == "5");
- detail::Value v2("ab");
- auto w = detail::get_value<std::string>(&v2);
- assert(w == "ab");
- }
- // main Value
- {
- Value v;
- v.init(G_TYPE_STRING);
- v.set_value("ab");
- auto w = v.get_value<std::string>();
- assert(w == "ab");
- }
- {
- Value v(0);
- auto w = v.get_value<int>();
- assert(w == 0);
- v.set_value(5);
- w = v.get_value<int>();
- assert(w == 5);
- }
- {
- Value v{std::string()};
- auto w = v.get_value<std::string>();
- assert(w.empty());
- }
- {
- Value v((double)1.0);
- auto w = v.get_value<double>();
- assert(w == 1.0);
- }
- {
- Value v('a');
- auto w = v.get_value<char>();
- assert(w == 'a');
- }
- {
- CDerived *ob = gi_cpp_example_new();
- auto wob = wrap(ob, transfer_none);
- Value v(wob);
- assert(refcount(ob) == 3);
- auto w2 = v.get_value<Derived>();
- assert(w2 == wob);
- assert(refcount(ob) == 4);
- auto wr = wrap(v.gobj_(), transfer_none);
- assert(wr == v);
- assert(refcount(ob) == 4);
- g_object_unref(ob);
- // others clean up by magic
- }
- {
- Value v(GBoxedExample{});
- auto w = v.get_value<GBoxedExample>();
- assert(w.gobj_() == nullptr);
- auto wr = v.get_value<GBoxedExampleRef>();
- assert(wr.gobj_() == nullptr);
- }
- {
- Value v(CppEnum::VALUE_0);
- auto w = v.get_value<CppEnum>();
- assert(w == CppEnum::VALUE_0);
- Value v1(v.copy_());
- assert(v != v1);
- }
- {
- Value v(CppFlags::VALUE_1);
- auto w = v.get_value<CppFlags>();
- assert(w == CppFlags::VALUE_1);
- Value v1(v.copy_());
- assert(v != v1);
- }
- { // test function; auto conversion
- auto tf = [](Value v, GType t) { assert(G_VALUE_TYPE(v.gobj_()) == t); };
- tf(5, G_TYPE_INT);
- tf("ab", G_TYPE_STRING);
- tf(GBoxedExample(), GI_CPP_TYPE_BOXED_EXAMPLE);
- }
- { // wrapping
- GValue *v = (GValue *)1;
- auto w = wrap(v, transfer_none);
- auto v1 = unwrap(w, transfer_none);
- assert(v1 = v);
- }
- }
- void
- test_property()
- {
- CDerived *ob = gi_cpp_example_new();
- // take ownership
- Derived w = wrap(ob, transfer_full);
- // manual
- w.set_property(NAME_NUMBER, 5);
- assert(w.get_property<int>(NAME_NUMBER) == 5);
- w.set_property(NAME_PRESENT, true);
- assert(w.get_property<bool>(NAME_PRESENT) == true);
- const char *str = "value";
- w.set_property(NAME_DATA, str);
- assert(w.get_property<std::string>(NAME_DATA) == str);
- w.set_property(NAME_OBJECT, w);
- auto w2 = w.get_property<Derived>(NAME_OBJECT);
- assert(w2 == w);
- assert(refcount(ob) == 3);
- // remove cycle ref held within ob
- w.set_property(NAME_OBJECT, Derived());
- assert(refcount(ob) == 2);
- w.set_property(NAME_ENUM, CppEnum::VALUE_1);
- assert(w.get_property<CppEnum>(NAME_ENUM) == CppEnum::VALUE_1);
- // multiple props
- w.set_properties(NAME_NUMBER, 10, NAME_FNUMBER, 5.2, NAME_PRESENT, FALSE);
- assert(w.get_property<int>(NAME_NUMBER) == 10);
- assert(w.get_property<double>(NAME_FNUMBER) == 5.2);
- assert(w.get_property<bool>(NAME_PRESENT) == false);
- // generic value
- #ifdef GI_GOBJECT_PROPERTY_VALUE
- w.get_property(NAME_NUMBER);
- #endif
- // via proxy
- Derived w3 = wrap(gi_cpp_example_new(), transfer_full);
- w.property_number().set(7);
- w.property_fnumber().set(6.2);
- w.property_data().set(str);
- w.property_object().set(w3);
- w.property_present().set(true);
- w.property_choice().set(CppEnum::VALUE_0);
- w.property_flags().set(CppFlags::VALUE_0);
- // boxed property
- GQuark domain = g_quark_from_string("test-domain");
- auto error = GLib::Error::new_literal(domain, 1, "msg");
- w.property_error().set(error.copy());
- w.property_error().set(std::move(error));
- const Derived cw = w;
- assert(cw.property_number().get() == 7);
- assert(cw.property_fnumber().get() == 6.2);
- assert(cw.property_data().get() == str);
- assert(cw.property_object().get() == w3);
- assert(cw.property_data().get() == str);
- assert(cw.property_choice().get() == CppEnum::VALUE_0);
- assert(cw.property_flags().get() == CppFlags::VALUE_0);
- // property queries
- auto pspec = cw.find_property(NAME_NUMBER);
- assert(pspec);
- assert(pspec.get_name() == NAME_NUMBER);
- auto pspecs = cw.list_properties();
- assert(pspecs.size() == PROP_LAST);
- }
- void
- test_signal()
- {
- // example values
- double v_d = 2.7;
- int v_i = 4;
- std::string v_s = "values";
- bool v_b = true;
- CppEnum v_e = CppEnum::VALUE_1;
- CppFlags v_f = CppFlags::VALUE_1;
- Derived v_o = wrap(gi_cpp_example_new(), transfer_full);
- // object to signal on
- CDerived *ob = gi_cpp_example_new();
- // take ownership
- Derived w = wrap(ob, transfer_full);
- // lambda callbacks
- int recv = 0;
- int ret = 7;
- auto l1 = [&](Derived src, GObject_::Object o, bool b, bool c,
- const std::string &s) -> int {
- assert(src == w);
- assert(o == v_o);
- assert(s == v_s);
- assert(b == v_b);
- assert(c == !b);
- ++recv;
- return ret;
- };
- w.signal_to_int().connect(l1);
- auto r = w.signal_to_int().emit(v_o, v_b, !v_b, v_s);
- assert(recv == 1);
- assert(r == ret);
- // another signal
- auto l2 = [&](Derived src, int i, gint64 ll) -> std::string {
- assert(src == w);
- ++recv;
- return std::to_string(i + ll);
- };
- w.signal_to_string().connect(l2);
- gint64 ll = 4;
- auto sr = w.signal_to_string().emit(v_i, ll);
- assert(recv == 2);
- assert(std::stoi(sr) == v_i + ll);
- // and another
- auto l3 = w.signal_to_void().slot(
- [&](Derived src, double d, CppEnum e, CppFlags f) {
- assert(src == w);
- assert(d == v_d);
- assert(e == v_e);
- assert(f == v_f);
- ++recv;
- });
- auto id = w.signal_to_void().connect(l3);
- w.signal_to_void().emit(v_d, v_e, v_f);
- assert(recv == 3);
- auto conn = make_connection(id, l3, w);
- assert(conn.connected());
- {
- // safe to disconnect twice (or attempt so)
- GObject_::SignalScopedConnection sconn(conn), sconn2(conn);
- assert(sconn.connected());
- assert(sconn2.connected());
- }
- assert(!conn.connected());
- w.signal_to_void().emit(v_d, v_e, v_f);
- assert(recv == 3);
- // signal collection and output argument
- auto l4 = [&](Derived src, unsigned &o, auto pa, auto la) {
- assert(src == w);
- o = pa.size() + std::distance(la.begin(), la.end());
- };
- gi::Collection<::GPtrArray, char *, gi::transfer_full_t> pa;
- gi::Collection<::GSList, char *, gi::transfer_full_t> la;
- pa.push_back("blah");
- la = pa;
- assert(!la.empty());
- la.push_back("foo");
- w.signal_to_output_int().connect(l4);
- unsigned result = 0;
- w.signal_to_output_int().emit(result, pa, nullptr);
- assert(result == pa.size());
- w.signal_to_output_int().emit(result, nullptr, la);
- assert(result == 2);
- // assert exception helper
- auto assert_exc = [](const std::function<void()> &func) {
- bool exc = false;
- try {
- func();
- } catch (std::exception &) {
- exc = true;
- }
- assert(exc);
- };
- // check connect check
- assert_exc(
- [&]() { w.connect<std::string(Derived, int, gint64)>("to_void", l2); });
- // check property value conversion
- assert_exc([&]() { w.set_property<std::string>(NAME_OBJECT, "blah"); });
- }
- // ExampleInterface
- class ExampleInterface : public gi::InterfaceBase
- {
- public:
- typedef GICppExampleItf BaseObjectType;
- static GType get_type_() G_GNUC_CONST
- {
- return gi_cpp_example_interface_get_type();
- }
- };
- class ExampleInterfaceDef
- {
- typedef ExampleInterfaceDef self;
- public:
- typedef ExampleInterface instance_type;
- typedef GICppExampleInterface interface_type;
- using GI_MEMBER_CHECK_CONFLICT(vmethod) = self;
- using GI_MEMBER_CHECK_CONFLICT(imethod) = self;
- struct TypeInitData;
- protected:
- ~ExampleInterfaceDef() = default;
- static void interface_init(gpointer iface, gpointer /*data*/);
- virtual int vmethod_(int a) = 0;
- virtual int imethod_(int a) = 0;
- };
- using ExampleInterfaceImpl = gi::detail::InterfaceImpl<ExampleInterfaceDef>;
- class ExampleInterfaceClassImpl
- : public gi::detail::InterfaceClassImpl<ExampleInterfaceImpl>
- {
- friend class ExampleInterfaceDef;
- typedef ExampleInterfaceImpl self;
- typedef gi::detail::InterfaceClassImpl<ExampleInterfaceImpl> super;
- protected:
- using super::super;
- int vmethod_(int a) override
- {
- auto _struct = get_struct_();
- return _struct->vmethod(this->gobj_(), a);
- }
- int imethod_(int a) override
- {
- auto _struct = get_struct_();
- return _struct->imethod(this->gobj_(), a);
- }
- };
- struct ExampleInterfaceDef::TypeInitData
- {
- GI_MEMBER_DEFINE(ExampleInterfaceClassImpl, vmethod)
- GI_MEMBER_DEFINE(ExampleInterfaceClassImpl, imethod)
- template<typename SubClass>
- constexpr static TypeInitData factory()
- {
- using DefData = detail::DefinitionData<SubClass, TypeInitData>;
- return {GI_MEMBER_HAS_DEFINITION(SubClass, DefData, vmethod),
- GI_MEMBER_HAS_DEFINITION(SubClass, DefData, imethod)};
- }
- };
- void
- ExampleInterfaceDef::interface_init(gpointer iface, gpointer data)
- {
- auto init_data = GI_MEMBER_INIT_DATA(TypeInitData, data);
- auto itf = (interface_type *)(iface);
- if (init_data.vmethod)
- itf->vmethod = gi::detail::method_wrapper<self, int (*)(int),
- transfer_full_t, std::tuple<transfer_none_t>>::wrapper<&self::vmethod_>;
- if (init_data.imethod)
- itf->imethod = gi::detail::method_wrapper<self, int (*)(int),
- transfer_full_t, std::tuple<transfer_none_t>>::wrapper<&self::imethod_>;
- }
- // PropertyInterface
- class PropertyInterface : public gi::InterfaceBase
- {
- public:
- typedef GICppPropertyItf BaseObjectType;
- static GType get_type_() G_GNUC_CONST
- {
- return gi_cpp_property_interface_get_type();
- }
- };
- class PropertyInterfaceDef
- {
- typedef PropertyInterfaceDef self;
- public:
- typedef PropertyInterface instance_type;
- typedef GICppPropertyInterface interface_type;
- protected:
- static void interface_init(gpointer iface, gpointer /*data*/)
- {
- auto itf = (interface_type *)(iface);
- (void)itf;
- }
- };
- using PropertyInterfaceImpl = gi::detail::InterfaceImpl<PropertyInterfaceDef>;
- class PropertyInterfaceClassImpl
- : public gi::detail::InterfaceClassImpl<PropertyInterfaceImpl>
- {
- typedef PropertyInterfaceImpl self;
- typedef gi::detail::InterfaceClassImpl<PropertyInterfaceImpl> super;
- protected:
- using super::super;
- };
- class DerivedClassDef
- {
- typedef DerivedClassDef self;
- public:
- typedef Derived instance_type;
- typedef GICppExampleClass class_type;
- struct TypeInitData;
- using GI_MEMBER_CHECK_CONFLICT(vmethod) = self;
- using GI_MEMBER_CHECK_CONFLICT(cmethod) = self;
- protected:
- ~DerivedClassDef() = default;
- static void class_init(gpointer g_class, gpointer class_data_factory);
- virtual int vmethod_(int a, int b) = 0;
- virtual int cmethod_(int a, int b) = 0;
- };
- GI_CLASS_IMPL_BEGIN
- class DerivedClass
- : public gi::detail::ClassTemplate<DerivedClassDef,
- GObject_::impl::internal::ObjectClass, ExampleInterfaceClassImpl>
- {
- friend class DerivedClassDef;
- typedef DerivedClass self;
- typedef gi::detail::ClassTemplate<DerivedClassDef,
- GObject_::impl::internal::ObjectClass, ExampleInterfaceClassImpl>
- super;
- public:
- typedef ExampleInterfaceClassImpl ExampleInterface_type;
- private:
- // make local helpers private
- using super::get_struct_;
- using super::gobj_;
- protected:
- GI_DISABLE_DEPRECATED_WARN_BEGIN
- using super::super;
- GI_DISABLE_DEPRECATED_WARN_END
- virtual int vmethod_(int a, int b) override
- {
- auto _struct = get_struct_();
- return _struct->vmethod(gobj_(), a, b);
- }
- virtual int cmethod_(int a, int b) override
- {
- auto _struct = get_struct_();
- return _struct->cmethod(gobj_(), a, b);
- }
- };
- struct DerivedClassDef::TypeInitData
- {
- GI_MEMBER_DEFINE(DerivedClass, vmethod)
- GI_MEMBER_DEFINE(DerivedClass, cmethod)
- template<typename SubClass>
- constexpr static TypeInitData factory()
- {
- using DefData = detail::DefinitionData<SubClass, TypeInitData>;
- return {GI_MEMBER_HAS_DEFINITION(SubClass, DefData, vmethod),
- GI_MEMBER_HAS_DEFINITION(SubClass, DefData, cmethod)};
- }
- };
- void
- DerivedClassDef::class_init(gpointer g_class, gpointer class_data_factory)
- {
- auto class_data = GI_MEMBER_INIT_DATA(TypeInitData, class_data_factory);
- GICppExampleClass *klass = (GICppExampleClass *)g_class;
- if (class_data.vmethod)
- klass->vmethod = gi::detail::method_wrapper<self, int (*)(int, int),
- transfer_full_t,
- std::tuple<transfer_none_t, transfer_none_t>>::wrapper<&self::vmethod_>;
- if (class_data.cmethod)
- klass->cmethod = gi::detail::method_wrapper<self, int (*)(int, int),
- transfer_full_t,
- std::tuple<transfer_none_t, transfer_none_t>>::wrapper<&self::cmethod_>;
- // local compile check
- (void)gi::detail::method_wrapper<self, int (*)(int, int),
- std::nullptr_t>::wrapper<&self::cmethod_>;
- }
- GI_CLASS_IMPL_END
- using DerivedImpl = gi::detail::ObjectImpl<Derived, DerivedClass>;
- template<typename T>
- class custom_property : public gi::property<T>
- {
- public:
- using super = gi::property<T>;
- using super::super;
- using handler = std::function<void(const T &)>;
- handler handler_;
- void set_property(const GValue *value) override
- {
- super::set_property(value);
- if (handler_)
- handler_(this->get_value());
- }
- };
- class UserDerived : public DerivedImpl
- {
- public:
- // possible conflict for vmethod
- // so we specify the override situation explicity
- struct DefinitionData
- {
- GI_DEFINES_MEMBER(DerivedClassDef, vmethod, true)
- GI_DEFINES_MEMBER(ExampleInterfaceDef, vmethod, true)
- };
- UserDerived()
- : DerivedImpl(this), prop_int_set(this, "prop_int_set", "prop_int_set",
- "prop_int_set", 0, 10, 0),
- prop_bool_override(this, NAME_PRESENT)
- {
- // check detection of method definitions
- constexpr auto class_def =
- DerivedClassDef::TypeInitData::factory<UserDerived>();
- static_assert(class_def.vmethod.value, "");
- static_assert(!class_def.cmethod.value, "");
- constexpr auto itf_def =
- ExampleInterfaceDef::TypeInitData::factory<UserDerived>();
- static_assert(itf_def.vmethod.value, "");
- static_assert(!itf_def.imethod.value, "");
- }
- int vmethod_(int a, int b) override { return a * b; }
- int pvmethod(int a, int b) { return DerivedImpl::vmethod_(a, b); }
- int vmethod_(int a) override { return 2 * a; }
- int pivmethod(int a) { return ExampleInterface_type::vmethod_(a); }
- custom_property<int> prop_int_set;
- custom_property<bool> prop_bool_override;
- };
- class UserDerived2 : public DerivedImpl
- {
- public:
- // possible conflict for vmethod
- // so we specify the override situation explicity
- struct DefinitionData
- {
- GI_DEFINES_MEMBER(DerivedClassDef, vmethod, true)
- GI_DEFINES_MEMBER(ExampleInterfaceDef, vmethod, false)
- };
- UserDerived2() : DerivedImpl(this)
- {
- // check detection of method definitions
- constexpr auto x = DerivedClassDef::TypeInitData::factory<UserDerived2>();
- static_assert(x.vmethod.value, "");
- static_assert(x.cmethod.value, "");
- constexpr auto itf_def =
- ExampleInterfaceDef::TypeInitData::factory<UserDerived2>();
- static_assert(!itf_def.vmethod.value, "");
- static_assert(itf_def.imethod.value, "");
- }
- int vmethod_(int a, int b) override { return a * b; }
- int cmethod_(int a, int b) override { return a * b; }
- int imethod_(int a) override { return 5 * a; }
- };
- GI_DISABLE_DEPRECATED_WARN_BEGIN
- class OldUserDerived : public DerivedImpl
- {
- public:
- OldUserDerived() : DerivedImpl(typeid(*this)) {}
- };
- GI_DISABLE_DEPRECATED_WARN_END
- static const int DEFAULT_PROP_INT = 7;
- class UserObject : public ExampleInterfaceImpl,
- public PropertyInterfaceImpl,
- public GObject_::impl::ObjectImpl
- {
- public:
- UserObject()
- : ObjectImpl(this, {}, {{NAME_INUMBER, {&prop_itf_int, nullptr}}}),
- signal_demo_(this, "demo"), prop_itf_int(this, NAME_INUMBER),
- prop_int(
- this, "prop_int", "prop_int", "prop_int", 0, 10, DEFAULT_PROP_INT),
- prop_bool(this, "prop_bool", "prop_bool", "prop_bool", false),
- prop_str(this, "prop_str", "prop_str", "prop_str", ""),
- prop_object(this, "prop_object", "prop_object", "prop_object"),
- prop_enum(this, "prop_enum", "prop_enum", "prop_enum")
- {}
- int vmethod_(int a) override { return 5 * a; }
- int imethod_(int a) override { return 7 * a; }
- gi::signal<void(Object, int)> signal_demo_;
- gi::property<int> prop_itf_int;
- gi::property<int> prop_int;
- gi::property<bool> prop_bool;
- gi::property<std::string> prop_str;
- gi::property<Object> prop_object;
- gi::property<CppEnum> prop_enum;
- };
- void
- test_impl()
- {
- {
- // base object implements interface
- UserDerived u, v;
- assert(u.gobj_type_() == v.gobj_type_());
- assert(u.pvmethod(2, 3) == 5);
- auto klass =
- G_TYPE_INSTANCE_GET_CLASS(u.gobj_(), u.gobj_type(), GICppExampleClass);
- assert(klass->vmethod(u.gobj_(), 2, 3) == 6);
- // no cmethod
- assert(!klass->cmethod);
- // interface
- assert(u.pivmethod(4) == 6);
- auto iface = G_TYPE_INSTANCE_GET_INTERFACE(
- u.gobj_(), ExampleInterface::get_type_(), GICppExampleInterface);
- assert(iface->vmethod((GICppExampleItf *)u.gobj_(), 4) == 8);
- assert(!iface->imethod);
- // implemented here
- UserDerived2 w;
- assert(w.imethod_(2) == 10);
- // compilation check
- assert(u.gobj_klass()->vmethod);
- { // check custom property (on a non-Object derived class)
- int value = 0;
- auto &tp = u.prop_int_set;
- tp.handler_ = [&value](int setv) { value = setv; };
- auto proxy = tp.get_proxy();
- //
- const int NEW_VALUE = 7;
- proxy.set(NEW_VALUE);
- assert(value == NEW_VALUE);
- assert(tp.get_value() == NEW_VALUE);
- tp.handler_ = nullptr;
- }
- { // likewise on overriden property
- auto &tp = u.prop_bool_override;
- auto proxy = tp.get_proxy();
- proxy.set(false);
- assert(!proxy.get());
- bool value = false;
- tp.handler_ = [&value](bool setv) { value = setv; };
- const bool NEW_VALUE = true;
- //
- proxy.set(NEW_VALUE);
- assert(value == NEW_VALUE);
- assert(tp.get_value() == NEW_VALUE);
- tp.handler_ = nullptr;
- }
- }
- {
- // vanilla object
- UserObject u, v;
- assert(u.gobj_type_() == v.gobj_type_());
- auto iface = G_TYPE_INSTANCE_GET_INTERFACE(
- u.gobj_(), ExampleInterface::get_type_(), GICppExampleInterface);
- assert(iface);
- assert(iface->vmethod((GICppExampleItf *)u.gobj_(), 4) == 20);
- // signal
- int i = 0, j = 5;
- u.signal_demo_.connect([&i](GObject_::Object, int in) -> void { i = in; });
- u.signal_demo_.emit(j);
- assert(i == j);
- // properties
- {
- // also check notification
- bool notified = false;
- auto proxy = u.prop_int.get_proxy();
- auto l = proxy.signal_notify().slot(
- [¬ified](
- GObject_::Object, GObject_::ParamSpec) { notified = true; });
- GObject_::SignalScopedConnection conn =
- make_connection(proxy.signal_notify().connect(l), l, u);
- assert(u.prop_int == DEFAULT_PROP_INT);
- u.prop_int = j;
- assert(notified);
- notified = false;
- assert(proxy.get() == j);
- proxy.set(2 * j);
- assert(u.prop_int == 2 * j);
- assert(notified);
- }
- {
- auto proxy = u.prop_bool.get_proxy();
- u.prop_bool = true;
- assert(proxy.get() == true);
- proxy.set(false);
- assert(u.prop_bool == false);
- }
- {
- auto proxy = u.prop_str.get_proxy();
- const std::string strv = "value";
- u.prop_str = strv;
- assert(proxy.get() == strv);
- proxy.set(strv + strv);
- assert(u.prop_str.get_value() == (strv + strv));
- }
- {
- auto proxy = u.prop_object.get_proxy();
- u.prop_object = v;
- assert(proxy.get() == v);
- proxy.set(nullptr);
- assert(u.prop_object.get_value() == nullptr);
- }
- {
- CppEnum v1 = CppEnum::VALUE_1, v0 = CppEnum::VALUE_0;
- auto proxy = u.prop_enum.get_proxy();
- u.prop_enum = v1;
- assert(proxy.get() == v1);
- proxy.set(v0);
- assert(u.prop_enum == v0);
- }
- {
- const int val = 8;
- u.prop_itf_int = val;
- auto proxy = u.prop_itf_int.get_proxy();
- assert(proxy.get() == val);
- proxy.set(val);
- assert(u.prop_itf_int == val);
- }
- {
- // local properties
- property_read<bool> p{&u, "p", "p", "p", false};
- p.get_proxy();
- property_write<bool> q{&u, "p", "p", "p", false};
- q.get_proxy();
- }
- }
- {
- // create non-stack
- auto u = gi::make_ref<UserObject>();
- assert(u->list_properties().size() > 0);
- // cast works ok
- GObject_::Object v = u;
- assert(refcount(v.gobj_()) == 2);
- auto l = [](GObject_::Object) {};
- l(u);
- auto u2 = ref_ptr_cast<UserObject>(v);
- assert(u2 == u);
- assert(refcount(v.gobj_()) == 3);
- auto u3 = u2;
- assert(refcount(v.gobj_()) == 4);
- auto u4 = std::move(u3);
- assert(refcount(v.gobj_()) == 4);
- assert(!u3);
- }
- }
- int
- main(int argc, char *argv[])
- {
- (void)argc;
- (void)argv;
- test_trait();
- test_wrap();
- test_collection();
- test_string();
- test_exception();
- test_enumflag();
- test_value();
- test_property();
- test_signal();
- test_callback();
- test_impl();
- return 0;
- }
|