lumiera_/src/lib/advice.hpp

282 lines
9.3 KiB
C++

/*
ADVICE.hpp - generic loosely coupled interaction guided by symbolic pattern
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
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.
**
** <b>Definition</b>: 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 <boost/operators.hpp>
//#include <tr1/memory>
//#include <iostream>
//#include <string>
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
*
* TODO planned implementation of memory handling: see notes in TiddlyWiki
* Basically I'll use this->resolution_ to point to the copy incorporated into the advice system.
* This is some kind of "unofficial" ownership and slightly incorrect, but seems the most straight forward implementation.
* Thus each Provision cares for "his" advice and just detaches when going away. Consequently, by default, advice provisions
* aren't freed during the lifetime of the application. We'll see if this causes problems.
*
*/
template<class AD>
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<AD>())
, 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<AD>());
UNIMPLEMENTED ("propagate binding change to index");
}
};
/**
* Access point for the advised entity (client).
* TODO type comment
*/
template<class AD>
class Request
: public PointOfAdvice
{
typedef const Provision<AD> 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<AD>())
{
UNIMPLEMENTED ("registration with the index");
}
~Request()
{
UNIMPLEMENTED ("detach from index");
}
AD const&
getAdvice() const
{
AdviceProvision* solution = static_cast<AdviceProvision*> (getSolution (*this));
if (!solution)
return this->handleMissingSolution();
else
return solution->getAdvice();
}
void
defineBinding (Literal topic)
{
setBindingPattern (Binding(topic).addTypeGuard<AD>());
UNIMPLEMENTED ("propagate binding change to index");
}
};
}} // namespace lib::advice
#endif