0

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?

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • You are possibly using negative indices if *term_count is less than 9. Or it could be that your for-loop is trying to index a pointer which is not an array, i.e. trying to index an individual value like `"ERL_DRV_ATOM"` – smac89 Oct 10 '14 at 15:49
  • What is `ErlDrvTermData`? If it's wider than `int`, the `3` must be cast when passed to `append_to_dataset`. – mafso Oct 10 '14 at 15:55
  • Good point, @mafso, but it applies equally to both codes, so it doesn't explain why they behave differently. – John Bollinger Oct 10 '14 at 16:03
  • @JohnBollinger: In the first code, there's an explicit conversion, in the second code, we have default argument promotions due to the vararg function. For example, with `typedef long ErlDrvTermData` a `3` (of type `int`) is passed to `append_to_dataset` and fetched as a `long`. The same may apply to all the arguments after the first two, actually. – mafso Oct 10 '14 at 16:08
  • I meant _implicit_ conversion, of course. Sorry, too late now... – mafso Oct 10 '14 at 16:14
  • 1
    @mafso, my bad. In that case, I observe that the varargs function is called with one argument of type `size_t` and another of type `int`. If the sizes of those types are not the same then it cannot be that they both match the size of `ErlDrvTermData`. – John Bollinger Oct 10 '14 at 16:16
  • @mafso Thanks, this seems like a possible answer (`ErlDrvTermData` is `unsigned __int64` under Windows and `unsigned long` under Linux). I'll test it when I get an opportunity. – Alexey Romanov Oct 10 '14 at 18:39
  • @mafso Yes, this was the problem. Please write an answer so that I can accept it. – Alexey Romanov Oct 10 '14 at 19:15

1 Answers1

1

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, typedefed to a short, it must be fetched as an int:

dataset[term_count + i] = va_arg(new_terms, int);
mafso
  • 5,433
  • 2
  • 19
  • 40