lumiera_/src/common/query/defs-manager.hpp

211 lines
9.3 KiB
C++

/*
DEFS-MANAGER.hpp - access to preconfigured default objects and definitions
Copyright (C) Lumiera.org
2008, 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 defs-manager.hpp
** Management of defaults and default rules.
** It is one of the central ideas to shape the behaviour of Lumiera
** not through hard wired procedures, but rather by the combination of
** pattern elements driven by rules. A key element within this approach
** is the notion of an *default*: Whenever some concrete calculation or
** behaviour needs an additional element as missing link, in order to
** reach its intrinsic goals, we (the developers) ask ourselves if the
** for of this additional element can be derived from first principles
** or if it is already determined by the input set driving the operation.
** If this is _not_ the case (and the expectation is that most elements
** are not fully determined), then we _query for an default._ This pattern
** creates a lot of implicit extension points, which are safe to use, since
** they are defined on a logical level. We ask for something to fulfil a
** given contract.
**
** Contrast this to a system, which just has arbitrarily built-in flexibility
** by parametrisation variables: such systems are known to be brittle, since
** the _parameter values_ are implicitly tied to the behaviour of the operation
** through _shared knowledge_ of implementation details. It requires both
** "Druid knowledge" and an overall focus on the implementation mechanics to
** adjust such parameters. In this respect, a logical query for defaults is
** quite different, insofar it forces the implementation to respect abstractions
** on a very fine grained level, and it allows to express the actual defaults
** by rules which talk the language of the usage context, not the implementation
** context. The user shall be able to define generic (logical) rules how the
** Session should behave for the given editing project. And by combination
** with an additional set of logical consistency rules, a resolution engine
** can figure out what would be the right _default element_ to use at a
** given point in the implementation.
**
** @note as of 2017, we are far from such a system, but it is of uttermost
** importance that we build our implementations with this goal in mind
**
** # Configuration Query Interface
** The [Defaults Manager](\ref lumiera::query::DefsManager) exposes an interface
** similar to a database. The intended audience of this interface is the writer
** of low-level implementation code. This facade interface is meant to create
** a more familiar front-end to an essentially logic and rules driven
** configuration system. And, as an additional benefit, it can be implemented
** just by a glorified table lookup. Which indeed is what we do until the more
** elaborate rules based system is in place.
**
** An implementation process in need for some missing link typically knows the
** _type of that missing element._ Which means, this type is possibly an abstract
** type and defines the contract any solution has to fulfil. Thus, the usage context
** can just demand "give me a suitable XYZ!". In practice, there are several flavours
** to this interaction, each of which is expressed by a dedicated method on the
** DefsManager facade interface:
** - we can just *retrieve* a suitable solution element, no questions asked.
** - we can *query* for a suitable solution, while limiting the search to what
** is already known and defined. This entails the possibility that there is
** no known solution yet.
** - we can *demand to fabricate* a suitable solution element. Such a call might
** still deliver something already fabricated, but the emphasis is on the
** "make it so" demand.
** - we can *provide and associate* a solution element we created ourselves,
** to be available from now on, and associated with a given query. Even
** this call implicates the necessity for the query to be fulfilled by
** the given element, which, as a consequence includes the possibility
** of failure.
** - for sake of completeness, we can also require a specific element to
** be purged from knowledge
**
** ## Fake implementation
** As of 2018, the Lumiera project still has to reach the goal of a complete
** running engine; we are proceeding with partial integrations for the time being.
** And we postpone advanced topics, like integration of an actual rules solver
** to future milestones. Meanwhile, we use a [fake implementation](\ref MockConfigRules)
** with preconfigured, hard-wired "answers" to some frequently encountered standard queries.
** This Fake implementation is configured and instantiated by the [Defaults Manager](\ref DefsManager)
**
** @see DefsManager_test
** @see DefsManagerImpl_test
**
*/
#ifndef LUMIERA_QUERY_DEFS_MANAGER_H
#define LUMIERA_QUERY_DEFS_MANAGER_H
#include "lib/p.hpp"
#include "lib/nocopy.hpp"
#include "common/query.hpp"
#include <memory>
namespace lumiera{
namespace query {
using lumiera::Query;
using std::unique_ptr;
namespace impl { class DefsRegistry; }
/**
* Organise a collection of preconfigured default objects.
* For various kinds of objects we can tweak the default parametrisation
* as part of the general session configuration. A ref to an instance of
* this class is accessible through the current session and can be used
* to fill in parts of the configuration of new objects, if the user
* code didn't give more specific parameters. Necessary sub-objects
* will be created on demand, and any default configuration, once
* found, will be remembered and stored with the current session.
*
* @note while the logic of defaults handling can be considered
* roughly final, as of 12/09 most of the actual object
* handling is placeholder code.
*/
class DefsManager
: util::NonCopyable
{
unique_ptr<impl::DefsRegistry> defsRegistry_;
public:
DefsManager () throw();
~DefsManager ();
/** common access point: retrieve the default object fulfilling
* some given conditions. May silently trigger object creation.
* @throw error::Config in case no solution is possible, which
* is considered \e misconfiguration.
*/
template<class TAR>
lib::P<TAR> operator() (Query<TAR> const&);
/** search through the registered defaults, never create anything.
* @return object fulfilling the query, \c empty ptr if not found.
*/
template<class TAR>
lib::P<TAR> search (Query<TAR> const&);
/** retrieve an object fulfilling the query and register it as default.
* The resolution is delegated to the ConfigQuery system (which may cause
* creation of new object instances)
* @return object fulfilling the query, \c empty ptr if no solution.
*/
template<class TAR>
lib::P<TAR> create (Query<TAR> const&);
/** register the given object as default, after ensuring it fulfils the
* query. The latter may cause some properties of the object to be set,
* trigger creation of additional objects, and may fail altogether.
* @return true if query was successful and object is registered as default
* @note only a weak ref to the object is stored
*/
template<class TAR>
bool define (lib::P<TAR> const&, Query<TAR> const& =Query<TAR>());
/** remove the defaults registration of the given object, if there was such
* @return false if nothing has been changed because the object wasn't registered
*/
template<class TAR>
bool forget (lib::P<TAR> const&);
/** @internal for session lifecycle */
void clear();
// Q: can we have something along the line of...?
//
// template
// < class TAR, ///< the target to query for
// template <class> class SMP ///< smart pointer class to wrap the result
// >
// SMP<TAR> operator() (const lumiera::Query<TAR>&);
// 12/09: according to my current knowledge of template metaprogramming, the answer is "no",
// but we could use a traits template to set up a fixed association of smart pointers
// and kinds of target object. This would allow to define a templated operator() returning
// the result wrapped into the right holder. But currently I don't see how to build a sensible
// implementation infrastructure backing such an interface.
//////////TICKET #452
};
}} // namespace lumiera::query
#endif