On Wed, Oct 04, 2000 at 09:42:51PM +0100, Sigbjørn Skjæret wrote:
> Just thought I'd say my thoughts on the different parameter setting function
> proposals we've had so far...
>
> Individual functions for each parameter:
>
> Pros:
> - None. ;)
>
> Cons:
> - Litters the API with "thousands" of functions.
> - If the parameter's type changes, the API has to change.
>
This never happens. First you can (mostly) prevent this by avoiding
miserliness of bits (Mark! It is easier to pass an boolean through a long
double argument than a long double argument through a boolean argument).
Secondly I prefer only to add functions. Old functions nevertheless slowly
dying if you stop testing them.
> - If a new parameter is introduced, the API has to change.
>
This is a generic property of an API: The API changes if the API changes.
You can't avoid this by reducing the API entry point to one function
(otherwise the Linux API has been not changed from Linux 0.01, it is
still the sys_call() function, which is an intreq 0x80).
Don't mix the two items "number of API entry points" and "complexity
of an API". Math libs don't become easier by:
math_call ( MATH_SIN_TYPE , MATH_ONE_ARGUMENT, MATH_DOUBLE_ARGUMENT, (double)0.707 );
math_call ( MATH_SQRT_TYPE, MATH_ONE_ARGUMENT, MATH_DOUBLE_ARGUMENT, (double)1.732 );
math_call ( MATH_POWER_TYPE, MATH_TWO_ARGUMENTS, MATH_DOUBLE_ARGUMENT,
(double)2.616, (double)1.616 );
Real programmers also merging all other functions (lame_ioctl, math_call,
sys_call, X11_ioctl, ...) and use only one function doing all things:
void do_stuff ( int do_, ... /* stuff */ );
Then you don't need a linker and can do linking by a hex editor ;-)
Also possible are:
DoIt ();
PerformDataFunction ();
HandleStuff ();
do_args_method ();
snafucate ();
do_args_method ( LIB_SELECT_LAME | LIB_LAME_SETUP_FUNC | LIB_LAME_SETUP_SFREQ |
LIB_SET_DBL_TYPE, (double)44100 );
do_args_method ( LIB_SELECT_POSIX | LIB_POSIX_IO_FUNC | LIB_POSIX_IO_WRITE |
(LIB_SET_INT_TYPE<<10) | (LIB_SET_VOIDPTR_TYPE<<5) |
(LIB_SET_SIZET_TYPE<<0),
(int)fd, (void*) buff, (size_t) length );
do_args_method ( LIB_SELECT_CLIB | LIB_CLIB_BASE_FUNC | LIB_CLIB_BASE_EXIT |
LIB_SET_INT_TYPE, (int)0 );
> Giving a parameter structure:
>
> Pros:
> - Hmmm, none. ;)
>
> Cons:
> - You have to be very careful not to disturb the order of the parameters.
> - You end up with a bunch of duplicates if you have to change parameters.
> - Different compilers can cause different alignments.
- you can't add a translation layer between API and lib.
> Giving tag-pairs on stack to one function which parses them:
>
> Pros:
> - API never has to change.
>
> Cons:
> - Littering with different tags for each type.
> - It's possible to pass the wrong type.
>
>
> Giving tag-pairs on stack to 3 functions (one for each type):
>
Why 3 functions? You only need an int ;-)
> Pros:
> - API never has to change.
> - One tag for any type.
>
> Cons:
> - It's still possible to pass the wrong type, but it's much clearer since
> the function itself states which to pass.
>
Giving tag-pairs on stack to one function which parses them:
const char* lame_ioctl ( enum TagItem, const char* TagValue );
returns NULL or the actually set parameter.
Note:
Both methods (thousands of functions and thousands of tags) are equivalent:
* use one function ( lame_ioctl() ) and thousands of constants
to tell this function what functionality is actually requested
* use thousands of functions (lame_xxxxx () ) to execute a
functionality
The difference is that second possiblity is more type safe, and the first
really looks like you never need to change the API, which is only partially
true (backward linking is possible, but you have still a runtime error,
this is often called error obscuring).
--
Frank "C programmers hate readable programs" Klemm
--
MP3 ENCODER mailing list ( http://geek.rcc.se/mp3encoder/ )