aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/target.hxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-04-20 10:55:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-04-20 10:55:50 +0200
commit9b138ccbebdcdc6bfdd6f6d52e534ae14df280af (patch)
tree04142d3c54864d2f18605b8a544d79e6ec40a9ae /libbuild2/target.hxx
parent1b8c19b427f7e0ec6a747df60eb9334a620843b7 (diff)
Replace match_extra::buffer with more general data storage facility
Diffstat (limited to 'libbuild2/target.hxx')
-rw-r--r--libbuild2/target.hxx68
1 files changed, 66 insertions, 2 deletions
diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx
index 563c264..bf8c4fe 100644
--- a/libbuild2/target.hxx
+++ b/libbuild2/target.hxx
@@ -145,8 +145,70 @@ namespace build2
//
struct match_extra
{
- bool fallback; // True if matching a fallback rule (see match_rule()).
- string buffer; // Auxiliary buffer that's reused during match/apply.
+ bool fallback; // True if matching a fallback rule (see match_rule()).
+
+ // Auxiliary data storage.
+ //
+ // A rule (whether matches or not) may use this pad to pass data between
+ // its match and apply functions (but not the recipe). The rule should
+ // static assert that the size of the pad is sufficient for its needs.
+ //
+ // This facility is complementary to the auxiliary data storage in target:
+ // it can store slightly more/extra data without dynamic memory allocation
+ // but can only be used during match/apply.
+ //
+ // 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 storage.
+ //
+ static constexpr size_t data_size = (sizeof (string) > sizeof (void*) * 4
+ ? sizeof (string)
+ : sizeof (void*) * 4);
+
+ std::aligned_storage<data_size>::type data_;
+ void (*data_dtor_) (void*) = nullptr;
+
+ 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);
+ clear_data ();
+ return *new (&data_) 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);
+ clear_data ();
+ T& r (*new (&data_) 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_);}
+
+ template <typename T>
+ const T&
+ data () const {return *reinterpret_cast<const T*> (&data_);}
+
+ void
+ clear_data ()
+ {
+ if (data_dtor_ != nullptr)
+ {
+ data_dtor_ (&data_);
+ data_dtor_ = nullptr;
+ }
+ }
// Implementation details.
//
@@ -840,6 +902,8 @@ namespace build2
// }
// };
//
+ // Note: see also similar facility in match_extra.
+ //
// 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