diff --git a/doc/08_revision_history.dox b/doc/08_revision_history.dox index 6b4586b0..f10e89a8 100644 --- a/doc/08_revision_history.dox +++ b/doc/08_revision_history.dox @@ -1,5 +1,18 @@ /*! \page revisions Revision History +DynaMix 1.2.1 +============= + +Released: 2017 Oct 15 + +- Improved mutation rules: They can now be added, removed and persisted via +shared pointers +- Added `object::has` and `object::get` with `const char*` to query mixins by +name +- `add_new_mutation_rule` is now deprecated +- Fixed library version header + + DynaMix 1.2.0 ============= diff --git a/doc/Doxyfile b/doc/Doxyfile index fdbb4d3f..0f9d415d 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -6,7 +6,7 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "DynaMix" -PROJECT_NUMBER = 1.2.0 +PROJECT_NUMBER = 1.2.1 PROJECT_BRIEF = "A new take on polymorphism in C++" # TODO: (max size 200x55) diff --git a/example/basic/object_manager.cpp b/example/basic/object_manager.cpp index b8010fa2..5f843a30 100644 --- a/example/basic/object_manager.cpp +++ b/example/basic/object_manager.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -31,7 +31,7 @@ void object_manager::create_objects() rendering_mixins->add(); rendering_mixins->add(); - add_new_mutation_rule(rendering_mixins); + add_mutation_rule(rendering_mixins); object_type_template type; type diff --git a/include/dynamix/domain.hpp b/include/dynamix/domain.hpp index b9f082ba..f1b4f8b5 100644 --- a/include/dynamix/domain.hpp +++ b/include/dynamix/domain.hpp @@ -15,13 +15,13 @@ #include #include +#include #include // alignment of #if DYNAMIX_THREAD_SAFE_MUTATIONS #include #endif - /** * \file * Domain related classes and functions. @@ -52,7 +52,9 @@ class DYNAMIX_API domain : public noncopyable // no static variables, not safe to call globally static const domain& instance(); - void add_new_mutation_rule(mutation_rule* rule); + mutation_rule_id add_mutation_rule(std::shared_ptr rule); + mutation_rule_id add_mutation_rule(mutation_rule* rule); + std::shared_ptr remove_mutation_rule(mutation_rule_id id); void apply_mutation_rules(object_type_mutation& mutation); size_t num_registered_mixins() const { return _num_registered_mixins; } @@ -144,9 +146,13 @@ class DYNAMIX_API domain : public noncopyable typedef std::unordered_map object_type_info_map; object_type_info_map _object_type_infos; - std::mutex _object_type_infos_mutex; - std::vector _mutation_rules; + std::vector> _mutation_rules; + +#if DYNAMIX_THREAD_SAFE_MUTATIONS + std::mutex _object_type_infos_mutex; + std::mutex _mutation_rules_mutex; +#endif // feature registration functions for the supported kinds of features void internal_register_feature(message_t& m); diff --git a/include/dynamix/global.hpp b/include/dynamix/global.hpp index 999c9972..d5368b68 100644 --- a/include/dynamix/global.hpp +++ b/include/dynamix/global.hpp @@ -54,6 +54,7 @@ namespace dynamix { typedef size_t mixin_id; typedef size_t feature_id; + typedef size_t mutation_rule_id; namespace internal { diff --git a/include/dynamix/mutation_rule.hpp b/include/dynamix/mutation_rule.hpp index dff5deb6..c29723e8 100644 --- a/include/dynamix/mutation_rule.hpp +++ b/include/dynamix/mutation_rule.hpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -15,6 +15,8 @@ #include "global.hpp" #include "mixin_collection.hpp" +#include + namespace dynamix { @@ -30,10 +32,33 @@ class DYNAMIX_API mutation_rule virtual void apply_to(object_type_mutation& mutation) = 0; }; +/// DEPRECATED: +/// Adds a mutation rule to the domain. +/// Takes ownership of the pointer and assumes it's created with `new`. +/// Returns the mutation rule id by whitch it can be removed. +/// +/// Does *not* perform a topological sort of the rules. +/// It is the user's responsibility to add the mutation rules in the appropriate order. +mutation_rule_id DYNAMIX_API add_new_mutation_rule(mutation_rule* rule); + /// Adds a mutation rule to the domain. +/// Takes ownership of the pointer and assumes it's created with `new`. +/// Returns the mutation rule id by whitch it can be removed. /// /// Does *not* perform a topological sort of the rules. /// It is the user's responsibility to add the mutation rules in the appropriate order. -void DYNAMIX_API add_new_mutation_rule(mutation_rule* rule); +mutation_rule_id DYNAMIX_API add_mutation_rule(mutation_rule* rule); + +/// Adds a mutation rule to the domain via a shared pointer. +/// Returns the mutation rule id by whitch it can be removed. +/// +/// Does *not* perform a topological sort of the rules. +/// It is the user's responsibility to add the mutation rules in the appropriate order. +mutation_rule_id DYNAMIX_API add_mutation_rule(std::shared_ptr rule); + +/// Removes a mutation rule from the domain by id. +/// Returns a shared pointer to the rule which the user might use to persist it, or null +/// if no such mutation rule exists +std::shared_ptr DYNAMIX_API remove_mutation_rule(mutation_rule_id id); } // namespace dynamix diff --git a/include/dynamix/version.hpp b/include/dynamix/version.hpp index b372d936..64089109 100644 --- a/include/dynamix/version.hpp +++ b/include/dynamix/version.hpp @@ -13,8 +13,8 @@ */ #define DYNAMIX_VERSION_MAJOR 1 -#define DYNAMIX_VERSION_MINOR 1 -#define DYNAMIX_VERSION_SUB_MINOR 0 +#define DYNAMIX_VERSION_MINOR 2 +#define DYNAMIX_VERSION_SUB_MINOR 1 /// The library's version. /// diff --git a/src/domain.cpp b/src/domain.cpp index 6e41d2b8..60b4befb 100644 --- a/src/domain.cpp +++ b/src/domain.cpp @@ -48,23 +48,60 @@ domain::~domain() { delete i.second; } +} + +mutation_rule_id domain::add_mutation_rule(mutation_rule* rule) +{ + std::shared_ptr ptr(rule); + return add_mutation_rule(ptr); +} + +mutation_rule_id domain::add_mutation_rule(std::shared_ptr rule) +{ +#if DYNAMIX_THREAD_SAFE_MUTATIONS + std::lock_guard lock(_mutation_rules_mutex); +#endif - for (const mutation_rule* rule : _mutation_rules) + // find free slot + for (mutation_rule_id i = 0; i < _mutation_rules.size(); ++i) { - delete rule; + auto& r = _mutation_rules[i]; + if (!r) + { + r = rule; + return i; + } } + + _mutation_rules.emplace_back(std::move(rule)); + return _mutation_rules.size() - 1; } -void domain::add_new_mutation_rule(mutation_rule* rule) +std::shared_ptr domain::remove_mutation_rule(mutation_rule_id id) { - _mutation_rules.push_back(rule); +#if DYNAMIX_THREAD_SAFE_MUTATIONS + std::lock_guard lock(_mutation_rules_mutex); +#endif + + if (id >= _mutation_rules.size()) return std::shared_ptr(); + + auto ret = _mutation_rules[id]; + _mutation_rules[id].reset(); + return ret; } void domain::apply_mutation_rules(object_type_mutation& mutation) { - for (mutation_rule* rule : _mutation_rules) +#if DYNAMIX_THREAD_SAFE_MUTATIONS + std::lock_guard lock(_mutation_rules_mutex); +#endif + + for (auto& rule : _mutation_rules) { - rule->apply_to(mutation); + if (rule) + { + rule->apply_to(mutation); + } } } @@ -309,9 +346,24 @@ mixin_id domain::get_mixin_id_by_name(const char* mixin_name) const } // namespace internal -void add_new_mutation_rule(mutation_rule* rule) +mutation_rule_id add_new_mutation_rule(mutation_rule* rule) +{ + return internal::domain::safe_instance().add_mutation_rule(rule); +} + +mutation_rule_id add_mutation_rule(mutation_rule* rule) +{ + return internal::domain::safe_instance().add_mutation_rule(rule); +} + +mutation_rule_id add_mutation_rule(std::shared_ptr rule) +{ + return internal::domain::safe_instance().add_mutation_rule(rule); +} + +std::shared_ptr remove_mutation_rule(mutation_rule_id id) { - internal::domain::safe_instance().add_new_mutation_rule(rule); + return internal::domain::safe_instance().remove_mutation_rule(id); } // set allocator to all domains diff --git a/test/mutation_rules/bundled.cpp b/test/mutation_rules/bundled.cpp index bb8b5ccd..6c476df4 100644 --- a/test/mutation_rules/bundled.cpp +++ b/test/mutation_rules/bundled.cpp @@ -21,7 +21,7 @@ TEST_CASE("bundled") auto bundle = new bundled_mixins; bundle->add(); bundle->add(); - add_new_mutation_rule(bundle); + add_mutation_rule(bundle); object o; diff --git a/test/mutation_rules/custom.cpp b/test/mutation_rules/custom.cpp index f66b0fe4..1c4b0729 100644 --- a/test/mutation_rules/custom.cpp +++ b/test/mutation_rules/custom.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -35,7 +35,9 @@ TEST_SUITE("mutation rules"); TEST_CASE("mutation rules") { - add_new_mutation_rule(new custom_rule()); + auto id = add_mutation_rule(new custom_rule()); + + CHECK(id == 0); object o; @@ -53,4 +55,45 @@ TEST_CASE("mutation rules") CHECK(!o.has()); CHECK(!o.has()); CHECK(o.has()); + + auto rule = remove_mutation_rule(id); + + mutate(o) + .add(); + + CHECK(o.has()); + CHECK(!o.has()); + CHECK(o.has()); + + mutate(o) + .add(); + + CHECK(o.has()); + CHECK(o.has()); + CHECK(o.has()); + + id = add_mutation_rule(rule); + CHECK(id == 0); + + auto depr = std::make_shared>(); + id = add_mutation_rule(depr); + CHECK(id == 1); + + mutate(o) + .remove(); + + CHECK(o.empty()); + + CHECK(rule == remove_mutation_rule(0)); + + mutate(o) + .add() + .add(); + + CHECK(o.has()); + CHECK(!o.has()); + CHECK(!o.has()); + + rule.reset(); + CHECK(rule == remove_mutation_rule(123)); } diff --git a/test/mutation_rules/dependent.cpp b/test/mutation_rules/dependent.cpp index fa360c3b..c09cf52c 100644 --- a/test/mutation_rules/dependent.cpp +++ b/test/mutation_rules/dependent.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -21,7 +21,7 @@ TEST_CASE("dependent") auto rule = new dependent_mixins; rule->set_master(); rule->add(); - add_new_mutation_rule(rule); + add_mutation_rule(rule); object o; diff --git a/test/mutation_rules/deprecated.cpp b/test/mutation_rules/deprecated.cpp index d9e767ab..7e55e422 100644 --- a/test/mutation_rules/deprecated.cpp +++ b/test/mutation_rules/deprecated.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -18,7 +18,7 @@ TEST_SUITE("mutation rules"); TEST_CASE("deprecated") { - add_new_mutation_rule(new deprecated_mixin()); + add_mutation_rule(new deprecated_mixin()); object o; diff --git a/test/mutation_rules/mandatory.cpp b/test/mutation_rules/mandatory.cpp index fc61f24c..8f0357ab 100644 --- a/test/mutation_rules/mandatory.cpp +++ b/test/mutation_rules/mandatory.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -18,7 +18,7 @@ TEST_SUITE("mutation rules"); TEST_CASE("mandatory") { - add_new_mutation_rule(new mandatory_mixin()); + add_mutation_rule(new mandatory_mixin()); object o; diff --git a/test/mutation_rules/mutually_exclusive.cpp b/test/mutation_rules/mutually_exclusive.cpp index c60c079d..1c4897c2 100644 --- a/test/mutation_rules/mutually_exclusive.cpp +++ b/test/mutation_rules/mutually_exclusive.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -23,7 +23,7 @@ TEST_CASE("mutually exclusive") rule->add(); rule->add(); - add_new_mutation_rule(rule); + add_mutation_rule(rule); object o; diff --git a/test/mutation_rules/substitute.cpp b/test/mutation_rules/substitute.cpp index 26b31494..15ddd120 100644 --- a/test/mutation_rules/substitute.cpp +++ b/test/mutation_rules/substitute.cpp @@ -1,5 +1,5 @@ // DynaMix -// Copyright (c) 2013-2016 Borislav Stanimirov, Zahary Karadjov +// Copyright (c) 2013-2017 Borislav Stanimirov, Zahary Karadjov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -18,7 +18,7 @@ TEST_SUITE("mutation rules"); TEST_CASE("substitute") { - add_new_mutation_rule(new substitute_mixin()); + add_mutation_rule(new substitute_mixin()); object o; diff --git a/tutorial/mutation_rules.cpp b/tutorial/mutation_rules.cpp index 2473acd6..a7bf97a6 100644 --- a/tutorial/mutation_rules.cpp +++ b/tutorial/mutation_rules.cpp @@ -74,11 +74,11 @@ mixin `furniture`. That could be accomplished if we manually add it to all mutations we make but there is a simpler way to do it. By adding the `mandatory_mixin` mutation rule. -All mutation rules should be added by calling `add_new_mutation_rule`. Since +All mutation rules should be added by calling `add_mutation_rule`. Since `mandatory_mixin` is a mutation rule that the library provides, we can accomplish this with a single line of code: */ - dynamix::add_new_mutation_rule(new dynamix::mandatory_mixin); + dynamix::add_mutation_rule(new dynamix::mandatory_mixin); /*` Now each mutation after this line will add `furniture` to the objects (even if it's not been explicitly added) and also if a mutation tries to remove the `furniture` mixin @@ -102,14 +102,26 @@ prevent anybody from adding the mixin to an object. Basically the exact opposite of `mandatory_mixin`. This is the mutation rule `deprecated_mixin` */ - dynamix::add_new_mutation_rule(new dynamix::deprecated_mixin); + auto id = dynamix::add_mutation_rule(new dynamix::deprecated_mixin); + /*` After that line of code, any mutation that tries to add `ofml_serialization` won't be able to, and all mutations will try to remove it if it's present in an object. Again, as was the case before, if a mutation does many things, only the part from it, trying to add `ofml_serialization` will be -silently ignored. +silently ignored. Also, we will store the id of the newly added rule for the next example. + +Mutation rules are registered globally and they are ran on every mutation, inadvertedly +slowing it down. Sometimes you will encounter the need to add a mutation rule which is needed +or only makes sense for a limited amount of time. For example deprecating `ofml_serialization` +from the source line above might only be needed when loading objects and then our code +might never add it, rendering this rule useless for the majority of the program's run. In such +cases we can remove a rule with `remove_mutation_rule` like so: +*/ + dynamix::remove_mutation_rule(id); + +/*` The last built-in rule in the library is `mutually_exclusive_mixins`. Since a piece of furniture has either wood frame or a metal frame and never @@ -121,7 +133,7 @@ helps us do exactly that. dynamix::mutually_exclusive_mixins* rule = new dynamix::mutually_exclusive_mixins; rule->add(); rule->add(); - dynamix::add_new_mutation_rule(rule); + dynamix::add_mutation_rule(rule); /*` You may add as many mutually exclusive mixins as you wish. If you had, say, @@ -164,7 +176,7 @@ Two rules are affected by this mutation. First it will implicitly add The mutually exclusive mixins will ensure that after this line the object won't have the `wood_frame` mixin. -Having listed all built-in mutation rules, let's define a custom one. +Having listed some built-in mutation rules, let's now define a custom one. Defining a custom rule is very easy. All you need to do is create a class derived from `dynamix::mutation_rule` and override its pure virtual method @@ -199,7 +211,7 @@ is being removed and the object has doors. That's it. Now all we have to do is add our mutation rule and it will be active. */ - dynamix::add_new_mutation_rule(new container_rule); + dynamix::add_mutation_rule(new container_rule); dynamix::mutate(o) .add(); @@ -219,12 +231,14 @@ its `has_doors` mixin removed. To see all ways in which you can change a mutation from the mutation rule, check out the documentation entry on `object_type_mutation`. -Lastly, there are two important pieces of information about mutation rules +Lastly, there are three more important pieces of information about mutation rules that you need to know. -First, note that the library will be responsible for freeing the memory and -destroying the rules you've added. All you need to do is call -`add_new_mutation_rule` with a rule, allocated and constructed with `new`. +In these examples we always added mutation rule pointers, allocated with `new`. +In such case the library will take ownership of the pointer and will be +responsible for destroying and deallocating the rules you've added. However +you can also add mutation rules with `std::shared_ptr` and keep ownership even +after they are removed (and potentially readd them). Second, you may have noticed that mutation rules can logically depend on each other. You may ask yourselves what does the library do about that?