Packages

Ceylan-WOOPER, a Wrapper for Object-Oriented Programming in Erlang, as an OTP active application here (see http://wooper.esperide.org)

Current section

Files

Jump to
wooper include wooper_execute_functions.hrl
Raw

include/wooper_execute_functions.hrl

% Copyright (C) 2003-2020 Olivier Boudeville
%
% This file is part of the Ceylan-WOOPER library.
%
% This library is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License or
% the GNU General Public License, as they are published by the Free Software
% Foundation, either version 3 of these Licenses, or (at your option)
% any later version.
% You can also redistribute it and/or modify it under the terms of the
% Mozilla Public License, version 1.1 or later.
%
% This library 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 Lesser General Public License and the GNU General Public License
% for more details.
%
% You should have received a copy of the GNU Lesser General Public
% License, of the GNU General Public License and of the Mozilla Public License
% along with this library.
% If not, see <http://www.gnu.org/licenses/> and
% <http://www.mozilla.org/MPL/>.
%
% Author: Olivier Boudeville [olivier (dot) boudeville (at) esperide (dot) com]
% Modular WOOPER header gathering all execute{Request,Oneway}* primitives that
% shall be (explicitly) used when having to call a method of this class from
% this class (i.e. when implementing methods calling other methods).
% Implementation notes:
%
% The initial request_sender field shall be preserved; for example, the body of
% a request might include a executeOneway call; if nothing was done, the oneway
% would detect a request/oneway mismatch; if request_sender was set to
% 'undefined', then the request would have lost the memory of its caller. Hence
% that field must be saved and restored in each execute* call, to allow nesting.
% Implementation section (debug mode managed in the called wooper_* helpers)
% Section for requests.
% Parameter-less request, calling implicitly any overridden version of the
% method.
%
% Returns an updated state and a result.
%
-spec executeRequest( wooper:state(), request_name() ) ->
{ wooper:state(), method_internal_result() }.
executeRequest( State, RequestAtom ) when is_record( State, state_holder )
andalso is_atom( RequestAtom ) ->
%trace_utils:debug_fmt( "executeRequest/2: executing ~s() from ~s.",
% [ RequestAtom, State#state_holder.actual_class ] ),
wooper_handle_local_request_execution( RequestAtom, State,
_ArgumentList=[] );
executeRequest( State, RequestAtomError )
when is_record( State, state_holder ) ->
wooper:log_error( "when executing local request: '~p' is not an atom.",
[ RequestAtomError ], State ),
throw( { wooper_invalid_request_call, RequestAtomError } );
executeRequest( StateError, RequestAtom ) when is_atom( RequestAtom ) ->
wooper:log_error( "when executing request ~p: "
"first parameter should be a state, not '~p'.",
[ RequestAtom, StateError ], ?MODULE ),
throw( { wooper_invalid_request_call, RequestAtom } );
executeRequest( StateError, RequestAtomError ) ->
wooper:log_error( "when executing request: '~p' is not a state and "
"'~p' is not an atom.",
[ StateError, RequestAtomError ], ?MODULE ),
throw( { wooper_invalid_request_call, StateError, RequestAtomError } ).
% Parameter-less const request, calling implicitly any overridden version of the
% method.
%
% Returns only a result.
%
-spec executeConstRequest( wooper:state(), request_name() ) ->
method_internal_result().
executeConstRequest( State, RequestAtom ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
{ _State, Result } = executeRequest( State, RequestAtom ),
Result.
% Allows to call synchronously from the code of a given class its actual
% overridden methods (requests, here), including from child classes.
%
% Example: If in a start method of an EngineVehicle class one wants to call the
% (possibly overridden by, say, a class Car) startEngine method, then
% executeRequest should be used: 'MyVehicle ! {startEngine..' would not be
% synchronous, startEngine() would call EngineVehicle:startEngine instead of
% Car:startEngine when called from a Car instance, and of course EngineVehicle
% should know nothing from its Car child class.
%
% Note: Stripped-down version of wooper_main_loop.
%
-spec executeRequest( wooper:state(), request_name(), method_arguments() ) ->
{ wooper:state(), method_internal_result() }.
executeRequest( State, RequestAtom, ArgumentList ) when
is_record( State, state_holder ) andalso is_atom( RequestAtom )
andalso is_list( ArgumentList ) ->
%trace_utils:debug_fmt( "executeRequest/3 with list: executing ~s(~w) "
% "from ~s.",
% [ RequestAtom, ArgumentList, State#state_holder.actual_class ] ),
wooper_handle_local_request_execution( RequestAtom, State, ArgumentList );
% Here the third parameter is not a list:
executeRequest( State, RequestAtom, StandaloneArgument ) when
is_record( State, state_holder ) andalso is_atom( RequestAtom )->
%trace_utils:debug_fmt( "executeRequest/3 with standalone argument: "
% "executing ~s(~w) from ~s.",
% [ RequestAtom, StandaloneArgument, State#state_holder.actual_class ] ),
wooper_handle_local_request_execution( RequestAtom, State,
_ArgumentList=[ StandaloneArgument ] );
% Catches all errors:
executeRequest( StateError, RequestAtom, _LastArg )
when is_atom( RequestAtom ) ->
wooper:log_error( "when executing request ~p: "
"first parameter should be a state, not '~p'.",
[ RequestAtom, StateError ], ?MODULE ),
throw( { wooper_invalid_request_call, RequestAtom } );
executeRequest( State, RequestAtomError, _LastArg )
when is_record( State, state_holder ) ->
wooper:log_error( "when executing request: '~p' is not an atom.",
[ RequestAtomError ], State ),
throw( { wooper_invalid_request_call, RequestAtomError } );
executeRequest( StateError, RequestAtomError, _LastArg ) ->
wooper:log_error( "when executing request: first parameter should "
"be a state, not '~p', and '~p' is not an atom.",
[ StateError, RequestAtomError ], ?MODULE ),
throw( { wooper_invalid_request_call, RequestAtomError } ).
% Allows to call synchronously from the code of a given class its actual
% overridden methods (const requests, here), including from child classes.
%
-spec executeConstRequest( wooper:state(), request_name(),
method_arguments() ) -> method_internal_result().
executeConstRequest( State, RequestAtom, ArgumentList ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
{ _State, Result } = executeRequest( State, RequestAtom, ArgumentList ),
Result.
% Parameter-less request, calling the version of the method as defined in the
% specified class.
%
-spec executeRequestAs( wooper:state(), classname(), request_name() ) ->
{ wooper:state(), method_internal_result() }.
executeRequestAs( State, Classname, RequestAtom )
when is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( RequestAtom ) ->
%trace_utils:debug_fmt( "executeRequestAs/3: executing ~s() from ~s "
% "with ~s.",
% [ RequestAtom, State#state_holder.actual_class, Classname ]),
wooper_handle_local_request_execution_as( RequestAtom, State,
_ArgumentList=[], Classname );
executeRequestAs( StateError, Classname, RequestAtom )
when is_atom( Classname ) andalso is_atom( RequestAtom ) ->
wooper:log_error( "when executing request ~p in the context of class ~s: "
"first parameter should be a state, not '~p'.",
[ RequestAtom, Classname, StateError ], ?MODULE ),
throw( { wooper_invalid_request_call, RequestAtom } );
executeRequestAs( _State, ClassnameError, RequestAtomError ) ->
wooper:log_error( "when executing request in a class context: "
"'~p' and '~p' should both be atoms.",
[ ClassnameError, RequestAtomError ], ?MODULE ),
throw( { wooper_invalid_request_call, ClassnameError, RequestAtomError } ).
% Parameter-less const request, calling the version of the method as defined in
% the specified class.
%
-spec executeConstRequestAs( wooper:state(), classname(), request_name() ) ->
method_internal_result().
executeConstRequestAs( State, Classname, RequestAtom ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
{ _State, Result } = executeRequestAs( State, Classname, RequestAtom ),
Result.
% Allows to call synchronously from the code of a given class overridden methods
% (requests, here) as defined in specified classes.
%
% Note: Stripped-down version of wooper_main_loop.
%
-spec executeRequestAs( wooper:state(), classname(), request_name(),
method_arguments() ) -> { wooper:state(), method_internal_result() }.
executeRequestAs( State, Classname, RequestAtom, ArgumentList ) when
is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( RequestAtom ) andalso is_list( ArgumentList ) ->
%trace_utils:debug_fmt( "executeRequestAs/4 with list: executing ~s(~w) "
% "from ~s with ~s.", [ RequestAtom, ArgumentList,
% State#state_holder.actual_class, Classname ] ),
wooper_handle_local_request_execution_as( RequestAtom, State,
ArgumentList, Classname );
% Here the third parameter is not a list:
executeRequestAs( State, Classname, RequestAtom, StandaloneArgument ) when
is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( RequestAtom ) ->
%trace_utils:debug_fmt( "executeRequestAs/3 with standalone argument: "
% "executing ~s(~w) from ~s with ~s.",
% [ RequestAtom, StandaloneArgument, State#state_holder.actual_class,
% Classname ] ),
wooper_handle_local_request_execution_as( RequestAtom, State,
_ArgumentList=[ StandaloneArgument ], Classname );
% Error cases below:
executeRequestAs( StateError, Classname, RequestAtom, _LastArg )
when is_atom( Classname ) andalso is_atom( RequestAtom ) ->
wooper:log_error( "when executing request ~p: "
"first parameter should be a state, not '~p'.",
[ RequestAtom, StateError ], ?MODULE ),
throw( { wooper_invalid_request_call, RequestAtom } );
% Catches all remaining errors:
executeRequestAs( _State, ClassnameError, RequestAtomError, _LastArg ) ->
wooper:log_error( "when executing request: both '~p' (classname) and "
"'~p' (request name) should be atoms.",
[ ClassnameError, RequestAtomError ], ?MODULE ),
throw( { wooper_invalid_request_call, ClassnameError, RequestAtomError } ).
% Allows to call synchronously from the code of a given class overridden methods
% (const requests, here) as defined in specified classes.
%
% Note: Stripped-down version of wooper_main_loop.
%
-spec executeConstRequestAs( wooper:state(), classname(), request_name(),
method_arguments() ) -> method_internal_result().
executeConstRequestAs( State, Classname, RequestAtom, ArgumentList ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
{ _State, Result } = executeRequestAs( State, Classname, RequestAtom,
ArgumentList ),
Result.
% Section for oneways:
% Parameter-less oneway.
%
-spec executeOneway( wooper:state(), oneway_name() ) -> wooper:state().
executeOneway( State, OnewayAtom ) when is_record( State, state_holder )
andalso is_atom( OnewayAtom ) ->
%trace_utils:debug_fmt( "executeOneway/2: executing ~s() from ~s.",
% [ OnewayAtom, State#state_holder.actual_class ] ),
wooper_handle_local_oneway_execution( OnewayAtom, State, _ArgumentList=[] );
executeOneway( State, OnewayError ) when is_record( State, state_holder ) ->
wooper:log_error( "when executing a oneway: its name should be an atom, "
"not '~p'.", [ OnewayError ], State ),
throw( { wooper_invalid_oneway_call, OnewayError } );
executeOneway( StateError, OnewayAtom ) when is_atom( OnewayAtom ) ->
wooper:log_error( "when executing oneway ~p: "
"first parameter should be a state, not '~p'.",
[ OnewayAtom, StateError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayAtom } );
executeOneway( StateError, OnewayError ) ->
wooper:log_error( "when executing oneway: '~s' is not a state and "
"'~p' is not an atom.",
[ StateError, OnewayError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayError } ).
% Parameter-less const oneway.
%
-spec executeConstOneway( wooper:state(), oneway_name() ) -> void().
executeConstOneway( State, OnewayAtom ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
_State = executeOneway( State, OnewayAtom ).
% Most classical oneway.
%
-spec executeOneway( wooper:state(), oneway_name(), method_arguments() ) ->
wooper:state().
executeOneway( State, OnewayAtom, ArgumentList ) when
is_record( State, state_holder ) andalso is_atom( OnewayAtom )
andalso is_list( ArgumentList ) ->
%trace_utils:debug_fmt( "executeOneway/3 with list: executing ~s(~w) "
% "from ~s.",
% [ OnewayAtom, ArgumentList, State#state_holder.actual_class ] ),
wooper_handle_local_oneway_execution( OnewayAtom, State, ArgumentList );
% Here third parameter is not a list:
executeOneway( State, OnewayAtom, StandaloneArgument ) when
is_record( State, state_holder ) andalso is_atom( OnewayAtom ) ->
%trace_utils:debug_fmt( "executeOneway/3 with standalone argument: "
% "executing ~s(~w) from ~s.",
% [ OnewayAtom, StandaloneArgument, State#state_holder.actual_class ] ),
wooper_handle_local_oneway_execution( OnewayAtom, State,
[ StandaloneArgument ] );
% All errors caught below:
executeOneway( StateError, OnewayAtom, _LastArg ) when is_atom( OnewayAtom ) ->
wooper:log_error( "when executing oneway ~p: "
"first parameter should be a state, not '~p'.",
[ OnewayAtom, StateError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayAtom } );
executeOneway( State, OnewayAtomError, _LastArg )
when is_record( State, state_holder ) ->
wooper:log_error( "when executing oneway: '~p' is not an atom.",
[ OnewayAtomError ], State ),
throw( { wooper_invalid_oneway_call, OnewayAtomError } );
executeOneway( _State, OnewayAtomError, _LastArg ) ->
wooper:log_error( "when executing oneway: '~p' is not an atom.",
[ OnewayAtomError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayAtomError } ).
% Most classical const oneway.
%
-spec executeConstOneway( wooper:state(), oneway_name(), method_arguments() ) ->
void().
executeConstOneway( State, OnewayAtom, ArgumentList ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
_State = executeOneway( State, OnewayAtom, ArgumentList ).
% Allows to call synchronously from the code of a given class its actual
% overridden methods (oneways, here), including from child classes.
%
% Example: If in a start method of a EngineVehicle class one wants to call the
% (possibly overridden by, say, a class Car) startEngine method, then
% executeOneway should be used: 'MyVehicle ! startEngine' would not be
% synchronous, startEngine() would call EngineVehicle:startEngine instead of
% Car:startEngine when called from a Car instance, and of course EngineVehicle
% should know nothing from its Car child class.
%
-spec executeOnewayAs( wooper:state(), classname(), oneway_name() ) ->
wooper:state().
executeOnewayAs( State, Classname, OnewayAtom )
when is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( OnewayAtom ) ->
%trace_utils:debug_fmt( "executeOnewayAs/3: executing ~s() from ~s.",
% [ OnewayAtom, State#state_holder.actual_class ] ),
wooper_handle_local_oneway_execution_as( OnewayAtom, State,
_ArgumentList=[], Classname );
executeOnewayAs( State, Classname, OnewayAtom )
when is_record( State, state_holder ) ->
wooper:log_error( "when executing oneway: '~p' (oneway name) and "
"'~p' (class name) should be both atoms.",
[ OnewayAtom, Classname ] ),
throw( { wooper_invalid_oneway_call, OnewayAtom } );
executeOnewayAs( StateError, Classname, OnewayAtom ) ->
wooper:log_error( "when executing oneway ~p with ~s: "
"first parameter should be a state, not '~p'.",
[ OnewayAtom, Classname, StateError ] ),
throw( { wooper_invalid_oneway_call, OnewayAtom } ).
% Allows to call synchronously from the code of a given class its actual
% overridden methods (const oneways, here), including from child classes.
%
-spec executeConstOnewayAs( wooper:state(), classname(), oneway_name() ) ->
void().
executeConstOnewayAs( State, Classname, OnewayAtom ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
_State = executeOnewayAs( State, Classname, OnewayAtom ).
% Allows to call synchronously from the code of a given class the oneway defined
% in specified class, instead of determining it from the instance virtual table.
%
% Note: Stripped-down version of wooper_main_loop.
%
-spec executeOnewayAs( wooper:state(), classname(), oneway_name(),
method_arguments() ) -> wooper:state().
executeOnewayAs( State, Classname, OnewayAtom, ArgumentList ) when
is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( OnewayAtom ) andalso is_list( ArgumentList ) ->
%trace_utils:debug_fmt( "executeOneway/4 with list: executing ~s(~w) "
% "from ~s with ~s.",
% [ OnewayAtom, ArgumentList, State#state_holder.actual_class,
% Classname ] ),
wooper_handle_local_oneway_execution_as( OnewayAtom, State,
ArgumentList, Classname );
% Here third parameter is not a list:
executeOnewayAs( State, Classname, OnewayAtom, StandaloneArgument ) when
is_record( State, state_holder ) andalso is_atom( Classname )
andalso is_atom( OnewayAtom ) ->
%trace_utils:debug_fmt( "executeOnewayAs/4 with standalone argument: "
% "executing ~s(~w) from ~s with ~s.",
% [ OnewayAtom, StandaloneArgument, State#state_holder.actual_class,
% Classname ] ),
wooper_handle_local_oneway_execution_as( OnewayAtom, State,
_ArgumentList=[ StandaloneArgument ], Classname );
executeOnewayAs( StateError, Classname, OnewayAtom, _LastArg )
when is_atom( Classname ) andalso is_atom( OnewayAtom ) ->
wooper:log_error( "when executing oneway ~p with ~s: "
"first parameter should be a state, not '~p'.",
[ OnewayAtom, Classname, StateError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayAtom } );
% Catches all remaining errors:
executeOnewayAs( _State, Classname, OnewayAtomError, _LastArg ) ->
wooper:log_error( "when executing oneway with ~s: both '~p' "
"and '~p' should be atoms.",
[ Classname, OnewayAtomError ], ?MODULE ),
throw( { wooper_invalid_oneway_call, OnewayAtomError } ).
% Allows to call synchronously from the code of a given class the const oneway
% defined in specified class, instead of determining it from the instance
% virtual table.
%
% Note: Stripped-down version of wooper_main_loop.
%
-spec executeConstOnewayAs( wooper:state(), classname(), oneway_name(),
method_arguments() ) -> void().
executeConstOnewayAs( State, Classname, OnewayAtom, ArgumentList ) ->
% Checks made by the callee; actual constness not checked yet shall derive
% from the transformed, corresponding method terminator:
%
_State = executeOnewayAs( State, Classname, OnewayAtom, ArgumentList ).