1.5 Callable

The KC3 Callable type is an or-type of Cfn and Fn.

In any case where you accept a Fn or Cfn as an argument you should use Callable as the type.

1.5.1 C functions : cfn

The Cfn type and cfn syntax allows you to define C function wrappers that are compatible with KC3 usual function calls.

To define a C function, use for example :

defmodule List
  def map = cfn Tag "plist_map" (List, Callable, Result)
end

The Cfn will be named according to its first binding Ident and its type also matches Callable.

1.5.2 C macro functions : cfn_macro

The cfn_macro syntax introduces a Cfn (and thus also a Callable) which behaves as a macro function.

The important rule to remember about macro functions is :

cfn_macro passes every argument to C unevaluated.

Therefore List.do(list_expression, pattern, do_block) must expose:

s_tag * plist_do (s_tag *list,
                  s_tag *pattern,
                  s_tag *do_block,
                  s_tag *dest);

with for example :

def do = cfn_macro Tag "plist_do" (Tag, Tag, Tag, Result)

1.5.3 KC3 types and C types

The types in a cfn declaration describe values exchanged through the C ABI. Scalar values are passed by value. Other values are passed as pointers to their libkc3 representation.

| KC3 type | C type | | --- | --- | | Void | void | | Bool | bool (uint8_t in the FFI ABI) | | Character | u32 | | S8 | s8 | | S16 | s16 | | S32 | s32 | | S64 | s64 | | Sw | sw | | U8 | u8 | | U16 | u16 | | U32 | u32 | | U64 | u64 | | Uw | uw | | F32 | f32 | | F64 | f64 | | F80 | f80 when enabled | | F128 | f128 when enabled | | Array | s_array * | | Block | s_do_block * | | Call | p_call * | | Callable | p_callable * | | Cfn | p_callable * | | Fn | p_callable * | | Fact | s_fact * | | Ident | s_ident * | | Integer | s_integer * | | List | p_list * | | Map | s_map * | | Pointer | s_pointer * | | Ptr | u_ptr_w * | | PtrFree | u_ptr_w * | | Ratio | s_ratio * | | Str | s_str * | | Struct | s_struct * | | StructType | s_struct_type * | | Sym | p_sym * | | Tag | s_tag * | | Time | s_time * | | Tuple | p_tuple * | | Var | p_var * |

Types ending in * in a KC3 declaration are typed C pointers. For example, TLS* is passed as the underlying struct tls * pointer.

Result is not supplied by the KC3 caller. It asks KC3 to append a pointer to the destination value. For example, cfn Str "str_init" (Result, Str) receives an s_str * destination and an s_str * input. The C function normally returns the destination pointer as well.

When a Result argument is present, the returned C pointer is checked after the call for mismatch :

  • a non-null pointer different from the Result destination is an ABI error; KC3 reports the mismatch and the C function call fails;
  • a null C return is treated as a void KC3 result, so the call returns void and the destination is not used as the result;
  • returning exactly the Result destination commits the value written there and returns it to the KC3 caller.

C functions using Result should therefore return the destination pointer on success and NULL on an ordinary failure. They must not return a different allocated pointer or that will be reported as an error by the Cfn call evaluator.

The idea behind this is that if you put your Result first in a C function and it matches a register size, then your first argument will probably be rax or similar and smaller. If your result also is the same register it matches most ISA as they return their register sized result into the first one thus eliminating several machine opcodes per function call. In a hot path that's why we use C or a finely tunable or smart compiled language : it matters.


Top : KC3 documentation

Previous : 1.04 Bool

Next : 1.06 Call