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"