LUMIERA.clone/tests/core/steam/mobject/session/session-service-access-test.cpp
Ichthyostega 20392eee1c clean-up: successfully replaced the old fixed type sequence (closes: #987)
This resolves an intricate problem related to metaprogramming with
variadic templates and function signatures. Due to exceptional complexity,
a direct solution was blocked for several years, and required a better
organisation of the support code involved; several workarounds were
developed, gradually leading to a transition path, which could now
be completed in an focused clean-up effort over the last week.

Metaprogramming with sequences of types is organised into three layers:
- simple tasks can be solved with the standard facilities of the language,
  using pattern match with variadic template specialisations
- the ''type-sequence'' construct `Types<T...>` takes the centre stage
  for the explicit definition of collections of types; it can be re-bound
  to other variadic templates and supports simple direct manipulation
- for more elaborate and advanced processing tasks, a ''Loki-style type list''
  can be obtained from a type-sequence, allowing to perform recursive
  list processing task with a technique similar to LISP.
2025-06-07 18:04:59 +02:00

341 lines
8.2 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.

/*
SessionServiceAccess(Test) - accessing implementation level session services
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 session-service-access-test.cpp
** unit test \ref SessionServiceAccess_test
*/
#include "lib/test/run.hpp"
#include "steam/mobject/session.hpp"
#include "lib/meta/generator.hpp"
#include "lib/format-cout.hpp"
#include "lib/depend.hpp"
#include <boost/lexical_cast.hpp>
#include <memory>
#include <string>
namespace steam {
namespace mobject {
namespace session {
namespace test {
using lib::Depend;
using boost::lexical_cast;
using std::unique_ptr;
using std::string;
namespace { // what follows is a simulated (simplified) version
// of the complete Session + SessionManager setup.....
using lib::meta::Types;
using lib::meta::InstantiateChained;
/* === Interface level === */ //----------------corresponding-to-session.hpp
struct TSessManager;
typedef TSessManager& PSess;
struct TSession
{
virtual ~TSession () { }
static TSessManager& current;
virtual void externalOperation () =0;
};
struct TSessManager
{
/** access to the current session */
virtual TSession* operator-> () =0;
virtual void reset () =0;
virtual ~TSessManager() { };
};
/* === Service level API === */ //----------------internal-API-definition-headers
struct InternalAPI_1
{
virtual ~InternalAPI_1() {}
virtual uint getMagic() =0;
static InternalAPI_1& access();
};
struct InternalAPI_2
{
static void invokeImplementationService();
};
/* === Implementation level === */ //----------------corresponding-to-session-impl.hpp
struct TSessionImpl : TSession
{
static uint magic_;
/* ==== Session API ==== */
void externalOperation() ;
/* ==== Implementation level API ==== */
void implementationService() ;
/* ==== internals ==== */
TSessionImpl();
operator string() const;
};
template<class API, class IMPL>
struct TServiceAccessPoint;
template<class IMPL>
struct TServiceAccessPoint<InternalAPI_1, IMPL>
: IMPL
, InternalAPI_1
{
uint
getMagic ()
{
return IMPL::magic_;
}
};
template<class IMPL>
struct TServiceAccessPoint<InternalAPI_2, IMPL>
: IMPL
{
void
forwardServiceInvocation()
{
IMPL::implementationService();
}
};
template< typename IMPS
, class FRONT
, class SESS
>
class TSessionServices
: public InstantiateChained<typename IMPS::List, TServiceAccessPoint, SESS>
{
public:
static FRONT& current;
template<class API>
API&
get()
{
return *this;
}
};
/* === storage and basic session manager configuration === */
struct TSessManagerImpl;
using SessionImplAPI = TSessionServices< Types<InternalAPI_1,InternalAPI_2>
, TSessManagerImpl
, TSessionImpl
>;
struct TSessManagerImpl : TSessManager
{
unique_ptr<SessionImplAPI> pImpl_;
TSessManagerImpl()
: pImpl_{}
{ }
SessionImplAPI*
operator-> ()
{
if (!pImpl_)
this->reset();
return pImpl_.get();
}
/* ==== Manager API ==== */
void
reset ()
{
unique_ptr<SessionImplAPI> tmpS {new SessionImplAPI};
pImpl_.swap (tmpS);
}
};
uint TSessionImpl::magic_;
TSessManager& TSession::current = Depend<TSessManagerImpl>()();
//note: already during static initialisation
template<>
TSessManagerImpl& SessionImplAPI::current = static_cast<TSessManagerImpl&> (TSession::current);
/* === Implementation of service access === */ //----------------corresponding-to-session-services.cpp
InternalAPI_1&
InternalAPI_1::access()
{
return SessionImplAPI::current->get<InternalAPI_1>();
}
void
InternalAPI_2::invokeImplementationService()
{
SessionImplAPI::current->forwardServiceInvocation();
}
/* === Implementation of Session internals === */ //----------------corresponding-to-session-impl.cpp
TSessionImpl::operator string() const
{
return string("Session-Impl(")
+ lexical_cast<string>(magic_)
+ ")";
}
TSessionImpl::TSessionImpl()
{
++magic_;
cout << "creating new Session " << magic_ << endl;
}
void
TSessionImpl::externalOperation()
{
cout << this << "::externalOperation()" << endl;
}
/* ==== Implementation level API ==== */
inline void
TSessionImpl::implementationService()
{
cout << this << "::implementationService()" << endl;
}
} // (END) simulated session management
/***************************************************************************//**
* Verify the access mechanism both to the pubic session API and
* to implementation level APIs used by Steam-Layer internals.
*
* Actually, this test uses a simulated setup of the real session,
* complete with interfaces, implementation and session manager frontend.
*
* @see session-impl.hpp the real thing
* @see SessionServices;
*/
class SessionServiceAccess_test : public Test
{
virtual void
run (Arg)
{
access_defaultSession();
make_newSession();
invoke_implServices();
}
/** @test accessing an non-existing session
* causes creation of a new TSessionImpl instance.
* After that, the public API function gets invoked.
*/
void
access_defaultSession ()
{
cout << "Session not yet used...." << endl;
TSession::current->externalOperation();
}
/** @test invoking the management API to close the session.
* The next public API invocation will create
* a new TSessionImpl instance.
*/
void
make_newSession ()
{
TSession::current.reset();
TSession::current->externalOperation();
}
/** example of an one-liner, as it might be used
* internally by implementation code within Steam-Layer */
uint magic() { return InternalAPI_1::access().getMagic(); }
/** @test accessing implementation-level APIs */
void
invoke_implServices ()
{
cout << "current Session-Impl-ID = " << magic() << endl;
InternalAPI_2::invokeImplementationService();
cout << "now resetting this session." << endl;
TSession::current.reset();
InternalAPI_2::invokeImplementationService(); // invocation creates new session as side effect
cout << "current Session-Impl-ID = " << magic() << endl;
}
};
/** Register this test class... */
LAUNCHER (SessionServiceAccess_test, "function session");
}}}} // namespace steam::mobject::session::test