Lumiera  0.pre.03
»edit your freedom«
tree-mutator-collection-binding.hpp
Go to the documentation of this file.
1 /*
2  TREE-MUTATOR-COLLECTION-BINDING.hpp - diff::TreeMutator implementation building block
3 
4  Copyright (C)
5  2016, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
72 #ifndef LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
73 #define LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
74 
75 
76 #include "lib/error.hpp"
77 #include "lib/nocopy.hpp"
78 #include "lib/meta/trait.hpp"
79 #include "lib/diff/gen-node.hpp"
81 #include "lib/iter-adapter-stl.hpp"
82 
83 #include <utility>
84 #include <vector>
85 #include <map>
86 
87 
88 namespace lib {
89 namespace diff{
90 
91  namespace { // Mutator-Builder decorator components...
92 
93 
94  using std::forward;
95  using lib::meta::Strip;
96  using lib::diff::GenNode;
97  using lib::iter_stl::eachElm;
98 
99 
100  /* === Technicalities of container access === */
101 
102  template<class C>
103  using _AsVector = std::vector<typename C::value_type>;
104  template<class C>
105  using _AsMap = std::map<typename C::key_type, typename C::mapped_type>;
106 
107  template<class C>
108  using IF_is_vector = lib::meta::enable_if< std::is_base_of<_AsVector<C>, C>>;
109  template<class C>
111 
112 
114  template<class C, typename SEL =void>
116  {
117  static_assert (not sizeof(C), "unable to determine any supported container type for C");
118  };
119 
120  template<typename V>
121  struct ContainerTraits<V, IF_is_vector<V> >
122  {
123  using Vec = _AsVector<V>;
124  using Elm = typename Vec::value_type;
125  using Itr = typename Vec::iterator;
126 
127  static Itr
128  recentElmRawIter (Vec& vec)
129  {
130  return Itr{&vec.back()};
131  }
132 
133  static void
134  append (Vec& vec, Elm&& elm)
135  {
136  vec.emplace_back (forward<Elm> (elm));
137  }
138  };
139 
140  template<typename M>
141  struct ContainerTraits<M, IF_is_map<M> >
142  {
143  using Map = _AsMap<M>;
144  using Key = typename Map::key_type;
145  using Val = typename Map::mapped_type;
146  using Elm = std::pair<const Key, Val>;
147 
153  static auto
154  recentElmRawIter (Map& map)
155  {
156  auto& recentPos = ++map.rend();
157  return map.find (recentPos->first);
158  }
159 
160  static void
161  append (Map& map, Elm&& elm)
162  {
163  map.emplace (forward<Elm> (elm));
164  }
165  };
166 
167 
168 
187  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
190  {
191  using Coll = typename Strip<COLL>::TypeReferred;
192  using Elm = typename Coll::value_type;
194 
195  using iterator = typename lib::iter_stl::_SeqT<Coll>::Range;
196  using const_iterator = typename lib::iter_stl::_SeqT<const Coll>::Range;
197 
198 
199  ASSERT_VALID_SIGNATURE (MAT, bool(GenNode const& spec, Elm const& elm))
200  ASSERT_VALID_SIGNATURE (CTR, Elm (GenNode const&))
201  ASSERT_VALID_SIGNATURE (SEL, bool(GenNode const&))
202  ASSERT_VALID_SIGNATURE (ASS, bool(Elm&, GenNode const&))
203  ASSERT_VALID_SIGNATURE (MUT, bool(Elm&, GenNode::ID const&, TreeMutator::Handle))
204 
205 
206  Coll& collection;
207 
208  MAT matches;
209  CTR construct;
210  SEL isApplicable;
211  ASS assign;
212  MUT openSub;
213 
214  CollectionBinding(Coll& coll, MAT m, CTR c, SEL s, ASS a, MUT u)
215  : collection(coll)
216  , matches(m)
217  , construct(c)
218  , isApplicable(s)
219  , assign(a)
220  , openSub(u)
221  { }
222 
223  // only move construction allowed,
224  // to enable use of unique_ptr in collections
225 
226 
227 
228  /* === content manipulation API === */
229 
230  Coll contentBuffer;
231 
232  iterator
233  initMutation ()
234  {
235  contentBuffer.clear();
236  swap (collection, contentBuffer);
237  return eachElm (contentBuffer);
238  }
239 
240  void
241  inject (Elm&& elm)
242  {
243  Trait::append (collection, forward<Elm>(elm));
244  }
245 
246  iterator
247  search (GenNode const& targetSpec, iterator pos)
248  {
249  while (pos and not matches(targetSpec, *pos))
250  ++pos;
251  return pos;
252  }
253 
256  iterator
257  locate (GenNode const& targetSpec)
258  {
259  if (not collection.empty()
260  and matches (targetSpec, recentElm()))
261  return recentElmIter();
262  else
263  return search (targetSpec, eachElm(collection));
264  }
265 
266 
267  private: /* === Technicalities of container access === */
268 
274  iterator
276  {
277  return iterator{Trait::recentElmRawIter (collection), std::end (collection)};
278  }
279 
280  Elm&
281  recentElm()
282  {
283  return *Trait::recentElmRawIter (collection);
284  }
285  };
286 
287 
288 
305  template<class PAR, class BIN>
307  : public PAR
308  {
309  using Iter = typename BIN::iterator;
310 
311  BIN binding_;
312  Iter pos_;
313 
314 
315  public:
316  ChildCollectionMutator(BIN&& wiringClosures, PAR&& chain)
317  : PAR(std::forward<PAR>(chain))
318  , binding_(forward<BIN>(wiringClosures))
319  , pos_()
320  { }
321 
322 
323 
324 
325  /* ==== Implementation of TreeNode operation API ==== */
326 
327  virtual void
328  init() override
329  {
330  pos_ = binding_.initMutation();
331  PAR::init();
332  }
333 
339  virtual bool
340  injectNew (GenNode const& n) override
341  {
342  if (binding_.isApplicable(n))
343  {
344  binding_.inject (std::move (binding_.construct(n)));
345  return true;
346  }
347  else
348  return PAR::injectNew (n);
349  }
350 
351  virtual bool
352  hasSrc () override
353  {
354  return bool(pos_) or PAR::hasSrc();
355  }
356 
359  virtual bool
360  matchSrc (GenNode const& spec) override
361  {
362  if (binding_.isApplicable(spec))
363  return pos_ and binding_.matches (spec, *pos_);
364  else
365  return PAR::matchSrc (spec);
366  }
367 
372  virtual void
373  skipSrc (GenNode const& n) override
374  {
375  if (binding_.isApplicable(n))
376  {
377  if (pos_)
378  ++pos_;
379  }
380  else
381  PAR::skipSrc (n);
382  }
383 
385  virtual bool
386  acceptSrc (GenNode const& n) override
387  {
388  if (binding_.isApplicable(n))
389  {
390  bool isSrcMatch = pos_ and binding_.matches (n, *pos_);
391  if (isSrcMatch) //NOTE: crucial to perform only our own match check here
392  {
393  binding_.inject (move(*pos_));
394  ++pos_;
395  }
396  return isSrcMatch;
397  }
398  else
399  return PAR::acceptSrc (n);
400  }
401 
403  virtual bool
404  findSrc (GenNode const& refSpec) override
405  {
406  if (binding_.isApplicable(refSpec))
407  {
408  Iter found = binding_.search (refSpec, pos_);
409  if (found)
410  {
411  binding_.inject (move(*found));
412  }
413  return bool(found);
414  }
415  else
416  return PAR::findSrc (refSpec);
417  }
418 
420  virtual bool
421  accept_until (GenNode const& spec) override
422  {
423  if (spec.matches (Ref::END)
424  or
425  (spec.matches (Ref::ATTRIBS)
426  and binding_.isApplicable (Ref::ATTRIBS)))
427  {
428  for ( ; pos_; ++pos_)
429  binding_.inject (move(*pos_));
430  return PAR::accept_until (spec);
431  }
432  else
433  if (binding_.isApplicable(spec))
434  {
435  bool foundTarget = false;
436  while (pos_ and not binding_.matches (spec, *pos_))
437  {
438  binding_.inject (move(*pos_));
439  ++pos_;
440  }
441  if (pos_ and binding_.matches (spec, *pos_))
442  {
443  binding_.inject (move(*pos_));
444  ++pos_;
445  foundTarget = true;
446  }
447  return foundTarget;
448  }
449  else
450  return PAR::accept_until (spec);
451  }
452 
455  virtual bool
456  assignElm (GenNode const& spec) override
457  {
458  if (binding_.isApplicable(spec))
459  {
460  Iter target_found = binding_.locate (spec);
461  return target_found and binding_.assign (*target_found, spec);
462  }
463  else
464  return PAR::assignElm (spec);
465  }
466 
479  virtual bool
480  mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff) override
481  {
482  if (binding_.isApplicable(spec))
483  {
484  Iter target_found = binding_.locate (spec);
485  return target_found and binding_.openSub (*target_found, spec.idi, targetBuff);
486  }
487  else
488  return PAR::mutateChild (spec, targetBuff);
489  }
490 
493  virtual bool
494  completeScope() override
495  {
496  return PAR::completeScope()
497  and isnil(this->pos_);
498  }
499  };
500 
501 
502 
503 
507  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
509  : CollectionBinding<COLL,MAT,CTR,SEL,ASS,MUT>
510  {
512 
513  template<class FUN>
515  matchElement (FUN matcher)
516  {
517  return { this->collection
518  , matcher
519  , this->construct
520  , this->isApplicable
521  , this->assign
522  , this->openSub
523  };
524  }
525 
526  template<class FUN>
528  constructFrom (FUN constructor)
529  {
530  return { this->collection
531  , this->matches
532  , constructor
533  , this->isApplicable
534  , this->assign
535  , this->openSub
536  };
537  }
538 
539  template<class FUN>
541  isApplicableIf (FUN selector)
542  {
543  return { this->collection
544  , this->matches
545  , this->construct
546  , selector
547  , this->assign
548  , this->openSub
549  };
550  }
551 
552  template<class FUN>
554  assignElement (FUN setter)
555  {
556  return { this->collection
557  , this->matches
558  , this->construct
559  , this->isApplicable
560  , setter
561  , this->openSub
562  };
563  }
564 
565  template<class FUN>
567  buildChildMutator (FUN childMutationBuilder)
568  {
569  return { this->collection
570  , this->matches
571  , this->construct
572  , this->isApplicable
573  , this->assign
574  , childMutationBuilder
575  };
576  }
577  };
578 
579 
581  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
582  inline auto
583  createCollectionBindingBuilder (COLL& coll, MAT m, CTR c, SEL s, ASS a, MUT u)
584  {
585  using Coll = typename Strip<COLL>::TypeReferred;
586 
588  }
589 
590 
591 
592  template<class ELM>
594  {
595  static bool
596  __ERROR_missing_matcher (GenNode const&, ELM const&)
597  {
598  throw error::Logic ("unable to build a sensible default matching predicate");
599  }
600 
601  static ELM
602  __ERROR_missing_constructor (GenNode const&)
603  {
604  throw error::Logic ("unable to build a sensible default for creating new elements");
605  }
606 
607  static bool
608  ignore_selector (GenNode const& spec)
609  {
610  return spec != Ref::ATTRIBS;
611  // by default apply diff unconditionally,
612  // but don't respond to after(ATTRIBS)
613  }
614 
615  static bool
616  disable_assignment (ELM&, GenNode const&)
617  {
618  return false;
619  }
620 
621  static bool
622  disable_childMutation (ELM&, GenNode::ID const&, TreeMutator::Handle)
623  {
624  return false;
625  }
626 
627 
628  template<class COLL>
629  static auto
630  attachTo (COLL& coll)
631  {
632  return createCollectionBindingBuilder (coll
633  ,__ERROR_missing_matcher
634  ,__ERROR_missing_constructor
635  ,ignore_selector
636  ,disable_assignment
637  ,disable_childMutation
638  );
639  }
640  };
641 
642 
643 
644  using lib::meta::enable_if;
646 
660  template<class ELM, typename SEL =void>
662  : _EmptyBinding<ELM>
663  { };
664 
665  template<class ELM>
666  struct _DefaultBinding<ELM, enable_if<can_wrap_in_GenNode<ELM>>>
667  {
668  template<class COLL>
669  static auto
670  attachTo (COLL& coll)
671  {
672  return _EmptyBinding<ELM>::attachTo(coll)
673  .matchElement([](GenNode const& spec, ELM const& elm)
674  {
675  return spec.matches(elm);
676  })
677  .constructFrom([](GenNode const& spec) -> ELM
678  {
679  return spec.data.get<ELM>();
680  });
681  }
682  };
683 
684 
687  template<>
689  {
690  template<class COLL>
691  static auto
692  attachTo (COLL& coll)
693  {
695  .matchElement([](GenNode const& spec, GenNode const& elm)
696  {
697  return spec.matches(elm);
698  })
699  .constructFrom([](GenNode const& spec) -> GenNode
700  {
701  return GenNode{spec};
702  })
703  .assignElement ([](GenNode& target, GenNode const& spec) -> bool
704  {
705  target.data = spec.data;
706  return true;
707  })
708  .buildChildMutator ([](GenNode& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
709  {
710  if (target.idi == subID // require match on already existing child object
711  and target.data.isNested())
712  {
713  mutateInPlace (target.data.get<Rec>())
714  .buildMutator(buff);
715  buff.get()->init();
716  return true;
717  }
718  else
719  return false;
720  });
721  }
722  };
723 
724 
725 
726 
727 
737  template<class COLL>
738  inline auto
739  collection (COLL& coll)
740  {
741  using Elm = typename COLL::value_type;
742 
743  return _DefaultBinding<Elm>::attachTo(coll);
744  }
745 
746 
747 
749  template<class PAR>
750  template<class BIN>
751  inline auto
752  Builder<PAR>::attach (BIN&& collectionBindingSetup)
753  {
754  return chainedBuilder<ChildCollectionMutator<PAR,BIN>> (forward<BIN>(collectionBindingSetup));
755  }
756 
757 
758  }//(END)Mutator-Builder decorator components...
759 
760 }} // namespace lib::diff
761 #endif /*LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H*/
#define ASSERT_VALID_SIGNATURE(_FUN_, _SIG_)
Macro for a compile-time check to verify the given generic functors or lambdas expose some expected s...
Definition: function.hpp:247
metafunction to detect types able to be wrapped into a GenNode.
Definition: gen-node.hpp:513
virtual void skipSrc(GenNode const &n) override
skip next pending src element, causing this element to be discarded
bool isNested() const
determine if payload constitutes a nested scope ("object")
Definition: gen-node.hpp:768
virtual bool accept_until(GenNode const &spec) override
repeatedly accept, until after the designated location
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:49
auto createCollectionBindingBuilder(COLL &coll, MAT m, CTR c, SEL s, ASS a, MUT u)
builder function to synthesise builder type from given functors
Record< VAL >::Mutator & mutateInPlace(Record< VAL > &record_to_mutate)
open an existing record for modification in-place.
Definition: record.hpp:594
Implementation namespace for support and library code.
typename enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition: meta/util.hpp:83
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Helper for type analysis: tries to strip all kinds of type adornments.
Definition: trait.hpp:250
virtual bool injectNew(GenNode const &n) override
fabricate a new element, based on the given specification (GenNode), and insert it at current positio...
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:104
virtual bool acceptSrc(GenNode const &n) override
accept existing element, when matching the given spec
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:862
iterator locate(GenNode const &targetSpec)
locate element for assignment or mutation, with special shortcut to the recently inserted element ...
Generic building block for tree shaped (meta)data structures.
CollectionBindingBuilder< COLL, MAT, CTR, SEL, ASS, FUN > buildChildMutator(FUN childMutationBuilder)
Helpers for type detection, type rewriting and metaprogramming.
Lumiera error handling (C++ interface).
bool matches(GenNode const &o) const
Definition: gen-node.hpp:351
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:865
Customisable intermediary to abstract generic tree mutation operations.
virtual bool completeScope() override
verify all our pending (old) source elements where mentioned.
virtual bool matchSrc(GenNode const &spec) override
ensure the next recorded source element matches on a formal level with given spec ...
auto collection(COLL &coll)
Entry point to a nested DSL for setup and configuration of a collection binding.
Preconfigured adapters for some STL container standard usage situations.
virtual bool findSrc(GenNode const &refSpec) override
locate designated element and accept it at current position
CollectionBindingBuilder< COLL, MAT, FUN,SEL, ASS, MUT > constructFrom(FUN constructor)
object-like record of data.
Definition: record.hpp:141
virtual bool mutateChild(GenNode const &spec, TreeMutator::Handle targetBuff) override
locate the designated target element and build a suitable sub-mutator for this element into the provi...
generic data element node within a tree
Definition: gen-node.hpp:222
virtual bool assignElm(GenNode const &spec) override
locate element already accepted into the target sequence and assign the designated payload value to i...