libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

parent_list.cpp (20586B)


      1 #include "libshit/container/parent_list.hpp"
      2 
      3 #include "libshit/doctest.hpp"
      4 #include "libshit/shared_ptr.hpp"
      5 #include "libshit/utils.hpp"
      6 
      7 #include <algorithm>
      8 #include <initializer_list>
      9 #include <ostream>
     10 #include <stdexcept>
     11 
     12 namespace Libshit::Test
     13 {
     14   TEST_SUITE_BEGIN("Libshit::ParentList");
     15   static int count;
     16 
     17   namespace
     18   {
     19     struct ParentListItem : RefCounted, ParentListBaseHook<>, Lua::DynamicObject
     20     {
     21       LIBSHIT_DYNAMIC_OBJECT;
     22     public:
     23       ParentListItem() = default;
     24       ParentListItem(int data) : data{data} {}
     25 
     26       int i, data;
     27     };
     28     struct ParentListItemTraits; // IWYU...
     29     using XList = ParentList<ParentListItem, ParentListItemTraits>;
     30 
     31     inline bool XCmp(const ParentListItem& a, const ParentListItem& b) noexcept
     32     { return &a == &b; }
     33 
     34     inline bool operator<(
     35       const ParentListItem& a, const ParentListItem& b) noexcept
     36     { return a.data < b.data; }
     37 
     38     inline bool operator==(
     39       const ParentListItem& a, const ParentListItem& b) noexcept
     40     { return a.data == b.data; }
     41 
     42     inline bool operator==(int a, const ParentListItem& b) noexcept
     43     { return a == b.data; }
     44 
     45     inline std::ostream& operator<<(std::ostream& os, const ParentListItem& x)
     46     { return os << "ParentListItem{" << x.i << ", " << x.data << "}"; }
     47 
     48     inline std::ostream& operator<<(std::ostream& os, const XList& lst)
     49     {
     50       os << "List{";
     51       bool delim = false;
     52       for (auto& i : lst)
     53       {
     54         if (delim) os << ", ";
     55         delim = true;
     56         os << i;
     57       }
     58       return os << "}";
     59     }
     60 
     61     struct ParentListItemTraits
     62     {
     63       static void add(XList&, ParentListItem& x) noexcept { x.i = count++; }
     64       static void remove(XList&, ParentListItem&) noexcept { --count; }
     65     };
     66 
     67 
     68     using X = ParentListItem;
     69   }
     70 
     71   TEST_CASE("ParentListHook")
     72   {
     73     X x;
     74     CHECK(x.is_linked() == false);
     75     CHECK(x.is_root() == false);
     76     CHECK(XList::opt_get_parent(x) == nullptr);
     77     CHECK_THROWS(XList::get_parent<Check::Throw>(x));
     78 
     79     XList lst;
     80     lst.push_back(x);
     81     CHECK(x.is_linked() == true);
     82     CHECK(x.is_root() == false);
     83     CHECK(XList::opt_get_parent(x) == &lst);
     84     CHECK(&XList::get_parent(x) == &lst);
     85 
     86     x.unlink();
     87 
     88     CHECK(x.is_linked() == false);
     89     CHECK(lst.empty());
     90   }
     91 
     92   TEST_CASE("default_ctor")
     93   {
     94     XList lst;
     95     SUBCASE("basic invariants")
     96     {
     97       CHECK(lst.empty());
     98       CHECK(lst.size() == 0);
     99       CHECK(lst.begin() == lst.end());
    100     }
    101 
    102 #define GEN(name)                                                   \
    103     SUBCASE(#name " throws")                                        \
    104     { CHECK_THROWS_AS(lst.name<Check::Throw>(), std::out_of_range); }
    105     GEN(pop_front) GEN(pop_back) GEN(front) GEN(back);
    106 #undef GEN
    107   }
    108 
    109 
    110   static bool Equal(
    111     std::initializer_list<std::reference_wrapper<X>> il, const XList& lst)
    112   {
    113     return std::equal(il.begin(), il.end(), lst.begin(), lst.end(),
    114                       Libshit::Test::XCmp);
    115   }
    116   static bool Equal(const X* b, const X* e, const XList& lst)
    117   { return std::equal(b, e, lst.begin(), lst.end(), Libshit::Test::XCmp); }
    118 
    119   TEST_CASE("basic")
    120   {
    121     count = 0;
    122     {
    123       X xs[6];
    124       XList lst{xs, xs+3};
    125       REQUIRE(!lst.empty());
    126       REQUIRE(lst.size() == 3);
    127 
    128       SUBCASE("move ctor")
    129       {
    130         XList lst2{Move(lst)};
    131         CAPTURE(lst2);
    132         CHECK(Equal(xs, xs+3, lst2));
    133         CHECK(lst.empty()); // not part of API
    134         CHECK(&XList::get_parent(xs[0]) == &lst2);
    135       }
    136 
    137       SUBCASE("move assign")
    138       {
    139         XList lst2{xs+3, xs+6};
    140         lst2 = Move(lst);
    141         CHECK(Equal(xs, xs+3, lst2));
    142         CHECK(lst.empty()); // not part of API
    143         CHECK(&XList::get_parent(xs[0]) == &lst2);
    144       }
    145 
    146       SUBCASE("swap")
    147       {
    148         XList lst2{xs+3, xs+6};
    149         lst.swap(lst2);
    150         CHECK(Equal(xs, xs+3, lst2));
    151         CHECK(Equal(xs+3, xs+6, lst));
    152         CHECK(&XList::get_parent(xs[0]) == &lst2);
    153         CHECK(&XList::get_parent(xs[3]) == &lst);
    154       }
    155 
    156       SUBCASE("push_back")
    157       {
    158         lst.push_back<Check::Throw>(xs[3]);
    159         CHECK(Equal(xs, xs+4, lst));
    160       }
    161       SUBCASE("pop_back")
    162       {
    163         lst.pop_back<Check::Throw>();
    164         CHECK(Equal(xs, xs+2, lst));
    165       }
    166       SUBCASE("push_front")
    167       {
    168         lst.push_front(xs[3]);
    169         CHECK(Equal({ xs[3], xs[0], xs[1], xs[2] }, lst));
    170       }
    171       SUBCASE("pop_front")
    172       {
    173         lst.pop_front<Check::Throw>();
    174         CHECK(Equal(xs+1, xs+3, lst));
    175 
    176         SUBCASE("then pop_back")
    177         {
    178           lst.pop_back<Check::Throw>();
    179           CHECK(Equal(xs+1, xs+2, lst));
    180         }
    181       }
    182 
    183       SUBCASE("front") { CHECK(&lst.front<Check::Throw>() == &xs[0]); }
    184       SUBCASE("back")  { CHECK(&lst.back<Check::Throw>()  == &xs[2]); }
    185 
    186       SUBCASE("iterator")
    187       {
    188         int i = 0;
    189         for (auto it = lst.begin(); it != lst.end(); ++it, ++i)
    190         {
    191           it->i = i+10;
    192           CHECK(&*it == &xs[i]);
    193           CHECK(xs[i].i == i+10);
    194         }
    195         CHECK(i == 3);
    196       }
    197 
    198       SUBCASE("const_iterator")
    199       {
    200         int i = 0;
    201         for (auto it = lst.cbegin(); it != lst.cend(); ++it, ++i)
    202         {
    203           CHECK(&*it == &xs[i]);
    204           CHECK(it.operator->() == &xs[i]);
    205           static_assert(std::is_same_v<decltype(*it), const X&>);
    206         }
    207         CHECK(i == 3);
    208       }
    209 
    210       SUBCASE("reverse_iterator")
    211       {
    212         int i = 2;
    213         for (auto it = lst.rbegin(); it != lst.rend(); ++it, --i)
    214           CHECK(&*it == &xs[i]);
    215         CHECK(i == -1);
    216       }
    217 
    218       SUBCASE("shift_backwards")
    219       {
    220         lst.shift_backwards();
    221         CHECK(Equal({ xs[2], xs[0], xs[1] }, lst));
    222       }
    223 
    224       SUBCASE("shift_forward")
    225       {
    226         lst.shift_forward();
    227         CHECK(Equal({ xs[1], xs[2], xs[0] }, lst));
    228       }
    229 
    230       SUBCASE("erase")
    231       {
    232         SUBCASE("from begin")
    233         {
    234           auto nbegin = lst.erase<Check::Throw>(lst.begin());
    235           CHECK(nbegin == lst.begin());
    236           CHECK(Equal(xs+1, xs+3, lst));
    237         }
    238 
    239         SUBCASE("from end")
    240         {
    241           auto nend = lst.erase<Check::Throw>(--lst.end());
    242           CHECK(nend == lst.end());
    243           CHECK(Equal(xs, xs+2, lst));
    244         }
    245 
    246         SUBCASE("from middle")
    247         {
    248           auto it = lst.erase<Check::Throw>(++lst.begin());
    249           CHECK(it == --lst.end());
    250           CHECK(Equal({ xs[0], xs[2] }, lst));
    251         }
    252 
    253         SUBCASE("range")
    254         {
    255           lst.erase<Check::Throw>(lst.begin(), --lst.end());
    256           CHECK(Equal(xs+2, xs+3, lst));
    257         }
    258 
    259         SUBCASE("invalid")
    260         {
    261           CHECK_THROWS_AS(lst.erase<Check::Throw>(lst.end()),
    262                           ContainerConsistency);
    263           CHECK_THROWS_AS(lst.erase<Check::Throw>(lst.end(), lst.begin()),
    264                           ContainerConsistency);
    265           CHECK_THROWS_AS(lst.erase<Check::Throw>(--lst.end(), ++lst.begin()),
    266                           ContainerConsistency);
    267           CHECK(Equal(xs, xs+3, lst)); // check strong guarantee
    268         }
    269       }
    270 
    271       SUBCASE("clear")
    272       {
    273         lst.clear();
    274         CHECK(lst.empty());
    275       }
    276 
    277       SUBCASE("insert")
    278       {
    279         SUBCASE("single")
    280         {
    281           lst.insert<Check::Throw>(++lst.begin(), xs[4]);
    282           CHECK(Equal({ xs[0], xs[4], xs[1], xs[2] }, lst));
    283         }
    284 
    285         SUBCASE("range")
    286         {
    287           lst.insert<Check::Throw>(--lst.end(), xs+3, xs+5);
    288           CHECK(Equal({ xs[0], xs[1], xs[3], xs[4], xs[2] }, lst));
    289         }
    290 
    291         SUBCASE("invalid")
    292         {
    293           CHECK_THROWS_AS(lst.insert<Check::Throw>(lst.end(), xs[0]),
    294                           ContainerConsistency);
    295           CHECK_THROWS_AS(lst.insert<Check::Throw>(lst.end(), xs, xs+2),
    296                           ContainerConsistency);
    297           CHECK(Equal(xs, xs+3, lst)); // check strong guarantee
    298         }
    299       }
    300 
    301       SUBCASE("assign")
    302       {
    303         SUBCASE("correct")
    304         {
    305           lst.assign<Check::Throw>(xs+3, xs+6);
    306           CHECK(Equal(xs+3, xs+6, lst));
    307         }
    308         SUBCASE("overlapping")
    309         {
    310           CHECK_THROWS_AS(lst.assign<Check::Throw>(xs+2, xs+4),
    311                           ContainerConsistency);
    312           CHECK(Equal(xs, xs+3, lst));
    313         }
    314       }
    315 
    316       SUBCASE("splice")
    317       {
    318         XList lst2{xs+3, xs+6};
    319         SUBCASE("whole")
    320         {
    321           lst.splice<Check::Throw>(++lst.begin(), lst2);
    322           CHECK(lst2.empty());
    323           CHECK(Equal({ xs[0], xs[3], xs[4], xs[5], xs[1], xs[2] }, lst));
    324         }
    325         SUBCASE("single")
    326         {
    327           lst.splice<Check::Throw>(lst.end(), lst2, ++lst2.begin());
    328           CHECK(Equal({ xs[3], xs[5] }, lst2));
    329           CHECK(Equal({ xs[0], xs[1], xs[2], xs[4] }, lst));
    330         }
    331         SUBCASE("range")
    332         {
    333           lst.splice<Check::Throw>(
    334             --lst.end(), lst2, lst2.begin(), --lst2.end());
    335           CHECK(Equal({ xs[5] }, lst2));
    336           CHECK(Equal({ xs[0], xs[1], xs[3], xs[4], xs[2] }, lst));
    337         }
    338 
    339         SUBCASE("invalid")
    340         {
    341           CHECK_THROWS_AS(
    342             lst.splice<Check::Throw>(lst.end(), lst2, lst2.end()),
    343             ContainerConsistency);
    344           CHECK_THROWS_AS(
    345             lst.splice<Check::Throw>(lst.end(), lst2, lst.begin()),
    346             ContainerConsistency);
    347           CHECK_THROWS_AS(
    348             lst.splice<Check::Throw>(lst.end(), lst2, lst.begin(), lst.end()),
    349             ContainerConsistency);
    350           CHECK(Equal(xs+3, xs+6, lst2));
    351           CHECK(Equal(xs, xs+3, lst));
    352         }
    353       }
    354 
    355       SUBCASE("reverse")
    356       {
    357         lst.reverse();
    358         CHECK(Equal({ xs[2], xs[1], xs[0] }, lst));
    359       }
    360 
    361 #define GEN(nam, code, var, fun, stat_fun)                       \
    362       SUBCASE(nam "iterator_to")                                 \
    363       {                                                          \
    364         code;                                                    \
    365         CHECK(var.fun<Check::Throw>(xs[0]) == var.begin());      \
    366         CHECK(var.fun<Check::Throw>(xs[1]) == ++var.begin());    \
    367         CHECK(var.fun<Check::Throw>(xs[2]) == ++++var.begin());  \
    368                                                                  \
    369         SUBCASE("validation")                                    \
    370         {                                                        \
    371           XList lst2{xs+5, xs+6};                                \
    372           CHECK_THROWS_AS(var.fun<Check::Throw>(xs[4]),          \
    373                           ContainerConsistency);                 \
    374           if (stat_fun)                                          \
    375             CHECK(var.fun<Check::Throw>(xs[5]) == lst2.begin()); \
    376           else                                                   \
    377             CHECK_THROWS_AS(var.fun<Check::Throw>(xs[5]),        \
    378                             ContainerConsistency);               \
    379         }                                                        \
    380       }
    381       GEN(,, lst, iterator_to, false);
    382       GEN("const ", const auto& clst = lst, clst, iterator_to, false);
    383       GEN(,, lst, s_iterator_to, true);
    384 #undef GEN
    385 
    386       SUBCASE("container_from_iterator")
    387       {
    388         CHECK(&XList::container_from_iterator<Check::Throw>(lst.begin()) == &lst);
    389       }
    390       SUBCASE("invalid iterator")
    391       {
    392         XList::iterator itb, ite;
    393         { XList lst2{xs+4, xs+6}; itb = lst2.begin(); ite = lst2.end(); }
    394         // it is invalid at this point
    395         SUBCASE("erase")
    396         { CHECK_THROWS(lst.erase<Check::Throw>(itb)); }
    397         SUBCASE("erase range")
    398         { CHECK_THROWS(lst.erase<Check::Throw>(itb, ite)); }
    399 
    400         SUBCASE("insert")
    401         { CHECK_THROWS(lst.insert<Check::Throw>(itb, xs[4])); }
    402 
    403         // the following is UB as end points to lst2 itself which is destructed
    404         // at this point (fortunately iterators are never passed to lua code)
    405         // SUBCASE("insert range")
    406         // { CHECK_THROWS(lst.insert<Check::Throw>(ite, xs+4, xs+6)); }
    407 
    408         SUBCASE("iterator_to")
    409         { CHECK_THROWS(lst.iterator_to<Check::Throw>(xs[4])); }
    410         SUBCASE("container_from_iterator")
    411         { CHECK_THROWS(XList::container_from_iterator<Check::Throw>(itb)); }
    412         SUBCASE("s_iterator_to")
    413         { CHECK_THROWS(XList::s_iterator_to<Check::Throw>(xs[4])); }
    414       }
    415     }
    416 
    417     CHECK(count == 0);
    418   }
    419 
    420   TEST_CASE("algorithms")
    421   {
    422     X xs[10];
    423     XList lst{std::begin(xs), std::end(xs)};
    424 
    425     SUBCASE("sort")
    426     {
    427       SUBCASE("already sorted") { for (int i = 0; i < 10; ++i) xs[i].data = i; }
    428       SUBCASE("reverse order") { for (int i = 0; i < 10; ++i) xs[i].data = 9-i; }
    429       SUBCASE("random order")
    430       {
    431         xs[0].data = 0; xs[1].data = 5; xs[2].data = 4; xs[3].data = 7;
    432         xs[4].data = 1; xs[5].data = 8; xs[6].data = 2; xs[7].data = 9;
    433         xs[8].data = 3; xs[9].data = 6;
    434       }
    435       lst.sort();
    436 
    437       int exp[] = {0,1,2,3,4,5,6,7,8,9};
    438       CAPTURE(lst);
    439       CHECK(std::equal(std::begin(exp), std::end(exp), lst.begin(), lst.end()));
    440     }
    441 
    442     SUBCASE("stable sort")
    443     {
    444       xs[0].data = 3; xs[1].data = 3; xs[2].data = 3;
    445       xs[3].data = 0;
    446       xs[4].data = 4; xs[5].data = 7; xs[6].data = 4; xs[7].data = 1;
    447         xs[8].data = 4;
    448       xs[9].data = 0;
    449       lst.sort();
    450 
    451       CAPTURE(lst);
    452       CHECK(Equal({ xs[3], xs[9], xs[7], xs[0], xs[1], xs[2], xs[4], xs[6],
    453                     xs[8], xs[5] }, lst));
    454     }
    455 
    456     SUBCASE("sort custom compare")
    457     {
    458       for (int i = 0; i < 10; ++i) xs[i].data = (i+7) % 10;
    459       lst.sort([](const auto& a, const auto& b) { return a.data > b.data; });
    460 
    461       int exp[] = {9,8,7,6,5,4,3,2,1,0};
    462       CAPTURE(lst);
    463       CHECK(std::equal(std::begin(exp), std::end(exp), lst.begin(), lst.end()));
    464     }
    465 
    466     SUBCASE("remove")
    467     {
    468       for (int i = 0; i < 4; ++i) xs[i].data = 0;
    469       for (int i = 4; i < 8; ++i) xs[i].data = i;
    470       for (int i = 8; i < 10; ++i) xs[i].data = i-4;
    471       // xs: 0,0,0,0, 4,5,6,7, 4,5
    472       SUBCASE("single")
    473       {
    474         lst.remove(xs[6]);
    475         CAPTURE(lst);
    476         CHECK(Equal({xs[0], xs[1], xs[2], xs[3], xs[4], xs[5], xs[7], xs[8],
    477                 xs[9] }, lst));
    478       }
    479       SUBCASE("multi")
    480       {
    481         lst.remove(xs[0]);
    482         CAPTURE(lst);
    483         CHECK(Equal(xs+4, xs+10, lst));
    484       }
    485       SUBCASE("predicate")
    486       {
    487         lst.remove_if([](auto& x) { return x.data < 5; });
    488         CAPTURE(lst);
    489         CHECK(Equal({ xs[5], xs[6], xs[7], xs[9] }, lst));
    490       }
    491       SUBCASE("nothing")
    492       {
    493         lst.remove_if([](auto&) { return false; });
    494         CHECK(Equal(xs, xs+10, lst));
    495       }
    496 
    497       SUBCASE("unique")
    498       {
    499         lst.unique();
    500         CAPTURE(lst);
    501         CHECK(Equal({ xs[0], xs[4], xs[5], xs[6], xs[7], xs[8], xs[9] }, lst));
    502       }
    503 
    504       SUBCASE("unique_if")
    505       {
    506         lst.unique([](auto& a, auto& b) { return a.data != b.data; });
    507         CAPTURE(lst);
    508         CHECK(Equal(xs, xs+4, lst));
    509       }
    510 
    511       SUBCASE("unique_if 2")
    512       {
    513         lst.unique([](auto& a, auto& b) { return a.data >= b.data; });
    514         CAPTURE(lst);
    515         CHECK(Equal({ xs[0], xs[4], xs[5], xs[6], xs[7] }, lst));
    516       }
    517     }
    518   }
    519 
    520   TEST_CASE("merge")
    521   {
    522     X xs[10];
    523     SUBCASE("empty")
    524     {
    525       XList lst0{xs, xs+10}, lst1;
    526       SUBCASE("from empty")
    527       {
    528         lst0.merge<Check::Throw>(lst1);
    529         CHECK(lst1.empty());
    530         CHECK(Equal(xs, xs+10, lst0));
    531       }
    532       SUBCASE("into empty")
    533       {
    534         lst1.merge<Check::Throw>(lst0);
    535         CHECK(lst0.empty());
    536         CHECK(Equal(xs, xs+10, lst1));
    537       }
    538     }
    539 
    540     SUBCASE("self")
    541     {
    542       XList lst{xs, xs+10};
    543       CHECK_THROWS_AS(lst.merge<Check::Throw>(lst), ContainerConsistency);
    544     }
    545 
    546     SUBCASE("same-sized")
    547     {
    548       XList lst0{xs, xs+5}, lst1{xs+5, xs+10};
    549       SUBCASE("(0...5).merge(5...10)")
    550       { for (int i = 0; i < 10; ++i) xs[i].data = i; }
    551 
    552       SUBCASE("(5...10).merge(0...5)")
    553       { for (int i = 0; i < 10; ++i) xs[i].data = (i+5) % 10; }
    554 
    555       // 1,3,5,7,9, 0,2,4,6,8
    556       SUBCASE("zig-zag")
    557       { for (int i = 0; i < 10; ++i) xs[i].data = (i*2+1) % 11; }
    558 
    559       lst0.merge<Check::Throw>(lst1);
    560       CHECK(lst1.empty());
    561       int exp[] = { 0,1,2,3,4,5,6,7,8,9 };
    562       CAPTURE(lst0);
    563       CHECK(std::equal(std::begin(exp), std::end(exp), lst0.begin(), lst0.end()));
    564     }
    565 
    566     SUBCASE("stable")
    567     {
    568       XList lst0{xs, xs+5}, lst1{xs+5, xs+10};
    569       xs[0].data = 0; xs[1].data = 0; xs[5].data = 0;
    570       xs[2].data = 1; xs[6].data = 1; xs[7].data = 1;
    571       xs[3].data = 2;
    572       xs[8].data = 3;
    573       xs[4].data = 4; xs[9].data = 4;
    574       lst0.merge<Check::Throw>(lst1);
    575       CHECK(lst1.empty());
    576       CHECK(Equal({ xs[0], xs[1], xs[5], xs[2], xs[6], xs[7], xs[3], xs[8],
    577               xs[4], xs[9] }, lst0));
    578     }
    579   }
    580 
    581 #define CHK(fun, call, ops, body, pre, scope, end)                \
    582   TEST_CASE(#fun " strong guarantee")                             \
    583   {                                                               \
    584     int counter; bool except = true;                              \
    585     for (int i = 0; except; ++i)                                  \
    586     {                                                             \
    587       counter = i;                                                \
    588       count = 0;                                                  \
    589       except = false;                                             \
    590       pre                                                         \
    591       {                                                           \
    592         scope                                                     \
    593                                                                   \
    594         try                                                       \
    595         {                                                         \
    596           call [&]ops { if (!counter--) throw 1; return body; }); \
    597         }                                                         \
    598         catch (int) { except = true; }                            \
    599                                                                   \
    600         end                                                       \
    601       }                                                           \
    602       CHECK(count == 0);                                          \
    603     }                                                             \
    604   }
    605 
    606 #define OPENPAREN (
    607 #define COMMA ,
    608 #define CHKBASIC(fun, ops, body, inv)                \
    609     CHK(fun, lst.fun OPENPAREN, ops, body,           \
    610         X xs[5];                                     \
    611         for (int j = 0; j < 5; ++j) xs[j].data = j;, \
    612         XList lst(xs, xs+5);,                        \
    613         INFO("lst = " << lst << ", i = " << i);      \
    614         CHECK(inv);)
    615 
    616   CHKBASIC(sort, (auto& a, auto& b), a.data < b.data, lst.size() == 5)
    617   CHKBASIC(remove_if, (auto& a), a.data == 2,
    618            (lst.size() & std::size_t(~1)) == 4)
    619   CHKBASIC(unique, (auto&, auto& b), b.data == 3,
    620            (lst.size() & std::size_t(~1)) == 4)
    621 
    622   CHK(merge, lst0.merge OPENPAREN lst1 COMMA, (auto& a, auto& b),
    623       a.data < b.data,
    624       X xs[10];
    625       for (int j = 0; j < 10; ++j) xs[j].data = (2*j+1) % 11;,
    626       XList lst0(xs, xs+5) COMMA lst1(xs+5, xs+10);,
    627       CHECK(lst0.size() + lst1.size() == 10);)
    628 
    629 #if LIBSHIT_WITH_LUA
    630   TEST_CASE("lua binding")
    631   {
    632     SmartPtr<X> xs[5];
    633 
    634     Lua::State vm;
    635     auto lst = MakeSmart<XList>();
    636     vm.TranslateException([&]()
    637     {
    638       vm.Push(lst);
    639       lua_setglobal(vm, "lst");
    640 
    641       for (size_t i = 0; i < 5; ++i)
    642       {
    643         xs[i] = MakeSmart<X>(i+3);
    644         lst->push_back(*xs[i]);
    645       }
    646     });
    647 
    648     SUBCASE("sort")
    649     {
    650       vm.DoString("lst:sort(function(a,b) return a.data > b.data end)");
    651       CHECK(Equal({ *xs[4], *xs[3], *xs[2], *xs[1], *xs[0] }, *lst));
    652       vm.DoString("lst:sort()");
    653       CHECK(Equal({ *xs[0], *xs[1], *xs[2], *xs[3], *xs[4] }, *lst));
    654     }
    655 
    656     SUBCASE("unique")
    657     {
    658       vm.DoString("lst:unique(function(a,b) return a.data//2 == b.data//2 end)");
    659       CHECK(Equal({ *xs[0], *xs[1], *xs[3] }, *lst));
    660     }
    661 
    662     SUBCASE("erase")
    663     {
    664       vm.DoString("lst:erase(lst:next(lst:front()))");
    665       CHECK(Equal({ *xs[0], *xs[2], *xs[3], *xs[4] }, *lst));
    666     }
    667 
    668     SUBCASE("to_table")
    669     {
    670       vm.DoString(R"(
    671 local tbl = lst:to_table()
    672 for i=0,4 do
    673   assert(tbl[i].i == i and tbl[i].data == i+3)
    674 end
    675 assert(tbl[5] == nil)
    676 )");
    677     }
    678   }
    679 #endif
    680 }
    681 
    682 #include <libshit/container/parent_list.lua.hpp>
    683 LIBSHIT_PARENT_LIST_LUAGEN(
    684   parent_list_item, true, Libshit::Test::ParentListItem,
    685   Libshit::Test::ParentListItemTraits);
    686 #include "parent_list.binding.hpp"