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) Lumiera.org
5  2016, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
81 #ifndef LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
82 #define LIB_DIFF_TREE_MUTATOR_COLLECTION_BINDING_H
83 
84 
85 #include "lib/error.hpp"
86 #include "lib/nocopy.hpp"
87 #include "lib/meta/trait.hpp"
88 #include "lib/diff/gen-node.hpp"
90 #include "lib/iter-adapter-stl.hpp"
91 
92 #include <utility>
93 #include <vector>
94 #include <map>
95 
96 
97 namespace lib {
98 namespace diff{
99 
100  namespace { // Mutator-Builder decorator components...
101 
102 
103  using std::forward;
104  using lib::meta::Strip;
105  using lib::diff::GenNode;
106  using lib::iter_stl::eachElm;
107 
108 
109  /* === Technicalities of container access === */
110 
111  template<class C>
112  using _AsVector = std::vector<typename C::value_type>;
113  template<class C>
114  using _AsMap = std::map<typename C::key_type, typename C::mapped_type>;
115 
116  template<class C>
117  using IF_is_vector = lib::meta::enable_if< std::is_base_of<_AsVector<C>, C>>;
118  template<class C>
120 
121 
123  template<class C, typename SEL =void>
125  {
126  static_assert (not sizeof(C), "unable to determine any supported container type for C");
127  };
128 
129  template<typename V>
130  struct ContainerTraits<V, IF_is_vector<V> >
131  {
132  using Vec = _AsVector<V>;
133  using Elm = typename Vec::value_type;
134  using Itr = typename Vec::iterator;
135 
136  static Itr
137  recentElmRawIter (Vec& vec)
138  {
139  return Itr{&vec.back()};
140  }
141 
142  static void
143  append (Vec& vec, Elm&& elm)
144  {
145  vec.emplace_back (forward<Elm> (elm));
146  }
147  };
148 
149  template<typename M>
150  struct ContainerTraits<M, IF_is_map<M> >
151  {
152  using Map = _AsMap<M>;
153  using Key = typename Map::key_type;
154  using Val = typename Map::mapped_type;
155  using Elm = std::pair<const Key, Val>;
156 
162  static auto
163  recentElmRawIter (Map& map)
164  {
165  auto& recentPos = ++map.rend();
166  return map.find (recentPos->first);
167  }
168 
169  static void
170  append (Map& map, Elm&& elm)
171  {
172  map.emplace (forward<Elm> (elm));
173  }
174  };
175 
176 
177 
196  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
199  {
200  using Coll = typename Strip<COLL>::TypeReferred;
201  using Elm = typename Coll::value_type;
203 
204  using iterator = typename lib::iter_stl::_SeqT<Coll>::Range;
205  using const_iterator = typename lib::iter_stl::_SeqT<const Coll>::Range;
206 
207 
208  ASSERT_VALID_SIGNATURE (MAT, bool(GenNode const& spec, Elm const& elm))
209  ASSERT_VALID_SIGNATURE (CTR, Elm (GenNode const&))
210  ASSERT_VALID_SIGNATURE (SEL, bool(GenNode const&))
211  ASSERT_VALID_SIGNATURE (ASS, bool(Elm&, GenNode const&))
212  ASSERT_VALID_SIGNATURE (MUT, bool(Elm&, GenNode::ID const&, TreeMutator::Handle))
213 
214 
215  Coll& collection;
216 
217  MAT matches;
218  CTR construct;
219  SEL isApplicable;
220  ASS assign;
221  MUT openSub;
222 
223  CollectionBinding(Coll& coll, MAT m, CTR c, SEL s, ASS a, MUT u)
224  : collection(coll)
225  , matches(m)
226  , construct(c)
227  , isApplicable(s)
228  , assign(a)
229  , openSub(u)
230  { }
231 
232  // only move construction allowed,
233  // to enable use of unique_ptr in collections
234 
235 
236 
237  /* === content manipulation API === */
238 
239  Coll contentBuffer;
240 
241  iterator
242  initMutation ()
243  {
244  contentBuffer.clear();
245  swap (collection, contentBuffer);
246  return eachElm (contentBuffer);
247  }
248 
249  void
250  inject (Elm&& elm)
251  {
252  Trait::append (collection, forward<Elm>(elm));
253  }
254 
255  iterator
256  search (GenNode const& targetSpec, iterator pos)
257  {
258  while (pos and not matches(targetSpec, *pos))
259  ++pos;
260  return pos;
261  }
262 
265  iterator
266  locate (GenNode const& targetSpec)
267  {
268  if (not collection.empty()
269  and matches (targetSpec, recentElm()))
270  return recentElmIter();
271  else
272  return search (targetSpec, eachElm(collection));
273  }
274 
275 
276  private: /* === Technicalities of container access === */
277 
283  iterator
285  {
286  return iterator{Trait::recentElmRawIter (collection), std::end (collection)};
287  }
288 
289  Elm&
290  recentElm()
291  {
292  return *Trait::recentElmRawIter (collection);
293  }
294  };
295 
296 
297 
314  template<class PAR, class BIN>
316  : public PAR
317  {
318  using Iter = typename BIN::iterator;
319 
320  BIN binding_;
321  Iter pos_;
322 
323 
324  public:
325  ChildCollectionMutator(BIN&& wiringClosures, PAR&& chain)
326  : PAR(std::forward<PAR>(chain))
327  , binding_(forward<BIN>(wiringClosures))
328  , pos_()
329  { }
330 
331 
332 
333 
334  /* ==== Implementation of TreeNode operation API ==== */
335 
336  virtual void
337  init() override
338  {
339  pos_ = binding_.initMutation();
340  PAR::init();
341  }
342 
348  virtual bool
349  injectNew (GenNode const& n) override
350  {
351  if (binding_.isApplicable(n))
352  {
353  binding_.inject (std::move (binding_.construct(n)));
354  return true;
355  }
356  else
357  return PAR::injectNew (n);
358  }
359 
360  virtual bool
361  hasSrc () override
362  {
363  return bool(pos_) or PAR::hasSrc();
364  }
365 
368  virtual bool
369  matchSrc (GenNode const& spec) override
370  {
371  if (binding_.isApplicable(spec))
372  return pos_ and binding_.matches (spec, *pos_);
373  else
374  return PAR::matchSrc (spec);
375  }
376 
381  virtual void
382  skipSrc (GenNode const& n) override
383  {
384  if (binding_.isApplicable(n))
385  {
386  if (pos_)
387  ++pos_;
388  }
389  else
390  PAR::skipSrc (n);
391  }
392 
394  virtual bool
395  acceptSrc (GenNode const& n) override
396  {
397  if (binding_.isApplicable(n))
398  {
399  bool isSrcMatch = pos_ and binding_.matches (n, *pos_);
400  if (isSrcMatch) //NOTE: crucial to perform only our own match check here
401  {
402  binding_.inject (move(*pos_));
403  ++pos_;
404  }
405  return isSrcMatch;
406  }
407  else
408  return PAR::acceptSrc (n);
409  }
410 
412  virtual bool
413  findSrc (GenNode const& refSpec) override
414  {
415  if (binding_.isApplicable(refSpec))
416  {
417  Iter found = binding_.search (refSpec, pos_);
418  if (found)
419  {
420  binding_.inject (move(*found));
421  }
422  return bool(found);
423  }
424  else
425  return PAR::findSrc (refSpec);
426  }
427 
429  virtual bool
430  accept_until (GenNode const& spec) override
431  {
432  if (spec.matches (Ref::END)
433  or
434  (spec.matches (Ref::ATTRIBS)
435  and binding_.isApplicable (Ref::ATTRIBS)))
436  {
437  for ( ; pos_; ++pos_)
438  binding_.inject (move(*pos_));
439  return PAR::accept_until (spec);
440  }
441  else
442  if (binding_.isApplicable(spec))
443  {
444  bool foundTarget = false;
445  while (pos_ and not binding_.matches (spec, *pos_))
446  {
447  binding_.inject (move(*pos_));
448  ++pos_;
449  }
450  if (pos_ and binding_.matches (spec, *pos_))
451  {
452  binding_.inject (move(*pos_));
453  ++pos_;
454  foundTarget = true;
455  }
456  return foundTarget;
457  }
458  else
459  return PAR::accept_until (spec);
460  }
461 
464  virtual bool
465  assignElm (GenNode const& spec) override
466  {
467  if (binding_.isApplicable(spec))
468  {
469  Iter target_found = binding_.locate (spec);
470  return target_found and binding_.assign (*target_found, spec);
471  }
472  else
473  return PAR::assignElm (spec);
474  }
475 
488  virtual bool
489  mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff) override
490  {
491  if (binding_.isApplicable(spec))
492  {
493  Iter target_found = binding_.locate (spec);
494  return target_found and binding_.openSub (*target_found, spec.idi, targetBuff);
495  }
496  else
497  return PAR::mutateChild (spec, targetBuff);
498  }
499 
502  virtual bool
503  completeScope() override
504  {
505  return PAR::completeScope()
506  and isnil(this->pos_);
507  }
508  };
509 
510 
511 
512 
516  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
518  : CollectionBinding<COLL,MAT,CTR,SEL,ASS,MUT>
519  {
521 
522  template<class FUN>
524  matchElement (FUN matcher)
525  {
526  return { this->collection
527  , matcher
528  , this->construct
529  , this->isApplicable
530  , this->assign
531  , this->openSub
532  };
533  }
534 
535  template<class FUN>
537  constructFrom (FUN constructor)
538  {
539  return { this->collection
540  , this->matches
541  , constructor
542  , this->isApplicable
543  , this->assign
544  , this->openSub
545  };
546  }
547 
548  template<class FUN>
550  isApplicableIf (FUN selector)
551  {
552  return { this->collection
553  , this->matches
554  , this->construct
555  , selector
556  , this->assign
557  , this->openSub
558  };
559  }
560 
561  template<class FUN>
563  assignElement (FUN setter)
564  {
565  return { this->collection
566  , this->matches
567  , this->construct
568  , this->isApplicable
569  , setter
570  , this->openSub
571  };
572  }
573 
574  template<class FUN>
576  buildChildMutator (FUN childMutationBuilder)
577  {
578  return { this->collection
579  , this->matches
580  , this->construct
581  , this->isApplicable
582  , this->assign
583  , childMutationBuilder
584  };
585  }
586  };
587 
588 
590  template<class COLL, class MAT, class CTR, class SEL, class ASS, class MUT>
591  inline auto
592  createCollectionBindingBuilder (COLL& coll, MAT m, CTR c, SEL s, ASS a, MUT u)
593  {
594  using Coll = typename Strip<COLL>::TypeReferred;
595 
597  }
598 
599 
600 
601  template<class ELM>
603  {
604  static bool
605  __ERROR_missing_matcher (GenNode const&, ELM const&)
606  {
607  throw error::Logic ("unable to build a sensible default matching predicate");
608  }
609 
610  static ELM
611  __ERROR_missing_constructor (GenNode const&)
612  {
613  throw error::Logic ("unable to build a sensible default for creating new elements");
614  }
615 
616  static bool
617  ignore_selector (GenNode const& spec)
618  {
619  return spec != Ref::ATTRIBS;
620  // by default apply diff unconditionally,
621  // but don't respond to after(ATTRIBS)
622  }
623 
624  static bool
625  disable_assignment (ELM&, GenNode const&)
626  {
627  return false;
628  }
629 
630  static bool
631  disable_childMutation (ELM&, GenNode::ID const&, TreeMutator::Handle)
632  {
633  return false;
634  }
635 
636 
637  template<class COLL>
638  static auto
639  attachTo (COLL& coll)
640  {
641  return createCollectionBindingBuilder (coll
642  ,__ERROR_missing_matcher
643  ,__ERROR_missing_constructor
644  ,ignore_selector
645  ,disable_assignment
646  ,disable_childMutation
647  );
648  }
649  };
650 
651 
652 
653  using lib::meta::enable_if;
655 
669  template<class ELM, typename SEL =void>
671  : _EmptyBinding<ELM>
672  { };
673 
674  template<class ELM>
675  struct _DefaultBinding<ELM, enable_if<can_wrap_in_GenNode<ELM>>>
676  {
677  template<class COLL>
678  static auto
679  attachTo (COLL& coll)
680  {
681  return _EmptyBinding<ELM>::attachTo(coll)
682  .matchElement([](GenNode const& spec, ELM const& elm)
683  {
684  return spec.matches(elm);
685  })
686  .constructFrom([](GenNode const& spec) -> ELM
687  {
688  return spec.data.get<ELM>();
689  });
690  }
691  };
692 
693 
696  template<>
698  {
699  template<class COLL>
700  static auto
701  attachTo (COLL& coll)
702  {
704  .matchElement([](GenNode const& spec, GenNode const& elm)
705  {
706  return spec.matches(elm);
707  })
708  .constructFrom([](GenNode const& spec) -> GenNode
709  {
710  return GenNode{spec};
711  })
712  .assignElement ([](GenNode& target, GenNode const& spec) -> bool
713  {
714  target.data = spec.data;
715  return true;
716  })
717  .buildChildMutator ([](GenNode& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
718  {
719  if (target.idi == subID // require match on already existing child object
720  and target.data.isNested())
721  {
722  mutateInPlace (target.data.get<Rec>())
723  .buildMutator(buff);
724  buff.get()->init();
725  return true;
726  }
727  else
728  return false;
729  });
730  }
731  };
732 
733 
734 
735 
736 
746  template<class COLL>
747  inline auto
748  collection (COLL& coll)
749  {
750  using Elm = typename COLL::value_type;
751 
752  return _DefaultBinding<Elm>::attachTo(coll);
753  }
754 
755 
756 
758  template<class PAR>
759  template<class BIN>
760  inline auto
761  Builder<PAR>::attach (BIN&& collectionBindingSetup)
762  {
763  return chainedBuilder<ChildCollectionMutator<PAR,BIN>> (forward<BIN>(collectionBindingSetup));
764  }
765 
766 
767  }//(END)Mutator-Builder decorator components...
768 
769 }} // namespace lib::diff
770 #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:256
metafunction to detect types able to be wrapped into a GenNode.
Definition: gen-node.hpp:522
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:777
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:58
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:602
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:92
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Helper for type analysis: tries to strip all kinds of type adornments.
Definition: trait.hpp:249
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:113
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:871
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:360
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:874
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:150
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:231
virtual bool assignElm(GenNode const &spec) override
locate element already accepted into the target sequence and assign the designated payload value to i...