Agent wrapper generation

Revision as of 09:44, 30 May 2007 by Manus (Talk | contribs) (Added category)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.

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 occurred 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

(this needs a better example, as the above can already be done, since it is the first open argument. Find an example where the second open argument is used)