instantiate with meta calls

This commit is contained in:
2019-11-02 03:48:02 +07:00
parent ee0393d5f7
commit 1fc8d3ff77
7 changed files with 220 additions and 237 deletions

View File

@@ -9,26 +9,20 @@
#include "../_high.hpp" #include "../_high.hpp"
#include "../factory.hpp" #include "../factory.hpp"
#include "../assets/script_asset.hpp"
namespace e2d namespace e2d
{ {
class behaviour final { class behaviour final {
public: public:
behaviour() = default; behaviour() = default;
behaviour(const script_asset::ptr& script);
behaviour& meta(sol::table&& value) noexcept; behaviour& meta(sol::table value) noexcept;
behaviour& meta(const sol::table& value);
[[nodiscard]] sol::table& meta() noexcept; [[nodiscard]] sol::table& meta() noexcept;
[[nodiscard]] const sol::table& meta() const noexcept; [[nodiscard]] const sol::table& meta() const noexcept;
behaviour& script(const script_asset::ptr& value) noexcept;
[[nodiscard]] const script_asset::ptr& script() const noexcept;
private: private:
str name_;
sol::table meta_; sol::table meta_;
script_asset::ptr script_;
}; };
template <> template <>
@@ -48,19 +42,11 @@ namespace e2d
namespace e2d namespace e2d
{ {
inline behaviour::behaviour(const script_asset::ptr& value) inline behaviour& behaviour::meta(sol::table value) noexcept {
: script_(value) {}
inline behaviour& behaviour::meta(sol::table&& value) noexcept {
meta_ = std::move(value); meta_ = std::move(value);
return *this; return *this;
} }
inline behaviour& behaviour::meta(const sol::table& value) {
meta_ = value;
return *this;
}
inline sol::table& behaviour::meta() noexcept { inline sol::table& behaviour::meta() noexcept {
return meta_; return meta_;
} }
@@ -68,13 +54,39 @@ namespace e2d
inline const sol::table& behaviour::meta() const noexcept { inline const sol::table& behaviour::meta() const noexcept {
return meta_; return meta_;
} }
}
inline behaviour& behaviour::script(const script_asset::ptr& value) noexcept { namespace e2d::behaviours
script_ = value; {
return *this; enum class call_result {
} failed,
success,
method_not_found,
};
inline const script_asset::ptr& behaviour::script() const noexcept { template < typename... Args >
return script_; call_result call_meta_method(behaviour& behaviour, str_view method, Args&&... args) {
if ( method.empty() || !behaviour.meta() || !behaviour.meta().valid() ) {
return call_result::method_not_found;
}
sol::optional<sol::protected_function> f = behaviour.meta()[method];
if ( !f ) {
return call_result::method_not_found;
}
sol::protected_function_result r = f->call(
behaviour.meta(),
std::forward<Args>(args)...);
if ( !r.valid() ) {
the<debug>().error("BEHAVIOUR: Behaviour method error:\n"
"--> Method: %0\n"
"--> Error: %1",
method,
sol::error(r).what());
return call_result::failed;
}
return call_result::success;
} }
} }

View File

@@ -25,29 +25,16 @@ namespace e2d
gobject instantiate(); gobject instantiate();
gobject instantiate(const prefab& prefab); gobject instantiate(const prefab& prefab);
gobject instantiate(const gobject& parent);
gobject instantiate(const node_iptr& parent); gobject instantiate(const node_iptr& parent);
gobject instantiate(const prefab& prefab, const gobject& parent);
gobject instantiate(const prefab& prefab, const node_iptr& parent); gobject instantiate(const prefab& prefab, const node_iptr& parent);
gobject instantiate(const gobject& parent, const t3f& transform);
gobject instantiate(const node_iptr& parent, const t3f& transform); gobject instantiate(const node_iptr& parent, const t3f& transform);
gobject instantiate(const prefab& prefab, const gobject& parent, const t3f& transform);
gobject instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform); gobject instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform);
void destroy_instance(gobject& inst) noexcept; void destroy_instance(gobject& inst) noexcept;
void finalize_instances() noexcept; void finalize_instances() noexcept;
gobject resolve(ecs::entity_id ent) const noexcept;
gobject resolve(const ecs::const_entity& ent) const noexcept;
private:
void destroy_instances_() noexcept;
void finalize_instance_(gobject& inst) noexcept;
private: private:
ecs::registry registry_; ecs::registry registry_;
hash_map<ecs::entity_id, gobject> gobjects_;
gobject_destroying_states destroying_states_; gobject_destroying_states destroying_states_;
}; };
} }

View File

@@ -103,7 +103,7 @@ namespace
the<world>().instantiate( the<world>().instantiate(
prefab, prefab,
scene_i, scene_i.component<actor>()->node(),
make_trs3(v3f{0,50.f,0}, q4f::identity(), v3f{20.f})); make_trs3(v3f{0,50.f,0}, q4f::identity(), v3f{20.f}));
} }
@@ -117,7 +117,7 @@ namespace
the<world>().instantiate( the<world>().instantiate(
prefab, prefab,
scene_i, scene_i.component<actor>()->node(),
math::make_translation_trs3(v3f{0,-50.f,0})); math::make_translation_trs3(v3f{0,-50.f,0}));
} }
@@ -139,7 +139,10 @@ namespace
{-80.f + j * 40.f, -200.f + i * 40.f, 0}, {-80.f + j * 40.f, -200.f + i * 40.f, 0},
q4f::identity(), q4f::identity(),
{2.f,2.f,1.f}}; {2.f,2.f,1.f}};
gobject inst = the<world>().instantiate(prefab_a, scene_i, trans); gobject inst = the<world>().instantiate(
prefab_a,
scene_i.component<actor>()->node(),
trans);
prefab prefab_b = prefab_a; prefab prefab_b = prefab_a;
prefab_b.prototype() prefab_b.prototype()
@@ -149,7 +152,9 @@ namespace
q4f::identity(), q4f::identity(),
v3f{0.3f,0.3f,3.f}))); v3f{0.3f,0.3f,3.f})));
the<world>().instantiate(prefab_b, inst); the<world>().instantiate(
prefab_b,
inst.component<actor>()->node());
} }
} }

View File

@@ -6,6 +6,8 @@
#include <enduro2d/high/components/behaviour.hpp> #include <enduro2d/high/components/behaviour.hpp>
#include <enduro2d/high/assets/script_asset.hpp>
namespace e2d namespace e2d
{ {
const char* factory_loader<behaviour>::schema_source = R"json({ const char* factory_loader<behaviour>::schema_source = R"json({
@@ -24,6 +26,7 @@ namespace e2d
if ( ctx.root.HasMember("script") ) { if ( ctx.root.HasMember("script") ) {
auto script = ctx.dependencies.find_asset<script_asset>( auto script = ctx.dependencies.find_asset<script_asset>(
path::combine(ctx.parent_address, ctx.root["script"].GetString())); path::combine(ctx.parent_address, ctx.root["script"].GetString()));
if ( !script ) { if ( !script ) {
the<debug>().error("BEHAVIOUR: Dependency 'script' is not found:\n" the<debug>().error("BEHAVIOUR: Dependency 'script' is not found:\n"
"--> Parent address: %0\n" "--> Parent address: %0\n"
@@ -32,7 +35,23 @@ namespace e2d
ctx.root["script"].GetString()); ctx.root["script"].GetString());
return false; return false;
} }
component.script(script);
//TODO(BlackMat): thread safe?
sol::protected_function_result meta = script->content().call();
if ( !meta.valid() ) {
the<debug>().error("BEHAVIOUR: Behaviour script error:\n"
"--> Error: %0",
sol::error(meta).what());
return false;
}
if ( meta.get_type() != sol::type::table ) {
the<debug>().error("BEHAVIOUR: Behaviour script must return the meta table");
return false;
}
component.meta(std::move(meta));
} }
return true; return true;

View File

@@ -7,6 +7,8 @@
#include <enduro2d/high/world.hpp> #include <enduro2d/high/world.hpp>
#include <enduro2d/high/components/actor.hpp> #include <enduro2d/high/components/actor.hpp>
#include <enduro2d/high/components/behaviour.hpp>
#include <enduro2d/high/components/disabled.hpp>
namespace namespace
{ {
@@ -60,12 +62,104 @@ namespace
}; };
} }
namespace
{
using namespace e2d;
void delete_instance(const gobject& inst) noexcept {
gcomponent<actor> inst_a{inst};
auto inst_n = inst_a ? inst_a->node() : nullptr;
if ( inst_n ) {
inst_n->for_each_child([](const node_iptr& child_n){
delete_instance(child_n->owner());
});
}
if ( inst ) {
auto inst_g = dynamic_pointer_cast<gobject_state>(inst.internal_state());
inst_g->raw_entity().destroy();
inst_g->mark_destroyed();
inst_g->mark_invalided();
}
}
gobject new_instance(world& world, const prefab& prefab) {
gobject inst;
ecs::entity ent = world.registry().create_entity(prefab.prototype());
try {
inst = gobject{make_intrusive<gobject_state>(world, ent)};
} catch (...) {
ent.destroy();
throw;
}
try {
auto n = node::create(inst);
gcomponent<actor> inst_a{inst};
if ( inst_a && inst_a->node() ) {
n->transform(inst_a->node()->transform());
}
inst_a.assign(std::move(n));
} catch (...) {
delete_instance(inst);
throw;
}
try {
for ( const auto& child_prefab : prefab.children() ) {
auto child = new_instance(world, child_prefab);
try {
gcomponent<actor> inst_a{inst};
gcomponent<actor> child_a{child};
inst_a->node()->add_child(child_a->node());
} catch (...) {
delete_instance(child);
throw;
}
}
} catch (...) {
delete_instance(inst);
throw;
}
return inst;
}
void shutdown_instance(gobject& inst) noexcept {
if ( gcomponent<actor> inst_a{inst}; inst_a ) {
nodes::for_extracted_nodes(inst_a->node(), [](const node_iptr& node){
if ( gcomponent<behaviour> inst_b{node->owner()}; inst_b ) {
behaviours::call_meta_method(
*inst_b,
"on_shutdown",
node->owner());
}
});
}
}
void start_instance(gobject& inst) {
if ( gcomponent<actor> inst_a{inst}; inst_a ) {
nodes::for_extracted_nodes(inst_a->node(), [&inst](const node_iptr& node){
if ( gcomponent<behaviour> inst_b{node->owner()}; inst_b ) {
const auto result = behaviours::call_meta_method(
*inst_b,
"on_start",
node->owner());
if ( result == behaviours::call_result::failed ) {
inst.component<disabled<behaviour>>().assign();
}
}
});
}
}
}
namespace e2d namespace e2d
{ {
world::~world() noexcept { world::~world() noexcept = default;
destroy_instances_();
finalize_instances();
}
ecs::registry& world::registry() noexcept { ecs::registry& world::registry() noexcept {
return registry_; return registry_;
@@ -76,157 +170,61 @@ namespace e2d
} }
gobject world::instantiate() { gobject world::instantiate() {
gobject inst{make_intrusive<gobject_state>( return instantiate(nullptr);
*this,
registry_.create_entity())};
gobjects_.emplace(inst.raw_entity().id(), inst);
try {
auto n = node::create(inst);
gcomponent<actor> inst_a{inst};
if ( inst_a && inst_a->node() ) {
n->transform(inst_a->node()->transform());
}
inst_a.assign(n);
} catch (...) {
finalize_instance_(inst);
throw;
}
return inst;
} }
gobject world::instantiate(const prefab& prefab) { gobject world::instantiate(const prefab& prefab) {
gobject inst{make_intrusive<gobject_state>( return instantiate(prefab, nullptr);
*this, }
registry_.create_entity(prefab.prototype()))};
gobjects_.emplace(inst.raw_entity().id(), inst);
try { gobject world::instantiate(const node_iptr& parent) {
auto n = node::create(inst); return instantiate(prefab(), parent);
gcomponent<actor> inst_a{inst}; }
if ( inst_a && inst_a->node() ) {
n->transform(inst_a->node()->transform()); gobject world::instantiate(const prefab& prefab, const node_iptr& parent) {
} gobject inst = new_instance(*this, prefab);
inst_a.assign(n);
} catch (...) { if ( parent ) {
finalize_instance_(inst); parent->add_child(inst.component<actor>()->node());
throw;
} }
try { try {
for ( const auto& child_prefab : prefab.children() ) { start_instance(inst);
auto child = instantiate(child_prefab);
try {
gcomponent<actor> inst_a{inst};
gcomponent<actor> child_a{child};
inst_a->node()->add_child(child_a->node());
} catch (...) {
finalize_instance_(child);
throw;
}
}
} catch (...) { } catch (...) {
finalize_instance_(inst); delete_instance(inst);
throw; throw;
} }
return inst; return inst;
} }
gobject world::instantiate(const gobject& parent) {
gobject go = instantiate();
if ( go && parent ) {
try {
gcomponent<actor> parent_a{parent};
if ( !parent_a ) {
parent_a.assign(node::create(parent));
}
if ( !parent_a->node() ) {
parent_a->node(node::create(parent));
}
parent_a->node()->add_child(gcomponent<actor>{go}->node());
} catch (...) {
finalize_instance_(go);
throw;
}
}
return go;
}
gobject world::instantiate(const node_iptr& parent) {
gobject go = instantiate();
if ( go && parent ) {
parent->add_child(gcomponent<actor>{go}->node());
}
return go;
}
gobject world::instantiate(const prefab& prefab, const gobject& parent) {
gobject go = instantiate(prefab);
if ( go && parent ) {
try {
gcomponent<actor> parent_a{parent};
if ( !parent_a ) {
parent_a.assign(node::create(parent));
}
if ( !parent_a->node() ) {
parent_a->node(node::create(parent));
}
parent_a->node()->add_child(gcomponent<actor>{go}->node());
} catch (...) {
finalize_instance_(go);
throw;
}
}
return go;
}
gobject world::instantiate(const prefab& prefab, const node_iptr& parent) {
gobject go = instantiate(prefab);
if ( go && parent ) {
parent->add_child(gcomponent<actor>{go}->node());
}
return go;
}
gobject world::instantiate(const gobject& parent, const t3f& transform) {
gobject go = instantiate(parent);
if ( go ) {
gcomponent<actor>{go}->node()->transform(transform);
}
return go;
}
gobject world::instantiate(const node_iptr& parent, const t3f& transform) { gobject world::instantiate(const node_iptr& parent, const t3f& transform) {
gobject go = instantiate(parent); return instantiate(prefab(), parent, transform);
if ( go ) {
gcomponent<actor>{go}->node()->transform(transform);
}
return go;
}
gobject world::instantiate(const prefab& prefab, const gobject& parent, const t3f& transform) {
gobject go = instantiate(prefab, parent);
if ( go ) {
gcomponent<actor>{go}->node()->transform(transform);
}
return go;
} }
gobject world::instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform) { gobject world::instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform) {
gobject go = instantiate(prefab, parent); gobject inst = new_instance(*this, prefab);
if ( go ) { inst.component<actor>()->node()->transform(transform);
gcomponent<actor>{go}->node()->transform(transform);
if ( parent ) {
parent->add_child(inst.component<actor>()->node());
} }
return go;
try {
start_instance(inst);
} catch (...) {
delete_instance(inst);
throw;
}
return inst;
} }
void world::destroy_instance(gobject& inst) noexcept { void world::destroy_instance(gobject& inst) noexcept {
auto gstate = inst auto gstate = inst
? dynamic_pointer_cast<gobject_state>(inst.internal_state()) ? dynamic_pointer_cast<gobject_state>(inst.internal_state())
: nullptr; : nullptr;
if ( gstate && !gstate->destroyed() ) { if ( gstate && !gstate->destroyed() && !gstate->invalided() ) {
gstate->mark_destroyed(); gstate->mark_destroyed();
destroying_states_.push_back(*gstate); destroying_states_.push_back(*gstate);
} }
@@ -236,46 +234,8 @@ namespace e2d
while ( !destroying_states_.empty() ) { while ( !destroying_states_.empty() ) {
gobject inst{&destroying_states_.front()}; gobject inst{&destroying_states_.front()};
destroying_states_.pop_front(); destroying_states_.pop_front();
finalize_instance_(inst); shutdown_instance(inst);
} delete_instance(inst);
}
gobject world::resolve(ecs::entity_id ent) const noexcept {
E2D_ASSERT(registry_.valid_entity(ent));
const auto iter = gobjects_.find(ent);
return iter != gobjects_.end()
? iter->second
: gobject();
}
gobject world::resolve(const ecs::const_entity& ent) const noexcept {
E2D_ASSERT(registry_.valid_entity(ent));
const auto iter = gobjects_.find(ent.id());
return iter != gobjects_.end()
? iter->second
: gobject();
}
void world::destroy_instances_() noexcept {
for ( auto& [_, go] : gobjects_ ) {
destroy_instance(go);
}
}
void world::finalize_instance_(gobject& inst) noexcept {
gcomponent<actor> inst_a{inst};
auto inst_n = inst_a ? inst_a->node() : nullptr;
if ( inst_n ) {
inst_n->for_each_child([this](const node_iptr& child_n){
destroy_instance(child_n->owner());
});
}
if ( inst ) {
inst.raw_entity().destroy();
gobjects_.erase(inst.raw_entity().id());
dynamic_pointer_cast<gobject_state>(inst.internal_state())->mark_invalided();
} }
} }
} }

View File

@@ -31,20 +31,20 @@ TEST_CASE("luasol") {
SECTION("vec2/vec3/vec4") { SECTION("vec2/vec3/vec4") {
v2f r0 = l.with_state([](sol::state& lua){ v2f r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local v = v2f.new(1,2) local v = e2d.v2f.new(1,2)
return v2f.new((v + v + 2).y) return e2d.v2f.new((v + v + 2).y)
)lua"); )lua");
}); });
v3f r1 = l.with_state([](sol::state& lua){ v3f r1 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local v = v3f.new(1,2,3) local v = e2d.v3f.new(1,2,3)
return v3f.new((v + v + 2).y) return e2d.v3f.new((v + v + 2).y)
)lua"); )lua");
}); });
v4f r2 = l.with_state([](sol::state& lua){ v4f r2 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local v = v4f.new(1,2,3,4) local v = e2d.v4f.new(1,2,3,4)
return v4f.new((v + v + 2).y) return e2d.v4f.new((v + v + 2).y)
)lua"); )lua");
}); });
REQUIRE(r0 == v2f(6)); REQUIRE(r0 == v2f(6));
@@ -55,7 +55,7 @@ TEST_CASE("luasol") {
SECTION("quat") { SECTION("quat") {
v3f r0 = l.with_state([](sol::state& lua){ v3f r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
return v3f.new(1,2,3) * q4f.make_quat_from_axis_angle(radf.new(10), v3f.new(1,2,3)) return e2d.v3f.new(1,2,3) * e2d.q4f.make_quat_from_axis_angle(e2d.radf.new(10), e2d.v3f.new(1,2,3))
)lua"); )lua");
}); });
REQUIRE(r0 == v3f(1,2,3) * math::make_quat_from_axis_angle(radf(10.f), v3f(1,2,3))); REQUIRE(r0 == v3f(1,2,3) * math::make_quat_from_axis_angle(radf(10.f), v3f(1,2,3)));
@@ -64,23 +64,23 @@ TEST_CASE("luasol") {
SECTION("mat2/mat2/mat3") { SECTION("mat2/mat2/mat3") {
std::pair<m2f, bool> r0 = l.with_state([](sol::state& lua){ std::pair<m2f, bool> r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local m = m2f.make_scale_matrix2(2,3) local m = e2d.m2f.make_scale_matrix2(2,3)
local rm, s = m2f.inversed(m) local rm, s = e2d.m2f.inversed(m)
return rm * m2f.identity(), s return rm * e2d.m2f.identity(), s
)lua"); )lua");
}); });
std::pair<m3f, bool> r1 = l.with_state([](sol::state& lua){ std::pair<m3f, bool> r1 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local m = m3f.make_rotation_matrix3(degf.new(45),2,3,4) local m = e2d.m3f.make_rotation_matrix3(e2d.degf.new(45),2,3,4)
local rm, s = m3f.inversed(m) local rm, s = e2d.m3f.inversed(m)
return rm * m3f.identity(), s return rm * e2d.m3f.identity(), s
)lua"); )lua");
}); });
std::pair<m4f, bool> r2 = l.with_state([](sol::state& lua){ std::pair<m4f, bool> r2 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local m = m4f.make_translation_matrix4(2,3,4) local m = e2d.m4f.make_translation_matrix4(2,3,4)
local rm, s = m4f.inversed(m) local rm, s = e2d.m4f.inversed(m)
return rm * m4f.identity(), s return rm * e2d.m4f.identity(), s
)lua"); )lua");
}); });
REQUIRE(r0.second); REQUIRE(r0.second);
@@ -94,15 +94,15 @@ TEST_CASE("luasol") {
SECTION("rect/aabb") { SECTION("rect/aabb") {
bool r0 = l.with_state([](sol::state& lua){ bool r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local b = b2f.unit() * 2 local b = e2d.b2f.unit() * 2
return b:inside(v2f.new(1.5,1.5)) return b:inside(e2d.v2f.new(1.5,1.5))
)lua"); )lua");
}); });
REQUIRE(r0); REQUIRE(r0);
bool r1 = l.with_state([](sol::state& lua){ bool r1 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local b = b3f.unit() * 2 local b = e2d.b3f.unit() * 2
return b:overlaps(b3f.new(1.5,1.5,1.5,2,2,2)) return b:overlaps(e2d.b3f.new(1.5,1.5,1.5,2,2,2))
)lua"); )lua");
}); });
REQUIRE(r1); REQUIRE(r1);
@@ -111,14 +111,14 @@ TEST_CASE("luasol") {
SECTION("trs2/trs3") { SECTION("trs2/trs3") {
radf r0 = l.with_state([](sol::state& lua){ radf r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local t = t2f.make_rotation_trs2(degf.new(45)) local t = e2d.t2f.make_rotation_trs2(e2d.degf.new(45))
return t.rotation return t.rotation
)lua"); )lua");
}); });
REQUIRE(r0 == math::to_rad(degf(45.f))); REQUIRE(r0 == math::to_rad(degf(45.f)));
v3f r1 = l.with_state([](sol::state& lua){ v3f r1 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
local t = t3f.make_translation_trs3(v3f.new(1,2,3)) local t = e2d.t3f.make_translation_trs3(e2d.v3f.new(1,2,3))
return t.translation return t.translation
)lua"); )lua");
}); });
@@ -128,13 +128,13 @@ TEST_CASE("luasol") {
SECTION("color/color32") { SECTION("color/color32") {
color r0 = l.with_state([](sol::state& lua){ color r0 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
return color.white() * 0.5 return e2d.color.white() * 0.5
)lua"); )lua");
}); });
REQUIRE(r0 == color(0.5f,0.5f,0.5f,0.5f)); REQUIRE(r0 == color(0.5f,0.5f,0.5f,0.5f));
color32 r1 = l.with_state([](sol::state& lua){ color32 r1 = l.with_state([](sol::state& lua){
return lua.script(R"lua( return lua.script(R"lua(
return color32.white() - 1 return e2d.color32.white() - 1
)lua"); )lua");
}); });
REQUIRE(r1 == color32(254,254,254,254)); REQUIRE(r1 == color32(254,254,254,254));

View File

@@ -617,13 +617,13 @@ TEST_CASE("node") {
const vector<node_iptr> ns{p, c1, c2, c3}; const vector<node_iptr> ns{p, c1, c2, c3};
{ {
vector<node_iptr> ns2; vector<node_iptr> ns2;
REQUIRE(4u == p->extract_all_nodes(std::back_inserter(ns2))); REQUIRE(4u == nodes::extract_nodes(p, std::back_inserter(ns2)));
REQUIRE(ns == ns2); REQUIRE(ns == ns2);
} }
{ {
const_node_iptr cp = p; const_node_iptr cp = p;
vector<const_node_iptr> ns2; vector<const_node_iptr> ns2;
REQUIRE(4u == cp->extract_all_nodes(std::back_inserter(ns2))); REQUIRE(4u == nodes::extract_nodes(cp, std::back_inserter(ns2)));
REQUIRE(ns.size() == ns2.size()); REQUIRE(ns.size() == ns2.size());
for ( std::size_t i = 0; i < ns.size(); ++i ) { for ( std::size_t i = 0; i < ns.size(); ++i ) {
REQUIRE(ns[i] == ns2[i]); REQUIRE(ns[i] == ns2[i]);