Yet another chainsaw massacre. One of the most obnoxious annoyances with C++ metaprogramming is the need to insert `typename` and `template` qualifiers into most definitions, to help the compiler to cope with the syntax, which is not context-free. The recent standards adds several clarifications, so that most of these qualifiers are redundant now, at least at places where it is unambiguously clear that only a type can be given. GCC already supports most of these relaxing rules (Clang unfortunately lags way behind with support of newer language features...)
305 lines
10 KiB
C++
305 lines
10 KiB
C++
/*
|
||
FAKE-CONFIGRULES.hpp - dummy implementation of the config rules system
|
||
|
||
Copyright (C)
|
||
2008, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
*/
|
||
|
||
|
||
/** @file fake-configrules.hpp
|
||
** Mock/Test/Debugging Implementation of the config rules system.
|
||
** Instead of actually parsing/analysing/resolving queries, this implementation
|
||
** uses a Table of hard wired queries together with preconfigured object instances
|
||
** as answer values. As of 1/2008 it is used to "keep the implementation work going"
|
||
** -- later on, when we use a real Prolog interpreter, it still may be useful for
|
||
** testing and debugging.
|
||
**
|
||
** @remarks the primary purpose of this header and fake-configrules.cpp is to define
|
||
** the type specialisations of the \c QueryHandler<TY>::resolve(solution,query)
|
||
** function(s). Below, there is a really confusing and ugly ping-pong game involving
|
||
** the faked solutions and the mocked defaults manager. This is spaghetti code, written
|
||
** for the reason everyone writes spaghetti code: to get away with it. So please look
|
||
** away, some day the real thing will be there, displacing this mess without further notice.
|
||
** @todo to be removed in Alpha, when integrating a real resolution engine /////////////////TICKET #710
|
||
**
|
||
** @see lumiera::Query
|
||
** @see lumiera::ConfigRules
|
||
**
|
||
*/
|
||
|
||
|
||
#ifndef MOBJECT_SESSION_QUERY_FAKECONFIGRULES_H
|
||
#define MOBJECT_SESSION_QUERY_FAKECONFIGRULES_H
|
||
|
||
#include "steam/mobject/session.hpp"
|
||
#include "steam/config-resolver.hpp"
|
||
#include "lib/depend-inject.hpp"
|
||
#include "lib/query-util.hpp"
|
||
#include "lib/util.hpp"
|
||
#include "lib/p.hpp"
|
||
|
||
#include <boost/any.hpp>
|
||
#include <string>
|
||
#include <map>
|
||
|
||
|
||
|
||
namespace steam {
|
||
namespace mobject {
|
||
namespace session {
|
||
namespace query {
|
||
|
||
namespace asset = steam::asset;
|
||
|
||
using asset::Pipe;
|
||
using asset::ProcPatt;
|
||
using asset::PProcPatt;
|
||
using steam::mobject::Session;
|
||
using lib::meta::InstantiateChained;
|
||
using lib::P;
|
||
|
||
using lumiera::Query;
|
||
using lumiera::QueryKey;
|
||
using lumiera::query::isFakeBypass; /////////TODO placeholder until there is a real resolution engine
|
||
|
||
using util::contains;
|
||
using util::isnil;
|
||
|
||
using boost::any;
|
||
using boost::any_cast;
|
||
|
||
|
||
|
||
|
||
|
||
namespace { // internal details
|
||
|
||
/** a traits-class to define the smart-ptr to wrap the result */
|
||
template<class TY>
|
||
struct WrapReturn { using Wrapper = P<TY>; };
|
||
|
||
template<>
|
||
struct WrapReturn<ProcPatt> { using Wrapper = PProcPatt; };
|
||
|
||
|
||
/** helper detecting if a query actually intended to retrieve a "default" object.
|
||
* This implementation is quite crude, of course it would be necessary actually to
|
||
* parse and evaluate the query. @note query is modified if "default" ... */
|
||
template<typename TY>
|
||
inline bool
|
||
is_defaults_query (Query<TY> const& querySpec)
|
||
{
|
||
return querySpec.usesPredicate ("default");
|
||
}
|
||
|
||
template<typename WRA>
|
||
inline bool
|
||
exists(WRA const& solution)
|
||
{
|
||
return bool(solution);
|
||
}
|
||
|
||
} // details (end)
|
||
|
||
|
||
|
||
/**
|
||
* the actual table holding preconfigured answers
|
||
* packaged as boost::any objects. MockTable is the implementation base;
|
||
* subclasses for the individual types are layered below to define the
|
||
* \c resolve(..) functions. Finally #MockConfigRules wraps things up.
|
||
*
|
||
* The implementation relies on boost::any records to stash the objects
|
||
* in automatically managed heap memory.
|
||
*/
|
||
class MockTable
|
||
: public steam::ConfigResolver
|
||
{
|
||
using Tab = std::map<QueryKey,any>;
|
||
|
||
Tab answer_;
|
||
bool isInit_;
|
||
|
||
protected:
|
||
MockTable ();
|
||
virtual void reset();
|
||
any const& fetch_from_table_for (QueryKey const& query);
|
||
|
||
// special cases....
|
||
template<class TY>
|
||
bool detect_case (typename WrapReturn<TY>::Wrapper&, Query<TY> const&);
|
||
bool fabricate_matching_new_Pipe (Query<Pipe> const& q, string const& pipeID, string const& streamID);
|
||
bool fabricate_just_new_Pipe (Query<Pipe> const& q);
|
||
bool fabricate_ProcPatt_on_demand (Query<const ProcPatt> const& q);
|
||
bool fabricate_Timeline_on_demand (Query<asset::Timeline> const& q);
|
||
bool fabricate_Sequence_on_demand (Query<asset::Sequence> const& q);
|
||
|
||
template<class TY>
|
||
bool set_new_mock_solution (Query<TY> const& q, typename WrapReturn<TY>::Wrapper& candidate);
|
||
|
||
|
||
private:
|
||
void fill_mock_table ();
|
||
|
||
/** shortcut for simply accessing a table entry */
|
||
template<class STRU>
|
||
any&
|
||
item (string const& querySpec)
|
||
{
|
||
return answer_[Query<STRU>(querySpec)];
|
||
}
|
||
};
|
||
|
||
|
||
// GCC > 13 warns at class definition when a new overload shadows an inherited virtual function.
|
||
// While theoretically correct, in practice any call will be dispatched through the base interface
|
||
// and it is not even possible to reach the concrete classes (and the implementation function is private).
|
||
// The workaround with a `using` clause is not applicable, since the name is ambiguous (by design)
|
||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109740
|
||
_Pragma("GCC diagnostic push") \
|
||
_Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"")
|
||
|
||
|
||
/**
|
||
* building block providing the
|
||
* mock implementation for a \em single type.
|
||
* We simply access a table holding pre-created objects.
|
||
*/
|
||
template<class TY, class BASE>
|
||
class LookupPreconfigured : public BASE
|
||
{
|
||
using Ret = WrapReturn<TY>::Wrapper;
|
||
|
||
/** (dummy) implementation of the QueryHandler interface */
|
||
virtual bool
|
||
resolve (Ret& solution, Query<TY> const& q)
|
||
{
|
||
any const& entry = this->fetch_from_table_for(q);
|
||
if (!isnil (entry))
|
||
{
|
||
Ret const& candidate (any_cast<Ret const&> (entry));
|
||
if (! solution
|
||
or(solution and solution == candidate) // simulates a real unification
|
||
)
|
||
return exists (solution = candidate);
|
||
}
|
||
return try_special_case(solution, q);
|
||
}
|
||
|
||
bool
|
||
try_special_case (Ret& solution, Query<TY> const& q)
|
||
{
|
||
if (solution and isFakeBypass(q)) // backdoor for tests
|
||
return exists (solution);
|
||
|
||
if (is_defaults_query (q))
|
||
{
|
||
Query<TY> defaultsQuery = q.rebuild().removeTerm("default");
|
||
return exists (solution = Session::current->defaults (defaultsQuery));
|
||
} // may lead to recursion
|
||
|
||
if (this->detect_case (solution, q))
|
||
return resolve (solution, q);
|
||
|
||
return exists (solution = Ret()); // fail: return default-constructed empty smart ptr
|
||
}
|
||
};
|
||
|
||
|
||
/** Hook for treating very special cases for individual types only */
|
||
template<class TY>
|
||
inline bool
|
||
MockTable::detect_case (typename WrapReturn<TY>::Wrapper&, Query<TY> const&)
|
||
{
|
||
return false;
|
||
}
|
||
template<>
|
||
inline bool
|
||
MockTable::detect_case (WrapReturn<Pipe>::Wrapper& candidate, Query<Pipe> const& q)
|
||
{
|
||
if (q.usesPredicate ("make"))
|
||
// used by tests to force fabrication of a new "solution"
|
||
return fabricate_just_new_Pipe (q);
|
||
|
||
const string pipeID = q.extractID("pipe");
|
||
const string streamID = q.extractID("stream");
|
||
|
||
if (candidate and pipeID == candidate->getPipeID())
|
||
return set_new_mock_solution (q, candidate); // "learn" this solution to be "valid"
|
||
|
||
if (!isnil(pipeID) and not isnil(streamID))
|
||
return fabricate_matching_new_Pipe (q, pipeID, streamID);
|
||
|
||
if (not candidate and (not isnil(streamID) or not isnil(pipeID)))
|
||
return fabricate_just_new_Pipe (q);
|
||
|
||
return false;
|
||
}
|
||
|
||
template<>
|
||
inline bool
|
||
MockTable::detect_case (WrapReturn<const ProcPatt>::Wrapper& candidate, Query<const ProcPatt> const& q)
|
||
{
|
||
const string streamID = q.extractID("stream");
|
||
|
||
if (not candidate and not isnil(streamID))
|
||
return fabricate_ProcPatt_on_demand (q);
|
||
return false;
|
||
}
|
||
|
||
template<>
|
||
inline bool
|
||
MockTable::detect_case (WrapReturn<asset::Timeline>::Wrapper& candidate, Query<asset::Timeline> const& q)
|
||
{
|
||
if (!candidate)
|
||
return fabricate_Timeline_on_demand (q);
|
||
return bool(candidate);
|
||
}
|
||
|
||
template<>
|
||
inline bool
|
||
MockTable::detect_case (WrapReturn<asset::Sequence>::Wrapper& candidate, Query<asset::Sequence> const& q)
|
||
{
|
||
if (!candidate)
|
||
return fabricate_Sequence_on_demand (q);
|
||
return bool(candidate);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/**
|
||
* Facade: Dummy Implementation of the query interface.
|
||
* Provides an explicit implementation using hard wired
|
||
* values for some types of interest for testing and debugging.
|
||
*/
|
||
class MockConfigRules
|
||
: public InstantiateChained < steam::InterfaceTypes
|
||
, LookupPreconfigured // building block used for each of the types
|
||
, MockTable // for implementing the base class (interface)
|
||
>
|
||
{
|
||
protected:
|
||
MockConfigRules (); ///< to be used only by the singleton factory
|
||
friend class lib::DependencyFactory<MockConfigRules>;
|
||
friend class lib::DependInject<ConfigResolver>::Local<MockConfigRules>;
|
||
public:
|
||
virtual ~MockConfigRules() {} ///< extends the ConfigResolver interface
|
||
|
||
|
||
/* === implementation of additional functions on the ConfigRules interface goes here === */
|
||
};
|
||
|
||
|
||
|
||
} // namespace query
|
||
}}} // namespace steam::mobject::session
|
||
#endif
|