LUMIERA.clone/src/steam/mobject/session/query/fake-configrules.hpp
Ichthyostega 20f3252892 Upgrade: down with typename!!
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...)
2025-07-06 01:19:08 +02:00

305 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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