API Changes in 0.9

(For Japanese version, see 0.9におけるAPIの変更)

In Gauche release 0.9, Some C API changes introduced incompatibilities. It affects extension packages written in C.

The changes have been announced for long time, and the new API has been available if you #define GAUCHE_API_0_9 in your source. Extension packages that are actively maintained are likely to have switched using the new API. If a package is already using the new API, it should be able to compile without problem with 0.9.

However, if an extension module hasn't been updated for some time, you are likely to get compile errors. An easy workaround is to #define GAUCHE_API_PRE_0_9 in the C sources that don't compile. (If the C source is generated from a stub file, you have to edit Makefile to give -DGAUCHE_API_PRE_0_9 flag to the genstub command line.)

In other words, the visible API according to the preprocessor flag has been changed as follows:

Before 0.9

On or after 0.9

The compatibility feature using GAUCHE_API_PRE_0_9 will be removed by 1.0 release, so the extension package maintainers are encouraged to switch to the new API.

The following functions are changed incompatibly, or deprecated:

Eval-family functions

In older Gauche, Scm_Eval, Scm_EvalCString, and Scm_Apply didn't catch errors. If an uncaught exception was thrown while execution, calls to these functions never returned. The consequence was either one of the followings.

  1. If the VM is "active" when the call is made, that is, the calling C code itself is called from VM, then VM will catch the exception and invokes handlers if any.
  2. If the VM is "not active", for example, when you call Scm_Eval from C main() function, the exception is handled by the default handler which prints a message and just exits.

The first behavior is usually desirable, for it is the same way the Scheme function behaves. The problem arises when the C code have something to clean up; in which case you need to use SCM_UNWIND_PROTECT macro in gauche/vm.h.

The second behavior is bad. You could use SCM_UNWIND_PROTECT to prevent the program from exitting, but that's awkward, and it doesn't give you what kind of exception was thrown.

There's another minor problem in the old API; the caller can only receive single value, although the evaluated expression may yield multiple values. The caller have to make extra calls to retrieve multiple values from the VM.

Thus, in 0.9, two sets of these functions are provided. The first set, "fully featured" version, catches errors. They take an extra argument, a pointer to ScmEvalPacket structure, to which the caught exception is stored. The structure also stores multiple values, so the caller doesn't need to make extra calls to retrieve them.

This first set is a kind of official way to call Scheme program from C code at the beginning. So they got the original names: Scm_Eval, Scm_EvalCString, and Scm_Apply.

The second set, "lightweight" version, behaves the same as the old Scm_Eval etc. They are supposed to be called from the C code that itself is called from the VM. They don't catch errors, for you know that there's the VM waiting to catch it. They are named as Scm_EvalRec, Scm_EvalCStringRec and Scm_ApplyRec, where "Rec" indicates it is a recursive callback to the VM.

So, the easiest way to change old code to the new API is to rename old Scm_Eval etc. to Scm_EvalRec etc. However, if the code is making calls while the VM is not yet active, use of new Scm_Eval etc. is recommended.

Load-family functions

Likewise, Scm_Load, Scm_LoadFromPort and Scm_Require are changed to capture errors during loading. They now take an ScmLoadPacket structure, in which the captured exception is stored. The ScmLoadPacket structure must be initialized by the new Scm_LoadPacketInit function. The return value of these functions are now consistent; 0 indicates success, -1 indicates an error occurred during loading.

To get a behavior close to the old API, you have to rewrite the call to the old API functions as shown below:

The new flag SCM_LOAD_PROPAGATE_ERROR tells not to capture errors during loading. If the flag is given and an error occurs during loading, these functions never returns, and the error is caught by the VM (if one is active.)

Although the above code works like the old API, you may actually want not to pass SCM_LOAD_PROPAGATE_ERROR and let the function catch errors, and then check the return value to find out if load is successful.

Hash table functions

Hash tables get a brand new set of APIs. Most functions of the new API have different names. The old functions are kept for the backward compatibility, but using them is deprecated. A couple of functions changed their signature and the old code using them must be changed.

The main change is that now hash tables are implemented in two layers. The base layer, called ScmHashCore, is a plain C structure (not a Scheme object) and can be used as a general hash table (keys and values are not necessarily Scheme objects). The upper layer, ScmHashTable, is implemented using ScmHashCore---an instance of ScmHashTable is a Scheme object, and its keys and values are also Scheme objects.

Another change is to decouple implementation of hash tables from its interface. The old API returns a reference to the actual key-value pair of the hash table; you modify the pair and it affects the actual hash table. It has an advantage that an operation to "look up something, check the value, and modify it necessary" can be written in a way that it only searches the table once---the lookup operation returns the key-value pair, and the caller can modify it if it needs to alter hash table, without re-searching the key. Unfortunately though, this architecture requires the hashtable to store a key and its value side-by-side, and restricts the future option to have different hash table implementation.

The new API takes these changes into consideration. The higher-level API that operates on ScmHashTable structure have the following API:

The constructor, Scm_MakeHashTableSimple, currently restricts you to create eq?, eqv?, equal?, and string=? hash tables just like the Scheme API does. In future we will add more general constructor that allows you to specify the customized hash and comparison functions. (The reason such a general constructor is not provided yet is that we have to be careful to design what constraints should be applied to the customized hash/comparison functions in order to keep consistency with the Scheme world. You can give custom hash/comparison function to the lower-layer ScmHashCore structure, at your own risk.)

Scm_HashTableRef is like the Scheme conterparts; you pass a key, and gets a value. If the table doesn't have an entry for the key, whatever value given to falback is returned. You may pass SCM_UNBOUND to check if an entry actually exists, for it can't be a valid value of an entry.

Scm_HashTableSet is to modify the hashtable. It works in different modes according to the flags value, which may be a bitwise or of SCM_DICT_NO_CREATE and SCM_DICT_NO_OVERWRITE.

So, the old Scm_HashTablePut behaivor is realized by flags=0, and the old Scm_HashTableAdd behavior is realized by flags=SCM_DICT_NO_OVERWRITE.

Scm_HashTableDelete isn't changed.

The iterators are now defined on ScmHashCore instead of ScmHashTable. We are planning to provide ScmHashTable-level iterator interface in near future. For the time being, the old code should be changed in the following way:

String port functions

Scm_GetOutputString, Scm_GetOutputStringUnsafe, and Scm_GetRemainingInputString now take one additional argument, flags. An acceptable flag is SCM_STRING_INCOMPLETE to force the resulting string incomplete.

To get the old API behavior, you can just pass 0 to the flags argument.

Last modified : 2009/12/03 21:16:09 UTC