2019-04-14 15:38:57 +02:00
/*
VERB - VISITOR . hpp - double dispatch to arbitrary functions on a common interface
Copyright ( C ) Lumiera . org
2019 , 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 0213 9 , USA .
*/
/** @file verb-visitor.hpp
* * A specific double dispatch variation for function invocation .
* * While the classic visitor invokes a common ` handle ` function with varying arguments ,
2019-05-10 16:32:56 +02:00
* * here we allow for pre - binding of arbitrary _handler functions_ on an interface with
* * together with individual , suitable arguments . Yet similar to the classic visitor , the
* * _actual receiver_ can be a subclass of the visitor target interface , which causes the
* * _second_ indirection in the dispatch chain , thus completing a full double - dispatch .
* * Since the actually distinguishing factor is not so much a type , but a specific operation ,
* * we refer to the delayed invocation handles created by this binding as _verb token_
* * on a _receiver_ object ( which is the concrete visitor ) .
2019-04-14 15:38:57 +02:00
* *
* * This setup is an extension or derivative of the [ generic verb token ] ( \ ref verb - token - hpp )
* * used for the diff system and similar applications ; likewise the intended usage is to establish
* * a language comprised of several abstract actions ( " verbs " ) , but to allow the concrete operation
* * to be supplied later , at application time , and within a different code context . The most notable
* * use case is for the drawing of track contents in the user interface , where this pattern allows
* * the separation of actual drawing code from the nested track controller structure .
* *
2019-05-10 16:32:56 +02:00
* *
* * # # implementation technique
* *
* * The actual foundation is quite simple : we store a [ member pointer ] . Typically , this embedded
* * pointer - to - member shall be bound to an abstract virtual function on the _visitor interface . _
* * So basically the " verb " boils down to storing an offset into the VTable on the interface .
* * Later , on invocation , a reference to the actual _receiver_ is passed in , typically a concrete
* * subclass of the visitor interface . The invocation then combines this receiver reference with
* * the offset ( the member pointer ) to invoke the desired virtual function .
* *
* * However , the complications and technicalities arise from the ability to bind arbitrary
* * function signatures , even together with the actual arguments to use at invocation . Those
* * function arguments are supplied when creating the " packaged verb " , and thus need to be stored
* * within this package , together with the member - pointer . The result is a _materialised_ and
* * _delayed_ invocation of an abstract ( interface ) function , while the actual concrete function
* * implementation shall be supplied later . Obviously , such a [ " verb pack " ] ( \ ref VerbPack ) has
* * _value semantics_ - - we want to store it , copy it and pass it along , often even within a
* * sequence of " verbs " . And even more : we do not want to pass " hidden references " and we
* * do not want to rely on some management service and heap allocations . Rather , each
* * VerbPack shall be a self - contained value object . Within certain limitations ,
* * this is possible in C + + by using an opaque buffer embedded within the
* * outer value object ; basically the pre - defined buffer size must be
* * sufficient to hold all possible argument tuples to bind .
* *
* * The actual implementation here relies on two other components from the Lumiera library :
* * - the lib : : VerbToken provides us with the dispatch through a stored member pointer
* * - the lib : : PolymorphicValue allows to embed a subclass within an opaque inline buffer ,
* * just exposing the common interface .
* * Yet another challenge is the necessity to unpack the argument values from the storage
* * tuple and pass them to an ( at this point abstracted ) function with arbitrary signature .
* * Here we rely on the common implementation technique from [ std : : apply ] , here with the
* * special twist that we don ' t use a pre - bound function , but rather need to combine the
* * actual invocation target at the moment of the invocation .
* *
* * [ member pointer ] : https : //en.cppreference.com/w/cpp/language/pointer
* * [ std : : apply ] : https : //en.cppreference.com/w/cpp/utility/apply
* *
2019-04-14 15:38:57 +02:00
* * @ see [ drawing on the track canvas ] ( \ ref body - canvas - widget . cpp )
* * @ see VerbVisitorDispatch_test
* *
*/
# ifndef LIB_VERB_VISITOR_H
# define LIB_VERB_VISITOR_H
2019-04-16 18:21:51 +02:00
# include "lib/meta/variadic-helper.hpp"
# include "lib/polymorphic-value.hpp"
2019-04-17 18:32:21 +02:00
# include "lib/verb-token.hpp"
2019-04-14 15:38:57 +02:00
# include "lib/symbol.hpp"
# include "lib/util.hpp"
# include <utility>
# include <string>
2019-04-16 18:21:51 +02:00
# include <tuple>
2019-04-14 15:38:57 +02:00
namespace lib {
2019-04-17 18:32:21 +02:00
namespace {
using JustSomeIrrelvantType = struct { } ;
const size_t VERB_TOKEN_SIZE = sizeof ( VerbToken < JustSomeIrrelvantType , void ( void ) > ) ;
constexpr size_t
storageOverhead ( size_t argStorage )
2019-04-14 15:38:57 +02:00
{
2019-04-17 18:32:21 +02:00
return argStorage + VERB_TOKEN_SIZE ;
}
}
2019-04-16 18:21:51 +02:00
2019-05-10 02:19:01 +02:00
2019-04-16 18:21:51 +02:00
template < class REC , class RET >
2019-04-17 18:32:21 +02:00
struct VerbInvoker
2019-05-10 16:32:56 +02:00
: polyvalue : : CloneValueSupport < polyvalue : : EmptyBase > // mark and mix-in virtual copy construction support
2019-04-16 18:21:51 +02:00
{
2019-04-17 18:32:21 +02:00
virtual ~ VerbInvoker ( ) { }
2019-04-16 18:21:51 +02:00
virtual RET applyTo ( REC & ) = 0 ;
} ;
template < class REC , class SIG >
2019-05-10 16:32:56 +02:00
struct VerbHolder ;
2019-04-16 18:21:51 +02:00
template < class REC , class RET , typename . . . ARGS >
2019-05-10 16:32:56 +02:00
struct VerbHolder < REC , RET ( ARGS . . . ) >
2019-04-20 22:23:55 +02:00
: VerbInvoker < REC , RET >
2019-05-09 23:40:47 +02:00
, VerbToken < REC , RET ( ARGS . . . ) >
2019-04-16 18:21:51 +02:00
{
using Verb = VerbToken < REC , RET ( ARGS . . . ) > ;
using Args = std : : tuple < ARGS . . . > ;
/** meta-sequence to pick argument values from the storage tuple */
using SequenceIterator = typename meta : : BuildIdxIter < ARGS . . . > : : Ascending ;
Args args_ ;
2019-05-10 16:32:56 +02:00
VerbHolder ( typename Verb : : Handler handlerRef , Literal verbID , ARGS & & . . . args )
2019-05-09 23:40:47 +02:00
: Verb { handlerRef , verbID }
2019-04-16 18:21:51 +02:00
, args_ { std : : forward < ARGS > ( args ) . . . }
{ }
RET
applyTo ( REC & receiver ) override
{
return invokeVerb ( receiver , SequenceIterator ( ) ) ;
}
2019-05-09 23:40:47 +02:00
private :
2019-04-16 18:21:51 +02:00
template < size_t . . . idx >
RET
invokeVerb ( REC & receiver , meta : : IndexSeq < idx . . . > )
2019-04-20 17:27:47 +02:00
{ //////////////////////////////////////////TICKET #1006 | TICKET #1184 why do we need std::forward here? the target is a "perfect forwarding" function, which should be able to receive a LValue reference to the tuple element just fine...
2019-05-09 23:40:47 +02:00
return ( receiver . * Verb : : handler_ ) ( std : : get < idx > ( args_ ) . . . ) ;
2019-04-16 18:21:51 +02:00
}
} ;
template < class REC , class RET , size_t arg_storage >
class VerbPack
2019-04-17 18:32:21 +02:00
: public PolymorphicValue < VerbInvoker < REC , RET > , storageOverhead ( arg_storage ) >
2019-04-16 18:21:51 +02:00
{
2019-04-17 18:32:21 +02:00
using PolyHolder = PolymorphicValue < VerbInvoker < REC , RET > , storageOverhead ( arg_storage ) > ;
2019-04-16 18:21:51 +02:00
template < typename . . . ARGS >
2019-05-10 16:32:56 +02:00
using PayloadType = VerbHolder < REC , RET ( ARGS . . . ) > * ;
2019-04-16 18:21:51 +02:00
template < typename . . . ARGS >
using Handler = typename VerbToken < REC , RET ( ARGS . . . ) > : : Handler ;
public :
template < typename . . . ARGS >
VerbPack ( Handler < ARGS . . . > handler , Literal verbID , ARGS & & . . . args )
: PolyHolder ( PayloadType < ARGS . . . > ( ) , handler , verbID , std : : forward < ARGS > ( args ) . . . )
{ }
2019-04-20 17:27:47 +02:00
RET
applyTo ( REC & receiver )
{
VerbInvoker < REC , RET > & dispatch ( * this ) ;
return dispatch . applyTo ( receiver ) ;
}
2019-04-16 18:21:51 +02:00
} ;
2019-04-14 15:38:57 +02:00
} // namespace lib
# endif /*LIB_VERB_VISITOR_H*/