mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
8 years ago
1 changed files with 169 additions and 0 deletions
@ -0,0 +1,169 @@ |
|||
===================== |
|||
API design guidelines |
|||
===================== |
|||
|
|||
Introduction |
|||
============ |
|||
|
|||
A consistent API design makes it easier to write applications: it should be |
|||
easy to remember API call naming, argument order, and other conventions, to |
|||
make it as easy as possible to write error-free code. |
|||
|
|||
API design decisions involve several important concerns: |
|||
|
|||
* Consistency: easy to remember API calls and arguments. |
|||
|
|||
* Versioning: easy to extend the API, avoid dead ends in design. |
|||
|
|||
* Footprint: minimize code footprint of Duktape internals and application |
|||
call site. |
|||
|
|||
* Performance: some API designs allow better performance than others. |
|||
|
|||
This document provides some notes on common API design issues and trade-offs. |
|||
The document is not intended to be complete, but just cover practical issues |
|||
as they come up. |
|||
|
|||
Naming |
|||
====== |
|||
|
|||
API calls |
|||
--------- |
|||
|
|||
All API calls are lowercase, underscore separated, and begin with the prefix |
|||
``duk_``. Duktape internals, not part of the public API, also have calls |
|||
matching this convention. The public API calls are documented in the public |
|||
API header (actually a segment of ``duktape.h`` in the distributable) and the |
|||
API documentation. |
|||
|
|||
Existing convention for operating on value stack items: |
|||
|
|||
* ``duk_get_XXX()`` gets a value without modifying the value on the stack. |
|||
If the value doesn't match expected type, some default value (like NULL |
|||
or zero) is returned. |
|||
|
|||
* ``duk_require_XXX()`` gets a value without modifying the value on the stack. |
|||
If the value doesn't match expected type, an error is thrown. |
|||
|
|||
* ``duk_to_XXX()`` coerces a value in-place into desired type. |
|||
|
|||
Varargs API calls: |
|||
|
|||
* Named with a trailing ``_va()``, for example ``duk_error_va()``. |
|||
|
|||
- Existing exception: ``duk_push_sprintf()``. |
|||
|
|||
Existing convention for safe/protected calls: |
|||
|
|||
* All calls are unsafe by default, i.e. may throw errors. |
|||
|
|||
* ``duk_pXXX()`` calls are protected, i.e. they catch errors. API call |
|||
return value indicates success or error, and error is typically passed |
|||
via the value stack. |
|||
|
|||
* ``duk_safe_XXX()`` calls are safe, i.e. don't throw, but they don't |
|||
provide a success/error indication. |
|||
|
|||
- Existing exception: ``duk_safe_call()`` doesn't currently match this |
|||
convention: it's a protected call (catches an error and returns a |
|||
success/failure indicator). |
|||
|
|||
Arguments |
|||
--------- |
|||
|
|||
Lowercase, underscore separated. No other strict guidelines, some current |
|||
names: |
|||
|
|||
* ``ctx`` |
|||
|
|||
* ``idx``, ``obj_index``, ``from_index``, ``to_index``, ``index1``, ``index2`` |
|||
|
|||
* ``arr_index`` |
|||
|
|||
* ``flags``, ``enum_flags`` |
|||
|
|||
* ``len``, ``ptr``, ``key`` |
|||
|
|||
Avoid the argument names: |
|||
|
|||
* ``index``: conflicts with a function from strings.h. |
|||
|
|||
Typing |
|||
====== |
|||
|
|||
Return values |
|||
------------- |
|||
|
|||
* ``duk_bool_t`` for true/false return values. |
|||
|
|||
* ``duk_idx_t`` for value stack indices. |
|||
|
|||
* ``duk_int_t`` for general return values which need to express both signed |
|||
and unsigned values. |
|||
|
|||
* ``duk_ret_t`` for Duktape/C function return values. |
|||
|
|||
Arguments |
|||
--------- |
|||
|
|||
* ``duk_idx_t`` for value stack indices. |
|||
|
|||
* ``duk_uarridx_t`` for Ecmascript array indices. |
|||
|
|||
* ``duk_size_t`` for byte lengths (strings, buffers, etc). |
|||
|
|||
* ``void *`` for generic void pointers. |
|||
|
|||
* ``const char *`` for string data pointers. |
|||
|
|||
* ``void *`` for buffer data pointers. |
|||
|
|||
Varargs API calls |
|||
----------------- |
|||
|
|||
Vararg API call variants are provided when it's awkward for an application to |
|||
use non-vararg API call variants. For example, it'd be awkward to construct a |
|||
formatted string first and then push it using ``duk_push_string()``, which is |
|||
why ``duk_push_sprintf()`` is provided. |
|||
|
|||
Flags vs. variants |
|||
================== |
|||
|
|||
A common issue is whether there should be an API call which provides a wide |
|||
variety of behavior controlled via flags, or a corresponding set of invididual |
|||
API calls. |
|||
|
|||
Individual API calls are generally favored in the API: |
|||
|
|||
* They make call sites easier in applications as there's no need to remember |
|||
and combine flag defines. |
|||
|
|||
* Individual API calls can be easily mapped to either individual internal |
|||
implementations, or to internal helpers which take flags, whichever is most |
|||
appropriate for the internal implementation. In contrast convering an API |
|||
call taking flags into individual internal calls is difficult: while it's |
|||
possible to check the flags in a macro call site, the macro will quite easily |
|||
end up evaluating flags multiple times which violates API macro guarantees. |
|||
|
|||
* They enable best performance because no flags field is built or decoded. |
|||
|
|||
* Even when improved calls are added to the API, individual calls are easy to |
|||
map to new providers, so versioning is easy even though some API clutter is |
|||
then easily left behind. |
|||
|
|||
There are situations where fewer calls with behavioral flags are preferable: |
|||
|
|||
* If the API call provides a lot of functionality, so that the set of |
|||
individual call variants would be very large, flags may be more appropriate. |
|||
|
|||
* If the API call behavior is likely to evolve new control flags over time, |
|||
flags may be more appropriate. |
|||
|
|||
For example, ``duk_def_prop()`` fits both of these criteria. The downside of |
|||
using flags is that: |
|||
|
|||
* It's not easy to decode the flags in an API macro and call invididual |
|||
internal implementations if that would otherwise be convenient. |
|||
|
|||
* Passing in flags has a small performance cost, and decoding the flags in |
|||
the call target has a relatively larger performance cost. |
Loading…
Reference in new issue