lumiera_/src/lib/verb-token.hpp
Ichthyostega 4356315021 diff-language interpreter: prefer to take payload by const&
each language token of our "linearised diff representation"
carries a payload data element, which typically is the piece
of data to be altered (added, mutated, etc).

Basically, these elements have value semantics and are
"sent over wire", and thus it seems natural when the
language interpreter functions accept that piece of payload
by-value. But since we're now sending GenNode elements as
parameter data in our diff, which typically are of the
size of 10 data elements (640 bit on a 64bit machine),
it seems more resonable to pass these argument elements
by const& through the interpreter function. This still
means we can (and will indeed) copy the mutated data
values when applying the diff, but we're able to
relay the data more efficiently to the point where
it's consumed.
2015-10-24 02:42:13 +02:00

129 lines
4 KiB
C++

/*
VERB-TOKEN.hpp - double dispatch based on DSL tokens
Copyright (C) Lumiera.org
2014, 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 02139, USA.
*/
/** @file verb-token.hpp
** Building blocks for a simple DSL using double dispatch to a handler function.
** Actually this is a specialised variation of the visitor pattern, where the
** "verb tokens" of the language are the domain objects accepting a "receiver"
** (visitor) to provide the concrete implementation function for each "verb".
**
** The intended usage is to set up a language comprised of several abstract actions ("verbs"),
** but to defer the concrete implementation to a specific set of handler functions, which
** is provided late, at application time. This way, we can send a sequence of verbs towards
** an unknown receiver, which supplies the actual meaning within the target context. In
** the end, there is a double-dispatch based both on the individual verb given and the
** concrete receiver, which needs to implement the interface used in the definition
** of the verb tokens. The handler functions defined within this interface may
** take additional arguments, which are passed through on application to
** the concrete receiver, e.g. `VERB_doit (receiver, arg1, arg2)`
** results in the invocation of \c receiver.doit(arg1,arg2)
**
** @see ...TODO
** @see VerbFunctionDispatch_test
**
*/
#ifndef LIB_VERB_TOKEN_H
#define LIB_VERB_TOKEN_H
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <utility>
#include <string>
#include <array>
namespace lib {
using std::string;
/**
* Action token implemented by double dispatch to a handler function,
* as defined in the "receiver" interface (parameter \c REC).
* The token is typically part of a DSL and can be applied
* to a concrete receiver subclass.
* @remarks while the included ID Literal is mostly for diagnostics,
* it also serves as identity for comparisons. Conceptually what
* we want is to compare the function "offset", but this leads
* into relying on implementation defined behaviour.
* @note the #VERB macro simplifies definition of actual tokens
*/
template<class REC, class SIG>
class VerbToken;
template<class REC, class RET, typename... ARGS>
class VerbToken<REC, RET(ARGS...)>
{
typedef RET (REC::*Handler) (ARGS...);
Handler handler_;
Literal token_;
public:
RET
applyTo (REC& receiver, ARGS&& ...args)
{
REQUIRE ("NIL" != token_);
return (receiver.*handler_)(std::forward<ARGS>(args)...);
}
operator string()
{
return string(token_);
}
VerbToken(Handler handlerFunction, Literal token)
: handler_(handlerFunction)
, token_(token)
{ }
VerbToken() : token_("NIL") { }
/* default copyable */
bool
operator== (VerbToken const& o) const ///< @remarks member pointers to virtual functions aren't comparable, for good reason
{
return token_ == o.token_;
}
bool
operator!= (VerbToken const& o) const
{
return token_ != o.token_;
}
};
#define VERB(RECEIVER, FUN) VERB_##FUN (&RECEIVER::FUN, STRINGIFY(FUN))
} // namespace lib
#endif /*LIB_VERB_TOKEN_H*/