Skip to content

Functions with variable arguments, countof()

WCPL fully supports <stdarg.h> header defining a type and three macros for advancing through an array of values supplied as the value of the ... (ellipsis) argument. Internally, this argument comes last in the argument list and has the va_list type, defined internally as follows:

typedef union va_arg { 
  int va_int; 
  unsigned int va_uint;
  long va_long; 
  unsigned long va_ulong;
  long long va_llong; 
  unsigned long long va_ullong;
  size_t va_size; 
  double va_double;
  v128_t va_v128;
  void *va_voidptr;
} va_arg_t, *va_list;

WCPL has no requirement of the ellipsis argument to be preceded by ‘normal’ arguments; if needed, ... can be the only argument in the argument list. The ellipsis argument can be referenced in the body of the function as a pointer expression either via WCPL-specific intrinsic va_etc() or via its equivalent pseudo-variable .... The following is a legal WCPL code (which, of course, can return garbage if no arguments are supplied to foo by the caller):

int foo(...) /* WCPL */
{ 
  return ...[0].va_int; 
}

It is compiled to the following WASM code:

(func $_:foo (export "foo")
  (param $ap$ i32) (result i32)
  local.get $ap$
  i32.load offset=0 align=4
  return
)

The va_arg form is an intrinsic (cannot be a macro because WCPL macros can’t have types as parameters):

va_arg(ap, type) is equivalent to *(type*)ap++

The rest of the standard macros for variable argument access are defined as follows:

#define va_start(ap, v) ((ap) = ...)
#define va_end(ap) ((void)(ap = NULL))
#define va_copy(dst, src) ((dst) = (src))

Also, WCPL supports a countof intrinsic, which serves three purposes. It can act as a shortcut for counting the number of elements in an array, so if a is an array expression:

countof(a) is equivalent to sizeof(a)/sizeof(a[0])

Its second purpose is to access the number of the arguments supplied to vararg function: countof(...) returns the corresponding count, which is always stored by the caller in va_arg_t-typed slot preceding the first actual argument in the ... argument array:

countof(...) is equivalent to ...[-1].va_size

This feature makes it possible to write code like this:

int sumints(...) /* WCPL */
{ 
  size_t n = countof(...); 
  int sum = 0;
  for (size_t i = 0; i < n; ++i) {
    sum += ...[i].va_int;
  }
  return sum; 
}

WCPL’s <stdarg.h> header defines an additional macro for this scenario:

#define va_count() (countof(...)) /* WCPL */

The third purpose of countof() is related to variadic and generic macros; it will be described in the next few posts.