I replaced following code
(*dataset_p)[*term_count_p - 9] = ERL_DRV_ATOM;
(*dataset_p)[*term_count_p - 8] = drv->atom_error;
(*dataset_p)[*term_count_p - 7] = ERL_DRV_INT;
(*dataset_p)[*term_count_p - 6] = error_code;
(*dataset_p)[*term_count_p - 5] = ERL_DRV_STRING;
(*dataset_p)[*term_count_p - 4] = (ErlDrvTermData) error;
(*dataset_p)[*term_count_p - 3] = strlen(error);
(*dataset_p)[*term_count_p - 2] = ERL_DRV_TUPLE;
(*dataset_p)[*term_count_p - 1] = 3;
(where dataset_p
has type ErlDrvTermData**
and term_count_p
is an int*
) with
append_to_dataset(9, *dataset_p, *term_count_p,
ERL_DRV_ATOM, drv->atom_error,
ERL_DRV_INT, error_code,
ERL_DRV_STRING, (ErlDrvTermData) error, strlen(error),
ERL_DRV_TUPLE, 3);
where append_to_dataset
is defined as
void append_to_dataset(int n, ErlDrvTermData* dataset, int term_count, ...) {
int i;
va_list new_terms;
va_start(new_terms, term_count);
for (i = -n; i < 0; i++) {
dataset[term_count + i] = va_arg(new_terms, ErlDrvTermData);
}
va_end(new_terms);
}
(diff to see that's the entire difference). It looks to me like behavior should be exactly identical, but while tests pass on the original code, the new version fails with an error message:
HUGE size (47278999994405)
make: *** [test] Aborted
What am I getting wrong?
Depending on the types of the expressions on the right hand side of the assignments in the first code snippet, the two code snippets may not be equivalent.
Variable arguments undergo default argument promotions but no other conversion (as opposed to calling a prototyped function, where arguments are converted if necessary if they’re assignment-compatible). For the second code to work they must be of type ErlDrvTermData
which in turn must the same as its default-promoted type to fetch the corresponding arguments correctly via va_arg
.
In particular, every argument but the first two (which aren’t variable arguments) need an explicit cast to be converted correctly:
append_to_dataset(9, *dataset_p, *term_count_p,
(ErlDrvTermData)ERL_DRV_ATOM, (ErlDrvTermData)drv->atom_error,
(ErlDrvTermData)ERL_DRV_INT, (ErlDrvTermData)error_code,
(ErlDrvTermData)ERL_DRV_STRING,
(ErlDrvTermData) error, (ErlDrvTermData)strlen(error),
(ErlDrvTermData)ERL_DRV_TUPLE, 3);
assuming the conversions are value-preserving. If ErlDrvTermData
is, for example, typedef
ed to a short
, it must be fetched as an int
:
dataset[term_count + i] = va_arg(new_terms, int);