Duktape API uses typedef-wrapped C types almost exclusively to ensure portability to exotic platforms. This section provides some background, summarizes the types, and describes how calling code should use types to maximize portability.
Portable C/C++ typing is a complex issue, involving:
INT_MIN
printf()
and
scanf()
format stringsDuktape only works on platforms with two's complement arithmetic.
duk_idx_t
and duk_ret_t
(described below) when declaring variables for maximum portability. Alternatively
you may use plain types (like long
) but your code will be less portable
and you may need to use casts to avoid warnings. Note that long
is a
better default integer type than int
, because int
may be
only 16 bits wide on some platforms.duk_int_t
have been chosen to be the most
convenient (or fastest) types for the compiler. If you're using them in
structs whose footprint matters, you may want to use other C types; remember
to check / cast accordingly.printf()
formatting cast Duktape types to a wide integer
type and use a standard format specific to ensure that the type and the
specifier always match. For integers, long
and unsigned long
are usually a good choice because they don't require C99/C++11 and can usually
hold all integer values used by Duktape typedefs. For example:
printf("Result: %ld\n", (long) duk_get_int(ctx, -3));
vsnprintf()
function. To maximize portability, select format specifiers carefully and
cast arguments to ensure types match. For example:
duk_int_t val = 123; duk_push_sprintf(ctx, "My integer: %ld", (long) val);
long
: %ld
unsigned long
: %lu
double
: %f
or %lf
for printf()
,
%lf
for scanf()
size_t
: %zu
(C99; pre-C99 compilers have various custom specifiers)intmax_t
: %jd
(C99)uintmax_t
: %ju
(C99)printf()
and scanf()
may be different. For scanf()
, use a standard type and a
standard format code (so that you can be certain they match), then cast
to a Duktape type as necessary. Again, long
and unsigned long
are a good default choice. For example:
long val; sscanf(my_str, "%ld", &val); duk_push_int(ctx, (duk_int_t) val);
L
(or UL
) suffix for constants which
are larger than 16 bits to maximize portability. Like the int
type, integer constants without a suffix are only guaranteed to be 16 bits
wide. With the L
suffix constants are guaranteed to be at least
32 bits wide. Example:
duk_push_int(ctx, 1234567L);
fopen()
. There is no way to specify an encoding
or support a wide character set. To do that, you need to implement a
platform specific helper yourself.For the most part you don't need to worry about these type wrappers: they're intended for exotic environments where some common assumptions about type bit counts and such don't hold.
The API documentation uses the Duktape wrapped typedef names (such as
duk_idx_t
). The concrete type used by the compiler depends
on your platform and compiler. When hovering over a prototype in the API
documentation the tool tip will show what concrete types are used when
C99/C++11 types are available and the platform int
is at least
32 bits wide (which is nowadays almost always the case).
The following table summarizes a few central typedefs and what the
concrete type selected will be in various (example) environments. The
table also suggests what plain type you should use for printf()
and scanf()
casts for portable formatting/scanning.
Duktape type | C99/C++11 32-bit int | Legacy 32-bit int | Legacy 16-bit int | printf |
scanf |
Notes |
---|---|---|---|---|---|---|
duk_int_t | int | int | long | %ld long |
%ld long |
All around integer type, range is [DUK_INT_MIN , DUK_INT_MAX ] |
duk_uint_t | unsigned int | unsigned int | unsigned long | %lu unsigned long |
%lu unsigned long |
All around unsigned integer type, range is [0, DUK_UINT_MAX ] |
duk_int32_t | int32_t | int | long | %ld long |
%ld long |
Exact type for ToInt32() coercion |
duk_uint32_t | uint32_t | unsigned int | unsigned long | %lu unsigned long |
%lu unsigned long |
Exact type for ToUint32() coercion |
duk_uint16_t | uint16_t | unsigned short | unsigned short | %u unsigned int |
%u unsigned int |
Exact type for ToUint16() coercion |
duk_idx_t | int | int | long | %ld long |
%ld long |
Value stack index |
duk_uarridx_t | unsigned int | unsigned int | unsigned long | %lu unsigned long |
%lu unsigned long |
Ecmascript array index |
duk_codepoint_t | int | int | long | %ld long |
%ld long |
Unicode codepoints |
duk_errcode_t | int | int | long | %ld long |
%ld long |
Integer error codes used in the Duktape API (range for user codes is [1,16777215]) |
duk_bool_t | int | int | int | %d int |
%d int |
Boolean return values |
duk_ret_t | int | int | int | %d int |
%d int |
Return value from Duktape/C function |
duk_size_t | size_t | size_t | size_t | %lu unsigned long |
%lu unsigned long |
1:1 mapping now, wrapped for future use. Range is [0, DUK_SIZE_MAX ].
C99 format specifier is %zu . |
duk_double_t | double | double | double | %f or %lf double |
%lf double |
1:1 mapping now, wrapped for future use, e.g. custom software floating point library. |
This section provides some background and rationale for the C typing.
Bit sizes of common types like int
vary across implementations.
C99/C++11 provide standard integer typedefs like int32_t
(exact
signed 32-bit type) and int_fast32_t
(fast integer type which
has at least signed 32-bit range). These typedefs are not available in
older compilers, so platform dependent type detection is necessary.
Duktape needs an integer type which is convenient for the architecture but still guaranteed to be 32 bits wide. Such a type is needed to represent array indices, Unicode points, etc. However, there is no such standard type and at least the following variations are seen:
int
and a 32-bit long
int
and a 64-bit long
, with the 64-bit
long
being inefficient for the processorint
and long
As can be seen, no built-in C type would be appropriate, so type detection
is needed. Duktape detects and defines duk_int_t
type for these
purposes (at least 32 bits wide, convenient to the CPU). Normally it is mapped
to int
if Duktape can reliably detect that int
is 32
bits or wider. When this is not the case, int_fast32_t
is used
if C99 types are available; if C99 is not available, Duktape uses platform
specific detection to arrive at an appropriate type. The duk_uint_t
is the same but unsigned. Most other types in the API (such as duk_idx_t
)
are mapped to duk_(u)int_t
but this may change in the future
if necessary.
Other special types are also needed. For instance, exactly N bits wide integers are also needed to ensure proper overflow behavior in some cases.
C/C++ types are often used with printf()
and scanf()
,
with each type having a format specifier. The set of format specifiers is only
partially standardized (e.g. %d
is used for an int
,
regardless of its bit size), but custom codes are sometimes used.
When using type wrappers, the correct format code depends on type detection.
For instance, duk_int_t
is mapped to a convenient integer type which is
at least 32 bits wide. On one platform the underlying type might be int
(format specifier %d
) and on another it might be long
(format specifier %ld
). Calling code cannot safely use such a value
in string formatting without either getting the proper format specified from a
preprocessor define or using a fixed format specifier and casting the argument:
duk_int_t val = /* ... */; /* Cast value to ensure type and format match. Selecting the appropriate * cast target is problematic, and caller must "play it safe". Without * relying on C99 types, "long" is usually good for signed integers. */ printf("value is: %ld\n", (long) val); /* When assuming C99 types (which limits portability), the maxint_t is * guaranteed to represent all signed integers and has a standard format * specifiers "%jd". For unsigned values, umaxint_t and "%ju". */ printf("value is: %jd\n", (maxint_t) val); /* Use a preprocessor define to provide the format code. Code and format * specifier are chosen to match during type detection. */ printf(DUK_PRIdINT, val);
C99 takes this approach and provides preprocessor defines for C99 types in
inttypes.h
. For instance, the printf()
decimal
format specifier for int_fast32_t
is PRIdFAST32
:
int_fast32_t val = /* ... */; printf("value is: " PRIdFAST32 "\n", val);
The printf()
and scanf()
format specifiers may
be different. One reason is that float
arguments are automatically
promoted to double
in printf()
but they are handled
as distinct types by scanf()
. See
why-does-scanf-need-lf-for-doubles-when-printf-is-okay-with-just-f.
The correct format specifier for a double
in
printf()
is %f
(float values are automatically
promoted to doubles) but %lf
is also accepted. The latter is
used in Duktape examples for clarity. See
correct-format-specifier-for-double-in-printf.