From 9034f7c51ef6437ce9d4547ba5bde217b4740fb2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 19 Apr 2022 11:10:53 +0200 Subject: Use target recipe for auxiliary data storage during match-apply In particular, we now have separate auxiliary data storage for inner and outer operations. --- libbuild2/target.hxx | 133 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 41 deletions(-) (limited to 'libbuild2/target.hxx') diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx index e3a64ca..563c264 100644 --- a/libbuild2/target.hxx +++ b/libbuild2/target.hxx @@ -625,8 +625,13 @@ namespace build2 // Applied recipe. // - build2::recipe recipe; - bool recipe_group_action; // Recipe is group_action. + // Note: also used as the auxiliary data storage during match, which is + // why mutable (see the target::data() API below for details). The + // default recipe_keep value is set by clear_target(). + // + mutable build2::recipe recipe; + mutable bool recipe_keep; // Keep after execution. + bool recipe_group_action; // Recipe is group_action. // Target state for this operation. Note that it is undetermined until // a rule is matched and recipe applied (see set_recipe()). @@ -640,8 +645,8 @@ namespace build2 // no iffy modifications of the group's variables by member's rules). // // They are also automatically cleared before another rule is matched, - // similar to the data pad. In other words, rule-specific variables are - // only valid for this match-execute phase. + // similar to the auxiliary data storage. In other words, rule-specific + // variables are only valid for this match-execute phase. // variable_map vars; @@ -785,30 +790,63 @@ namespace build2 // mutable action_state prerequisite_targets; - // Auxilary data storage. + // Auxiliary data storage. // // 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. The - // rule may static assert that the small size of the pad (which doesn't - // require dynamic memory allocation) 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. @@ TMP - // - // 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. - // - // The data is not destroyed until the next match, which is relied upon to - // communicate between rules for inner/outer operations. @@ TMP - // - // Note that the recipe may modify the data. Currently reserved for the - // inner part of the action. @@ TMP - // - // See also match_extra::buffer. + // use this facility to pass data between its match and apply functions as + // well as the recipe. Specifically, between match() and apply() the data + // is stored in the recipe member (which is std::move_only_function-like). + // If the data needs to be passed on to the recipe, then it must become + // the recipe itself. Here is a typical arrangement: + // + // class compile_rule + // { + // struct match_data + // { + // ... // Data. + // + // const compile_rule& rule; + // + // target_state + // operator() (action a, const target& t) + // { + // return rule.perform_update (a, t, this); + // } + // }; + // + // virtual bool + // match (action a, const target& t) + // { + // ... // Determine if matching. + // + // t.data (a, match_data {..., *this}); + // return true; + // } + // + // virtual bool + // apply (action a, target& t) + // { + // match_data& md (t.data (a)); + // + // ... // Match prerequisites, etc. + // + // return move (md); // Data becomes the recipe. + // } + // + // target_state + // perform_update (action a, const target& t, match_data& md) const + // { + // ... // Access data (also available as t.data (a)). + // } + // }; + // + // After the recipe is executed, the recipe/data is destroyed, unless + // explicitly requested not to (see below). The rule may static assert + // that the small size of the storage (which doesn't require dynamic + // memory allocation) is sufficient for its needs. + // + // Note also that a rule that delegates to another rule may need to store + // the base rule's data/recipe in its own data/recipe. // Provide the small object optimization size for the common compilers // (see recipe.hxx for details) in case a rule wants to make sure its data @@ -825,14 +863,10 @@ namespace build2 #elif defined(_MSC_VER) sizeof (void*) * 6 #else - // Assume at least 2 pointers. - // - sizeof (void*) * 2 + sizeof (void*) * 2 // Assume at least 2 pointers. #endif ; - mutable recipe data_pad; - template struct data_wrapper { @@ -857,43 +891,60 @@ namespace build2 template typename std::enable_if::value, void>::type - data (T&& d) const + data (action a, T&& d) const { using V = typename std::remove_cv< typename std::remove_reference::type>::type; - data_pad = data_wrapper {forward (d)}; + const opstate& s (state[a]); + s.recipe = data_wrapper {forward (d)}; + s.recipe_keep = false; // Can't keep non-recipe data. } template typename std::enable_if::value, T&>::type& - data () const + data (action a) const { using V = typename std::remove_cv::type; - return data_pad.target> ()->d; + return state[a].recipe.target> ()->d; } // Note that in this case we don't strip const (the expectation is that we // move the recipe in/out of data). // + // If keep is true, then keep the recipe as data after execution. In + // particular, this can be used to communicate between inner/outer rules + // (see cc::install_rule for an example). + // + // template typename std::enable_if::value, void>::type - data (T&& d) const + data (action a, T&& d, bool keep = false) const + { + const opstate& s (state[a]); + s.recipe = forward (d); + s.recipe_keep = keep; + } + + void + keep_data (action a, bool keep = true) const { - data_pad = forward (d); + state[a].recipe_keep = keep; } template typename std::enable_if::value, T&>::type& - data () const + data (action a) const { - return *data_pad.target (); + return *state[a].recipe.target (); } void - clear_data () const + clear_data (action a) const { - data_pad = nullptr; + const opstate& s (state[a]); + s.recipe = nullptr; + s.recipe_keep = false; } // Target type info and casting. -- cgit v1.1