LCOV - code coverage report
Current view: top level - src/bindings/cpp/include - kdbcontext.hpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 171 173 98.8 %
Date: 2019-09-12 12:28:41 Functions: 59 60 98.3 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #ifndef ELEKTRA_KDBCONTEXT_HPP
      10             : #define ELEKTRA_KDBCONTEXT_HPP
      11             : 
      12             : #include <kdbmeta.h>
      13             : #include <kdbvalue.hpp>
      14             : 
      15             : #include <cassert>
      16             : #include <functional>
      17             : #include <memory>
      18             : #include <set>
      19             : #include <string>
      20             : #include <unordered_map>
      21             : #include <vector>
      22             : 
      23             : namespace kdb
      24             : {
      25             : 
      26         108 : class Subject
      27             : {
      28             : public:
      29             :         virtual ~Subject () = 0;
      30             :         typedef std::vector<std::string> Events;
      31             :         virtual void attachObserver (ValueObserver &);
      32             :         virtual void attachObserverByEvent (std::string const & event, ValueObserver &);
      33             : 
      34             :         /**
      35             :          * @brief Notify all with given events
      36             :          *
      37             :          * @param events the event to be notify (observers need to listen to)
      38             :          */
      39             :         virtual void notifyByEvents (Events const & events) const;
      40             : 
      41             :         /**
      42             :          * @brief Notify by every event.
      43             :          *
      44             :          * Calls notifyByEvents () with every possible event.
      45             :          */
      46             :         virtual void notifyAllEvents () const;
      47             : 
      48             :         /**
      49             :          * @brief Notify on KeySet update.
      50             :          *
      51             :          * Not only notify keys that have a layer to update, because
      52             :          * every value might be updated now.
      53             :          */
      54             :         virtual void notifyKeySetUpdate () const;
      55             : 
      56             : protected:
      57             :         Subject ();
      58             : 
      59             : private:
      60             :         typedef std::set<ValueObserver::reference> ObserverSet;
      61             :         ObserverSet m_observers;
      62             :         mutable std::unordered_map<std::string, ObserverSet> m_events;
      63             : };
      64             : 
      65         390 : inline Subject::Subject ()
      66             : {
      67             : }
      68             : 
      69         462 : inline Subject::~Subject ()
      70             : {
      71             : }
      72             : 
      73         180 : inline void Subject::attachObserver (ValueObserver & observer)
      74             : {
      75         360 :         m_observers.insert (std::ref (observer));
      76         180 : }
      77             : 
      78         236 : inline void Subject::attachObserverByEvent (std::string const & event, ValueObserver & observer)
      79             : {
      80         472 :         auto it = m_events.find (event);
      81         472 :         if (it == m_events.end ())
      82             :         {
      83             :                 // add first observer for that event
      84             :                 // (creating a new vector)
      85         400 :                 ObserverSet & os = m_events[event];
      86         400 :                 os.insert (std::ref (observer));
      87             :         }
      88             :         else
      89             :         {
      90         108 :                 it->second.insert (std::ref (observer));
      91             :         }
      92         236 : }
      93             : 
      94         610 : inline void Subject::notifyByEvents (Events const & events) const
      95             : {
      96        1220 :         ObserverSet os;
      97        2988 :         for (auto & e : events)
      98             :         {
      99        1096 :                 auto it = m_events.find (e);
     100        1096 :                 if (it != m_events.end ())
     101             :                 {
     102        1570 :                         for (auto & o : it->second)
     103             :                         {
     104         412 :                                 os.insert (o); // (discarding duplicates)
     105             :                         }
     106             :                 }
     107             : #if DEBUG && VERBOSE
     108             :                 else
     109             :                 {
     110             :                         std::cout << "Trying to notify " << e << " but event does not exist" << std::endl;
     111             :                 }
     112             : #endif
     113             :         }
     114             :         // now call any observer exactly once
     115        2202 :         for (auto & o : os)
     116             :         {
     117         372 :                 o.get ().updateContext ();
     118             :         }
     119         610 : }
     120             : 
     121           4 : inline void Subject::notifyAllEvents () const
     122             : {
     123           8 :         Events events;
     124          22 :         for (auto & o : m_events)
     125             :         {
     126          10 :                 events.push_back (o.first);
     127             :         }
     128           4 :         notifyByEvents (events);
     129           4 : }
     130             : 
     131          24 : inline void Subject::notifyKeySetUpdate () const
     132             : {
     133          48 :         KeySet deps;
     134          24 :         size_t i = 0;
     135         132 :         for (auto & o : m_observers)
     136             :         {
     137         120 :                 Key dep (o.get ().getDepKey ());
     138          60 :                 dep.set<size_t> (i);
     139          60 :                 ++i;
     140          60 :                 deps.append (dep);
     141             :         }
     142             : 
     143          48 :         std::vector<ckdb::Key *> ordered;
     144          24 :         ordered.resize (deps.size ());
     145          24 :         int ret = elektraSortTopology (deps.getKeySet (), &ordered[0]);
     146          24 :         if (ret == 0) throw std::runtime_error ("Cycle in layer dependencies");
     147          22 :         if (ret == -1) throw std::logic_error ("elektraSortTopology used wrongly");
     148             : 
     149         136 :         for (auto & o : ordered)
     150             :         {
     151         336 :                 std::next (m_observers.begin (), Key (o).get<size_t> ())->get ().updateContext (false);
     152             :         }
     153          22 : }
     154             : 
     155             : /**
     156             :  * @brief Provides a context for configuration
     157             :  *
     158             :  * Is a subject for observers.
     159             :  *
     160             :  * Holds currently active layers and allows
     161             :  * global/scoped activation of layers.
     162             :  */
     163         534 : class Context : public Subject
     164             : {
     165             : public:
     166         520 :         Context () : m_active_layers ()
     167             :         {
     168             :         }
     169             : 
     170         348 :         virtual void execute (Command & c)
     171             :         {
     172         696 :                 c ();
     173         348 :         }
     174             : 
     175             :         /**
     176             :          * Lookup value for a current active layer
     177             :          *
     178             :          * @param layer the name of the requested layer
     179             :          * @return the layer
     180             :          */
     181         290 :         std::string operator[] (std::string const & layer) const
     182             :         {
     183         580 :                 auto f = m_active_layers.find (layer);
     184         580 :                 if (f != m_active_layers.end ())
     185             :                 {
     186             :                         assert (f->second && "no null pointers in active_layers");
     187         156 :                         return (*f->second) ();
     188             :                 }
     189         268 :                 return ""; // this line is surprisingly expensive
     190             :         }
     191             : 
     192             :         /**
     193             :          * @return size of all current layers (to be used with operator[])
     194             :          */
     195             :         size_t size () const
     196             :         {
     197          80 :                 return m_active_layers.size ();
     198             :         }
     199             : 
     200             :         /**
     201             :          * Attach observer using to all events given by
     202             :          * its specification (name)
     203             :          *
     204             :          * @param key_name the name with placeholders to be used for attaching
     205             :          * @param observer the observer to attach to
     206             :          */
     207         180 :         void attachByName (std::string const & key_name, ValueObserver & observer)
     208             :         {
     209         180 :                 this->attachObserver (observer);
     210             :                 evaluate (key_name, [&](std::string const & current_id, std::string &, bool) {
     211         236 :                         this->attachObserverByEvent (current_id, observer);
     212             :                         return false;
     213         720 :                 });
     214         180 :         }
     215             : 
     216             :         /**
     217             :          * Evaluate a specification (name) and return
     218             :          * a key name under current context
     219             :          *
     220             :          * @param key_name the name with placeholders to be evaluated
     221             :          */
     222         640 :         std::string evaluate (std::string const & key_name) const
     223             :         {
     224        1552 :                 return evaluate (key_name, [&](std::string const & current_id, std::string & ret, bool in_group) {
     225        4656 :                         auto f = m_active_layers.find (current_id);
     226        1552 :                         bool left_group = true;
     227        3104 :                         if (f != m_active_layers.end ())
     228             :                         {
     229             :                                 assert (f->second && "no null pointers in active_layers");
     230        1568 :                                 std::string r = (*f->second) ();
     231         784 :                                 if (!r.empty ())
     232             :                                 {
     233         778 :                                         if (in_group)
     234             :                                         {
     235             :                                                 ret += "%";
     236             :                                         }
     237             :                                         ret += r;
     238             :                                         left_group = false;
     239             :                                 }
     240           6 :                                 else if (!in_group)
     241             :                                 {
     242             :                                         ret += "%";
     243             :                                 }
     244             :                         }
     245         768 :                         else if (!in_group)
     246             :                         {
     247             :                                 ret += "%";
     248             :                         }
     249        1552 :                         return left_group;
     250        1920 :                 });
     251             :         }
     252             : 
     253             :         /**
     254             :          * Evaluate specification with this context.
     255             :          *
     256             :          * @param key_name the keyname with placeholders to evaluate
     257             :          * @param on_layer the function to be called for every
     258             :          *                 placeholder found
     259             :          *
     260             :          * @par on_layer is called for every layer in the
     261             :          * specification.
     262             :          * @return the evaluated string
     263             :          */
     264         880 :         std::string evaluate (std::string const & key_name,
     265             :                               std::function<bool(std::string const &, std::string &, bool in_group)> const & on_layer) const
     266             :         {
     267         880 :                 size_t const & s = key_name.size ();
     268         880 :                 std::string ret;
     269        1760 :                 std::string current_id;
     270         880 :                 bool capture_id = false; // we are currently within a % block (group or single layer)
     271         880 :                 bool left_group = false; // needed to omit layers that do not matter in a group anymore
     272         880 :                 bool is_in_group = false;
     273             : 
     274             :                 // heuristic how much too allocate
     275         880 :                 ret.reserve (s * 2);
     276         880 :                 current_id.reserve (32);
     277             : 
     278       52624 :                 for (std::string::size_type i = 0; i < s; ++i)
     279             :                 {
     280       25872 :                         if (key_name[i] == '%')
     281             :                         {
     282        2976 :                                 if (capture_id)
     283             :                                 {
     284             :                                         // finish capturing
     285        1488 :                                         if (!left_group)
     286             :                                         {
     287        1386 :                                                 on_layer (current_id, ret, is_in_group);
     288             :                                         }
     289        1488 :                                         current_id.clear ();
     290        1488 :                                         capture_id = false;
     291             :                                 }
     292             :                                 else
     293             :                                 {
     294             :                                         // start capturing
     295             :                                         capture_id = true;
     296             :                                         left_group = false;
     297             :                                         is_in_group = false;
     298             :                                 }
     299             :                         }
     300       37874 :                         else if (capture_id && key_name[i] == ' ' && !left_group)
     301             :                         {
     302             :                                 // found group separator in active
     303             :                                 // group
     304         446 :                                 left_group = on_layer (current_id, ret, true);
     305         446 :                                 if (!is_in_group && left_group)
     306             :                                 {
     307             :                                         ret += "%"; // empty groups
     308             :                                 }
     309             :                                 else
     310             :                                 {
     311             :                                         is_in_group = true;
     312             :                                 }
     313             :                                 current_id.clear ();
     314             :                         }
     315             :                         else // non % character
     316             :                         {
     317       22450 :                                 if (capture_id)
     318             :                                 {
     319       14532 :                                         current_id += key_name[i];
     320             :                                 }
     321             :                                 else
     322             :                                 {
     323        7918 :                                         ret += key_name[i];
     324             :                                 }
     325             :                         }
     326             :                 }
     327             : 
     328             :                 assert (!capture_id && "number of % incorrect");
     329             : 
     330         880 :                 return ret;
     331             :         }
     332             : 
     333             : protected:
     334             :         // activates layer, records it, but does not notify
     335             :         template <typename T, typename... Args>
     336          90 :         void lazyActivate (Args &&... args)
     337             :         {
     338         278 :                 std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
     339          90 :                 lazyActivateLayer (layer);
     340          90 :         }
     341             : 
     342             :         // needed for with
     343          94 :         void lazyActivateLayer (std::shared_ptr<Layer> const & layer)
     344             :         {
     345         188 :                 std::string const & id = layer->id (); // optimisation
     346         188 :                 auto p = m_active_layers.emplace (std::make_pair (id, layer));
     347          94 :                 if (!p.second)
     348             :                 {
     349          12 :                         m_with_stack.push_back (*p.first);
     350           6 :                         p.first->second = layer; // update
     351             :                 }
     352             :                 else
     353             :                 {
     354             :                         // no layer was not active before, remember that
     355         352 :                         m_with_stack.push_back (std::make_pair (id, std::shared_ptr<Layer> ()));
     356             :                 }
     357             : #if DEBUG && VERBOSE
     358             :                 std::cout << "lazy activate layer: " << id << std::endl;
     359             : #endif
     360          94 :         }
     361             : 
     362             :         void clearAllLayer ()
     363             :         {
     364           0 :                 m_active_layers.clear ();
     365             :         }
     366             : 
     367             :         // needed for global activation
     368         230 :         void activateLayer (std::shared_ptr<Layer> const & layer)
     369             :         {
     370         920 :                 auto p = m_active_layers.emplace (std::make_pair (layer->id (), layer));
     371         230 :                 if (!p.second)
     372             :                 {
     373          48 :                         p.first->second = layer; // update
     374             :                 }
     375             : 
     376         920 :                 notifyByEvents ({ layer->id () });
     377             : 
     378             : #if DEBUG && VERBOSE
     379             :                 std::cout << "activate layer: " << layer->id () << std::endl;
     380             : #endif
     381         230 :         }
     382             : 
     383             : public:
     384             :         /**
     385             :          * @brief Globally activate the layer
     386             :          *
     387             :          * @tparam T the layer to activate
     388             :          * @tparam Args the types for the  arguments to pass to layer construction
     389             :          * @param args the arguments to pass to layer construction
     390             :          */
     391             :         template <typename T, typename... Args>
     392          94 :         std::shared_ptr<Layer> activate (Args &&... args)
     393             :         {
     394         324 :                 std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
     395          94 :                 activateLayer (layer);
     396          94 :                 return layer;
     397             :         }
     398             : 
     399          36 :         std::shared_ptr<Layer> activate (Wrapped const & value)
     400             :         {
     401         108 :                 std::shared_ptr<Layer> layer = std::make_shared<WrapLayer> (value);
     402          36 :                 activateLayer (layer);
     403          36 :                 return layer;
     404             :         }
     405             : 
     406          16 :         std::shared_ptr<Layer> activate (std::string key, std::string value)
     407             :         {
     408          48 :                 std::shared_ptr<Layer> layer = std::make_shared<KeyValueLayer> (key, value);
     409          16 :                 activateLayer (layer);
     410          16 :                 return layer;
     411             :         }
     412             : 
     413             : protected:
     414             :         template <typename T, typename... Args>
     415          24 :         void lazyDeactivate (Args &&... args)
     416             :         {
     417          80 :                 std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
     418          24 :                 lazyDeactivateLayer (layer);
     419          24 :         }
     420             : 
     421          24 :         void lazyDeactivateLayer (std::shared_ptr<Layer> const & layer)
     422             :         {
     423          72 :                 auto p = m_active_layers.find (layer->id ());
     424          48 :                 if (p != m_active_layers.end ())
     425             :                 {
     426          48 :                         m_with_stack.push_back (*p);
     427          24 :                         m_active_layers.erase (p);
     428             :                 }
     429             : // else: deactivate whats not there:
     430             : // nothing to do!
     431             : #if DEBUG && VERBOSE
     432             :                 std::cout << "lazy deactivate layer: " << layer->id () << std::endl;
     433             : #endif
     434          24 :         }
     435             : 
     436          42 :         void deactivateLayer (std::shared_ptr<Layer> const & layer)
     437             :         {
     438         126 :                 m_active_layers.erase (layer->id ());
     439             : 
     440             : #if DEBUG && VERBOSE
     441             :                 std::cout << "deactivate layer: " << layer->id () << std::endl;
     442             : #endif
     443          42 :         }
     444             : 
     445             : public:
     446             :         template <typename T, typename... Args>
     447          36 :         std::shared_ptr<Layer> deactivate (Args &&... args)
     448             :         {
     449         122 :                 std::shared_ptr<Layer> layer = std::make_shared<T> (std::forward<Args> (args)...);
     450          36 :                 deactivateLayer (layer);
     451         144 :                 notifyByEvents ({ layer->id () });
     452          36 :                 return layer;
     453             :         }
     454             : 
     455           4 :         std::shared_ptr<Layer> deactivate (Wrapped const & value)
     456             :         {
     457          12 :                 std::shared_ptr<Layer> layer = std::make_shared<WrapLayer> (value);
     458           4 :                 deactivateLayer (layer);
     459          16 :                 notifyByEvents ({ layer->id () });
     460           4 :                 return layer;
     461             :         }
     462             : 
     463             :         std::shared_ptr<Layer> deactivate (std::string key, std::string value)
     464             :         {
     465             :                 std::shared_ptr<Layer> layer = std::make_shared<KeyValueLayer> (key, value);
     466             :                 deactivateLayer (layer);
     467             :                 notifyByEvents ({ layer->id () });
     468             :                 return layer;
     469             :         }
     470             : 
     471             : public:
     472             :         Context & withl (std::shared_ptr<Layer> & l)
     473             :         {
     474             :                 // build up staple (until function is executed)
     475           0 :                 lazyActivateLayer (l);
     476             :                 return *this;
     477             :         }
     478             : 
     479             :         template <typename T, typename... Args>
     480             :         Context & with (Args &&... args)
     481             :         {
     482             :                 // build up staple (until function is executed)
     483          90 :                 lazyActivate<T, Args...> (std::forward<Args> (args)...);
     484             :                 return *this;
     485             :         }
     486             : 
     487             :         template <typename T, typename... Args>
     488             :         Context & without (Args &&... args)
     489             :         {
     490             :                 // build up staple (until function is executed)
     491          24 :                 lazyDeactivate<T, Args...> (std::forward<Args> (args)...);
     492             :                 return *this;
     493             :         }
     494             : 
     495             :         Context & operator() (std::function<void()> const & f)
     496             :         {
     497          94 :                 execHelper (f);
     498             : 
     499             :                 return *this;
     500             :         }
     501             : 
     502             :         Context & withl (std::shared_ptr<Layer> & l, std::function<void()> const & f)
     503             :         {
     504           4 :                 lazyActivateLayer (l);
     505           4 :                 execHelper (f);
     506             : 
     507             :                 return *this;
     508             :         }
     509             : 
     510             : private:
     511             :         typedef std::vector<std::pair<std::string, std::shared_ptr<Layer>>> WithStack;
     512             : 
     513          98 :         void execHelper (std::function<void()> const & f)
     514             :         {
     515         196 :                 WithStack with_stack = m_with_stack;
     516         196 :                 m_with_stack.clear (); // allow with to be called recursively
     517             :                 // last step, now lets really activate
     518         196 :                 Subject::Events to_notify;
     519         510 :                 for (auto & s : with_stack)
     520             :                 {
     521         118 :                         to_notify.push_back (s.first);
     522             :                 }
     523          98 :                 notifyByEvents (to_notify);
     524             : 
     525             :                 // now do the function call,
     526             :                 // keep roll back information on the stack
     527             :                 f ();
     528             : 
     529             :                 // now roll everything back before all those with()
     530             :                 // and without()
     531         334 :                 while (!with_stack.empty ())
     532             :                 {
     533         236 :                         auto s = with_stack.back ();
     534         118 :                         with_stack.pop_back ();
     535         118 :                         if (!s.second)
     536             :                         {
     537             :                                 // do not add null pointer
     538             :                                 // but erase layer instead
     539          88 :                                 m_active_layers.erase (s.first);
     540             :                         }
     541             :                         else
     542             :                         {
     543          60 :                                 auto it = m_active_layers.insert (s);
     544          30 :                                 if (!it.second)
     545             :                                 {
     546          10 :                                         it.first->second = s.second;
     547             :                                 }
     548             :                         }
     549             :                 }
     550          98 :                 notifyByEvents (to_notify);
     551          98 :         }
     552             : 
     553             :         std::unordered_map<std::string, std::shared_ptr<Layer>> m_active_layers;
     554             :         // the with stack holds all layers that were
     555             :         // changed in the current .with().with()
     556             :         // invocation chain
     557             :         WithStack m_with_stack;
     558             : };
     559             : 
     560             : template <typename T, typename PolicySetter1 = DefaultPolicyArgs, typename PolicySetter2 = DefaultPolicyArgs,
     561             :           typename PolicySetter3 = DefaultPolicyArgs, typename PolicySetter4 = DefaultPolicyArgs,
     562             :           typename PolicySetter5 = DefaultPolicyArgs>
     563             : using ContextualValue = Value<T, ContextPolicyIs<Context>, PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4, PolicySetter5>;
     564             : 
     565             : typedef ContextualValue<uint32_t> Integer;
     566             : typedef ContextualValue<bool> Boolean;
     567             : typedef ContextualValue<std::string> String;
     568             : 
     569             : } // namespace kdb
     570             : 
     571             : #endif

Generated by: LCOV version 1.13