C types

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:

Duktape only works on platforms with two's complement arithmetic.

Guidelines for code using the Duktape API

Wrapped types used in the Duktape API

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.

Background on C/C++ typing issues

This section provides some background and rationale for the C typing.

Bit sizes are not standard (and there's no guaranteed fast 32-bit type)

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:

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.

Format specifiers

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.