Agent wrapper generation
Sometimes the situation arises when you have a routine that you want to use as an agent, but the number of parameters required by the routine is fewer than those that will be passed by the agent call. An example from Eiffel Software's Vision2 library:
class EV_POINTER_BUTTON_ACTION_SEQUENCE inherit EV_ACTION_SEQUENCE [TUPLE [INTEGER, INTEGER, INTEGER, DOUBLE, DOUBLE, DOUBLE, INTEGER, INTEGER]] -- EV_ACTION_SEQUENCE [TUPLE [x, y, button: INTEGER; x_tilt, y_tilt, pressure: DOUBLE; screen_x, screen_y: INTEGER]]
If have have a routine that wants to know when a button is pressed, and which button it was, then it needs the `button'
argument,
but none of the others.
Best practice for this situation at the moment (according to Eric Bezault) is to write a wrapper routine that just passes on the `button'
argument.
A similar situation is when you have the correct number of arguments, but they are the wrong way round. This happened to me yesterday, so I had to write a wrapper routine.
It would be much nicer if the compiler were able to generate the necessary wrapper code for you. This artcile explores possible syntaxes for these problems.
Contents
Argument rotation
A simple solution for specifying a rotation of 2 arguments (or any permutation that does not drop arguments), is to extend the use of the question mark by suffixing it with a decimal specifying the number of the argument to be passed in that location. For example, to index a list of floating point numbers:
floats_list: DS_ARRAYED_LIST [REAL_64] -- Floating point numbers floats_map: DS_HASH_TABLE [INTEGER, REAL_64] -- Map of floating-point indices within `floats_array' build_map -- Build `floats_map' from `floats_list'. require floats_list_not_void: floats_list /= Void do create floats_map.make (floats_list.count) floats_list.do_all_with_index (agent {DS_HASH_TABLE} floats_map.put_new (?2, ?1)) end
Dropping arguments
Various notations are possible. It seems to me to be sensible to say that if any occurence of a question mark suffixed by a decimal occurs, then only those arguments are passed, and the compiler should reason about the type of the agent accordingly. We can add a rule that unsuffixed question marks and suffixed question marks cannot be mixed in the same agent call.
The remaining problems are how to indicate that an agent should receive *no* open arguments at all. As empty parentheses are anathema to Eiffel programmers, and no parentheses already means to pass all arguments open, a suitable notation for no closed arguments and no open arguments might be a single underscore within parentheses. This can then be generalized to the case of one or more closed arguments, and no open arguments, by following all closed arguments with a comma followed by a single underscore.
The compiler now has all the information necessary to check the type of the agent, and to generate a wrapper routine (hopefully, a smart optimizing compiler can eliminate the wrapper altogether).
Alternative syntaxes for dropping arguments
Franck Arnaud does not like _ to indicate dropping remaining arguments. He prefers -? (or maybe it was ?-). I don't like this. If a question mark must be used, then I would prefer ?0, but I much prefer the underscore.
A thought that occured to me is should it be an error to supply the underscore, if there are no arguments to be passed? Probably it should.
An alternative syntax for dropping an argument might be ?-n, meaning drop the nth argument. If this was used, then all arguments must be accounted for at least once. For example, if you only wanted the button number from the action sequence at the top of the page, then the syntax would be agent notify_button_press (?3, ?-1, ?-2, ?-4, ?-5, ?-6, ?-7, ?-8)
. This has the advantage that if the number of expected arguments were to change in the future, then you get a compile error, so you can consider what to do about it. This might be important if the button argument were to disappear, for instance!
But I don't approve of changing APIs like that, without changing the name. So I still prefer to see agent notify_button_press (?3)
(or agent notify_button_press (_)
if all you are interested in knowing about is that the user clicked any mouse button).
Other alternatives for dropping all arguments:
agent notify_button_press (Void)
agent notify_button_press ({NONE})
Killing three birds with one stone
There are other patterns which currently need wrappers, but the proposed syntax would help with:
Repeating an open argument
Another requirement that has occasionally come up is to repeatedly use an open argument. For example:
event_dates: ARRAY [DATE] -- Dates on which event occurred . . . do event_dates.do_all (agent process_interval (?1, ?1)) end . . . process_interval (a_start, a_end: DATE) is -- Process event lasting from `a_start' to `a_end' inclusive. do . . . end
Using an open argument as a target
Sometimes you have a collection of objects and you want to invoke a routine of the object itself:
fleet: ARRAY [AEROPLANE] -- All planes in fleet do fleet.do_if (agent {AEROPLANE}(?1).refuel, agent {AEROPLANE}(?1).is_low_fuel) end