diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-10-14 14:59:35 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-11-04 08:29:23 +0200 |
commit | 2cccbe9150669f9df00f439b8db03054a97ed4bb (patch) | |
tree | abe55a7ddabb29e8ddbf287281a4675daff8462f | |
parent | d4a42cdeedabfc05ba4720852c4e924a7e28ef03 (diff) |
Implement auxiliary data storage pad in target
-rw-r--r-- | build2/bin/target | 2 | ||||
-rw-r--r-- | build2/bin/target.cxx | 2 | ||||
-rw-r--r-- | build2/target | 73 | ||||
-rw-r--r-- | build2/target.cxx | 7 |
4 files changed, 80 insertions, 4 deletions
diff --git a/build2/bin/target b/build2/bin/target index 849316e..0a324a5 100644 --- a/build2/bin/target +++ b/build2/bin/target @@ -101,7 +101,7 @@ namespace build2 libs* s {nullptr}; virtual void - reset (action_type); + reset (action_type) override; public: static const target_type static_type; diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx index d78d34c..f2ebd05 100644 --- a/build2/bin/target.cxx +++ b/build2/bin/target.cxx @@ -226,6 +226,8 @@ namespace build2 void lib:: reset (action_type) { + clear_data (); + // Don't clear prerequisite_targets since it is "given" to our // members to implement "library meta-information protocol". } diff --git a/build2/target b/build2/target index 8fc0e8c..3123905 100644 --- a/build2/target +++ b/build2/target @@ -6,7 +6,7 @@ #define BUILD2_TARGET #include <iterator> // tags, etc. -#include <type_traits> +#include <type_traits> // aligned_storage #include <unordered_map> #include <butl/multi-index> // map_iterator_adapter @@ -251,7 +251,7 @@ namespace build2 public: virtual - ~target () = default; + ~target (); target (const target&) = delete; target& operator= (const target&) = delete; @@ -260,7 +260,7 @@ namespace build2 : dir (move (d)), out (move (o)), name (move (n)), ext (e) {} // Reset the target before matching it to a rule. The default - // implementation clears prerequisite_targets. + // implementation clears the auxilary data and prerequisite_targets. // virtual void reset (action_type); @@ -457,6 +457,73 @@ namespace build2 // size_t dependents; + // Auxilary data storage. + // + public: + // A rule that matches (i.e., returns true from its match() function) may + // use this pad to pass data between its match and apply functions as well + // as the recipe. After the recipe is executed, the data is destroyed by + // calling data_dtor (if not NULL). The rule should static assert that the + // size of the pad is sufficient for its needs. + // + // Note also that normally at least 2 extra pointers may be stored without + // a dynamic allocation in the returned recipe (small object optimization + // in std::function). So if you need to pass data only between apply() and + // the recipe, then this might be a more convenient way. + // + // Note also that a rule that delegates to another rule may not be able to + // use this mechanism fully since the delegated-to rule may also need the + // data pad. + // + // Currenly the data is not destroyed until the next match. + // + std::aligned_storage<sizeof (void*) * 4>::type data_pad; + void (*data_dtor) (void*) = nullptr; + + // VC 14 needs decltype. + // + static const size_t data_size = sizeof (decltype (data_pad)); + + template <typename R, + typename T = typename std::remove_cv< + typename std::remove_reference<R>::type>::type> + typename std::enable_if<std::is_trivially_destructible<T>::value,T&>::type + data (R&& d) + { + assert (sizeof (T) <= data_size && data_dtor == nullptr); + return *new (&data_pad) T (forward<R> (d)); + } + + template <typename R, + typename T = typename std::remove_cv< + typename std::remove_reference<R>::type>::type> + typename std::enable_if<!std::is_trivially_destructible<T>::value,T&>::type + data (R&& d) + { + assert (sizeof (T) <= data_size && data_dtor == nullptr); + T& r (*new (&data_pad) T (forward<R> (d))); + data_dtor = [] (void* p) {static_cast<T*> (p)->~T ();}; + return r; + } + + template <typename T> + T& + data () {return *reinterpret_cast<T*> (&data_pad);} + + template <typename T> + const T& + data () const {return *reinterpret_cast<const T*> (&data_pad);} + + void + clear_data () + { + if (data_dtor != nullptr) + { + data_dtor (&data_pad); + data_dtor = nullptr; + } + } + public: action_type action; // Action this recipe is for. diff --git a/build2/target.cxx b/build2/target.cxx index cdecfd3..540f4b4 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -48,6 +48,12 @@ namespace build2 // target // + target:: + ~target () + { + clear_data (); + } + void target:: recipe (action_type a, recipe_type r) { @@ -87,6 +93,7 @@ namespace build2 void target:: reset (action_type) { + clear_data (); prerequisite_targets.clear (); } |