LUMIERA.clone/src/steam/mobject/session/query/fake-configrules.hpp

306 lines
10 KiB
C++
Raw Normal View History

/*
FAKE-CONFIGRULES.hpp - dummy implementation of the config rules system
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2008, Hermann Vosseler <Ichthyostega@web.de>
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **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.
2010-12-17 23:28:49 +01:00
*/
/** @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"
2012-12-03 00:18:18 +01:00
#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;
2011-12-03 02:56:50 +01:00
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;
2008-04-07 03:19:24 +02:00
namespace { // internal details
2008-04-07 03:19:24 +02:00
/** a traits-class to define the smart-ptr to wrap the result */
template<class TY>
2025-06-07 23:59:57 +02:00
struct WrapReturn { using Wrapper = P<TY>; };
2008-04-07 03:19:24 +02:00
template<>
2025-06-07 23:59:57 +02:00
struct WrapReturn<ProcPatt> { using Wrapper = PProcPatt; };
2008-04-07 03:19:24 +02:00
/** 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
2008-04-07 03:19:24 +02:00
* parse and evaluate the query. @note query is modified if "default" ... */
template<typename TY>
2008-04-07 03:19:24 +02:00
inline bool
is_defaults_query (Query<TY> const& querySpec)
2008-04-07 03:19:24 +02:00
{
return querySpec.usesPredicate ("default");
2008-04-07 03:19:24 +02:00
}
template<typename WRA>
inline bool
exists(WRA const& solution)
{
return bool(solution);
}
2008-04-07 03:19:24 +02:00
} // 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
2025-06-07 23:59:57 +02:00
* in automatically managed heap memory.
*/
class MockTable
: public steam::ConfigResolver
{
2025-06-07 23:59:57 +02:00
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....
2025-06-07 23:59:57 +02:00
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 ();
2025-06-07 23:59:57 +02:00
/** shortcut for simply accessing a table entry */
template<class STRU>
any&
item (string const& querySpec)
{
return answer_[Query<STRU>(querySpec)];
}
};
Upgrade: address warnings -- shaddowing overloaded-virtual This is an advanced diagnostics added (presumably) with GCC-13 and attempts to protect against an insidious side-effect of ''overload resolution'' Basically C++ (like its ancestor C) is oriented towards direct linkage and adds the OO-style dynamic dispatch (through virtual functions and a VTable) only as an extension, which must be requested explicitly. Thus the resolution of ''overloads'' (as opposed to ''overridden'' virtual functions) always takes precedence and happens within the directly visible scope, which can cause the compiler to perform an implicit conversion instead of invoking a different virtual function, which is defined in a base class. However, this diagnostics seems to be implemented in an overly zealous way: The compiler warns at the time of the type instantiation, and even in cases where it is effectively impossible to encounter this dangerous shadowing situation. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109740 This leads to several ill-guided warnings in the Lumiera code base, which unfortunately can only be addressed by disabling this diagnostics for all usages of some header. The reason is, we often generate chains of template instantiations driven by type lists, and in such usage pattern, it is not even possible to bring all the other inherited overloads into scope (with a using `BASE::func` clause), because such a specification would be ambiguous and result in a real compile error, because even the interface is generated from a chain of mix-in templates
2025-04-15 22:42:34 +02:00
// 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\"")
2025-06-07 23:59:57 +02:00
/**
* 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;
2008-02-29 18:58:29 +01:00
/** (dummy) implementation of the QueryHandler interface */
2025-06-07 23:59:57 +02:00
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
2025-06-07 23:59:57 +02:00
or(solution and solution == candidate) // simulates a real unification
)
return exists (solution = candidate);
}
return try_special_case(solution, q);
}
2025-06-07 23:59:57 +02:00
bool
try_special_case (Ret& solution, Query<TY> const& q)
{
2025-06-07 23:59:57 +02:00
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
2025-06-07 23:59:57 +02:00
if (this->detect_case (solution, q))
return resolve (solution, q);
2008-02-29 18:58:29 +01:00
return exists (solution = Ret()); // fail: return default-constructed empty smart ptr
}
};
/** Hook for treating very special cases for individual types only */
2008-04-07 03:19:24 +02:00
template<class TY>
2025-06-07 23:59:57 +02:00
inline bool
MockTable::detect_case (typename WrapReturn<TY>::Wrapper&, Query<TY> const&)
{
return false;
}
2008-04-07 03:19:24 +02:00
template<>
2025-06-07 23:59:57 +02:00
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);
2008-04-08 03:21:24 +02:00
const string pipeID = q.extractID("pipe");
const string streamID = q.extractID("stream");
2025-06-07 23:59:57 +02:00
if (candidate and pipeID == candidate->getPipeID())
return set_new_mock_solution (q, candidate); // "learn" this solution to be "valid"
2025-06-07 23:59:57 +02:00
if (!isnil(pipeID) and not isnil(streamID))
return fabricate_matching_new_Pipe (q, pipeID, streamID);
2008-04-08 03:21:24 +02:00
2025-06-07 23:59:57 +02:00
if (not candidate and (not isnil(streamID) or not isnil(pipeID)))
return fabricate_just_new_Pipe (q);
return false;
}
template<>
2025-06-07 23:59:57 +02:00
inline bool
MockTable::detect_case (WrapReturn<const ProcPatt>::Wrapper& candidate, Query<const ProcPatt> const& q)
{
const string streamID = q.extractID("stream");
2025-06-07 23:59:57 +02:00
if (not candidate and not isnil(streamID))
return fabricate_ProcPatt_on_demand (q);
return false;
}
template<>
2025-06-07 23:59:57 +02:00
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<>
2025-06-07 23:59:57 +02:00
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);
}
2025-06-07 23:59:57 +02:00
/**
* Facade: Dummy Implementation of the query interface.
* Provides an explicit implementation using hard wired
* values for some types of interest for testing and debugging.
*/
2025-06-07 23:59:57 +02:00
class MockConfigRules
: public InstantiateChained < steam::InterfaceTypes
2011-12-03 02:56:50 +01:00
, LookupPreconfigured // building block used for each of the types
2025-06-07 23:59:57 +02:00
, MockTable // for implementing the base class (interface)
2011-12-03 02:56:50 +01:00
>
{
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 === */
};
2025-06-07 23:59:57 +02:00
} // namespace query
}}} // namespace steam::mobject::session
#endif