/* ADVICE.hpp - generic loosely coupled interaction guided by symbolic pattern Copyright (C) Lumiera.org 2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file advice.hpp ** Expecting Advice and giving Advice: a cross-cutting collaboration of loosely coupled participants. ** This header exposes the basics of the advice system and the public access points. The advice system ** is a system wide singleton service, but clients never talk directly to this singleton; rather they ** use advice::Provision and advice::Request as access point. ** ** \par Advice collaboration pattern ** Advice collaboration is a special pattern of interaction extracted from multiple use cases within ** Lumiera. Creating this abstraction was partially inspired by aspect oriented programming, especially ** the idea of cross-cutting the primary dependency hierarchy. Another source of inspiration where the ** various incarnations of properties with dynamic binding. For defining the actual binding, we rely ** on predicate notation and matching (later unification) as known from rule based systems. ** ** Definition: Advice is an optional, mediated collaboration between entities taking on ** the roles of advisor and advised, thereby passing a custom piece of advice data, managed by ** the advice support system. The possibility of advice is created by both of the collaborators ** entering the system, where the advised entity exposes a point of advice, while the advising ** entity provides an actual advice value. ** ** \par Collaborators ** - the advised entity ** - the advisor ** - point of advice ** - advice system ** - the binding ** - the advice ** ** Usually, the \em advised entity opens the collaboration by requesting an advice. The \em advice itself ** is a piece of data of a custom type, which needs to be copyable. Obviously, both the advised and the ** advisor need to share knowledge about the meaning of this advice data. The actual advice collaboration ** happens at a \em point-of-advice, which needs to be derived first. To this end, the advised puts up an ** \em request by providing his \em binding, which is a pattern for matching. An entity about to give advice ** opens possible \advice \em channels by putting up an advisor binding, which similarly is a pattern. The ** advice \em system as mediator resolves both sides, by matching (which in the most general case could be ** an unification). This process creates an advice point \em solution -- allowing the advisor to fed the ** piece of advice into the advice channel, causing it to be placed into the point of advice. After passing ** a certain (implementation defined) break point, the advice leaves the influence of the advisor and gets ** exposed to the advised entities. Especially this involves copying the advice data into a location managed ** by the advice system. In the standard case, the advised entity accesses the advice synchronously and ** non-blocking. Typically, the advice data type is default constructible and thus there is always a basic ** form of advice available, thereby completely decoupling the advised entity from the timings related ** to this collaboration. ** ** TODO WIP-WIP ** ** @note as of 4/2010 this is an experimental setup and implemented just enough to work out ** the interfaces. Ichthyo expects this collaboration service to play a central role ** at various places within proc-layer. ** ** @see configrules.hpp ** @see typed-lookup.cpp corresponding implementation ** @see typed-id-test.cpp ** */ #ifndef LIB_ADVICE_H #define LIB_ADVICE_H #include "lib/error.hpp" //#include "proc/asset.hpp" //#include "proc/asset/struct-scheme.hpp" //#include "lib/hash-indexed.hpp" //#include "lib/util.hpp" #include "lib/symbol.hpp" #include "lib/advice/binding.hpp" //#include //#include //#include //#include namespace lib { ///////TODO: how to arrange the namespaces best? namespace advice { /** * TODO type comment */ class PointOfAdvice { Binding::Matcher pattern_; PointOfAdvice* resolution_; protected: /** define or re-define the binding, which * specifically labels this attachment to the advice system. * @note issuing this on an existing connection is equivalent * to re-connecting with the new binding. */ void setBindingPattern (Binding const& binding) { pattern_ = binding.buildMatcher(); } public: explicit PointOfAdvice (Binding const& binding) : pattern_(binding.buildMatcher()) , resolution_(0) { } // using default copy/assignment /* == Adapter interface for use within the Index == */ friend HashVal hash_value (PointOfAdvice const& entry) { return hash_value (entry.pattern_); } friend const Binding::Matcher getMatcher (PointOfAdvice const& entry) { return entry.pattern_; } friend const PointOfAdvice* getSolution (PointOfAdvice const& entry) { return entry.resolution_; } friend void setSolution (PointOfAdvice* entry, PointOfAdvice* solution =0) { REQUIRE (entry); entry->resolution_ = solution; } }; /** * Access point for the advising entity (server). * TODO type comment */ template class Provision : public PointOfAdvice { AD theAdvice_; /* == policy definitions == */ ////TODO: extract into policy classes AD const& handleMissingSolution() const { return AD(); } /////////////////////TODO either return value or build a registry of defaults void deregistrate() { /* NOP */ } public: explicit Provision (Literal bindingSpec =0) : PointOfAdvice (Binding(bindingSpec).addTypeGuard()) , theAdvice_() { } ~Provision() { this->deregistrate(); } AD const& getAdvice() const { return theAdvice_; } void setAdvice (AD const& pieceOfAdvice) { theAdvice_ = pieceOfAdvice; UNIMPLEMENTED ("change advice provision registration"); } void retractAdvice() { theAdvice_ = this->handleMissingSolution(); UNIMPLEMENTED ("notify index of retracted advice"); } void defineBinding (Literal topic) { setBindingPattern (Binding(topic).addTypeGuard()); UNIMPLEMENTED ("propagate binding change to index"); } }; /** * Access point for the advised entity (client). * TODO type comment */ template class Request : public PointOfAdvice { typedef const Provision AdviceProvision; /* == policy definitions == */ ////TODO: extract into policy classes AD const& handleMissingSolution() const { return AD(); } /////////////////////TODO either return value or build a registry of defaults public: explicit Request (Literal bindingSpec =0) : PointOfAdvice (Binding(bindingSpec).addTypeGuard()) { UNIMPLEMENTED ("registration with the index"); } ~Request() { UNIMPLEMENTED ("detach from index"); } AD const& getAdvice() const { AdviceProvision* solution = static_cast (getSolution (*this)); if (!solution) return this->handleMissingSolution(); else return solution->getAdvice(); } void defineBinding (Literal topic) { setBindingPattern (Binding(topic).addTypeGuard()); UNIMPLEMENTED ("propagate binding change to index"); } }; }} // namespace lib::advice #endif