2

I have a variadic function that requires a double-NULL terminator.

Contrived, Simplified Example

void makeLine(char *name, ...) {
    ... accepts (x,y) coordinates until (0,0) is found,
    ... assume that any coordinate except (0,0) is legal
            [i.e. (0,1) or (1,0) should not trigger a stop condition]
}

And I'd like to annotate it with __attribute ((sentinel))__ from gcc. But it seems that with the sentinel attribute, only one of the arguments can be required NULL as opposed to the desired sentinel(0,1) [last two required to be NULL].

Is there a way to ask gcc to enforce a double-NULL, or...

Given that the NULL can only be enforced for one of the two parameters, would you decorate this function with sentinel(0) or sentinel(1) and why? Which of the two positions would be more likely to catch bugs?

Pat
  • 1,882
  • 2
  • 15
  • 22
  • Please clarify how contrived and simplified your example is! `__attribute__((sentinel))` isn't appropriate at all for checking for an integer 0 - which is not necessarily the same thing as a null pointer in a context where the compiler does not know the type (as is the case with a variadic function). As the documentation you linked to says: "This function attribute ensures that a parameter in a function call is an explicit `NULL`. [...] *A valid `NULL` in this context is defined as zero with any pointer type.* [...]". – Matthew Slattery Aug 20 '12 at 01:10
  • Too contrived apparently, they should be pointers, not integers. The intent was to clarify that NULL would not be interpreted as a legal coordinate (as a pointer of value 0 and integer of value 0 to a naiive variadic function would be indistinguishable). I was trying to distill the question down - didn't mean to introduce confusion in the process. – Pat Aug 20 '12 at 01:17
  • Read again assumption `any legal x or y cannot be 0` and think do you really need TWO nulls ? – Agnius Vasiliauskas Aug 20 '12 at 06:24
  • Flaw of the contrived example, edited to fix – Pat Aug 20 '12 at 07:17

1 Answers1

1

Probably the only way to achieve compilation error in case of any of TWO sentinels is missing - is to use C99 variadic macros:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#pragma GCC diagnostic error "-Wformat"

void checkSentinel0(char * name, ...) __attribute__ ((sentinel(0)));
void checkSentinel1(char * name, ...) __attribute__ ((sentinel(1)));

void checkSentinel0(char * name, ...) {
    (void)name;
}
void checkSentinel1(char * name, ...) {
    (void)name;
}

#define MY_VARIADIC(name, ...) do {checkSentinel0(name, __VA_ARGS__);\
                                   checkSentinel1(name, __VA_ARGS__);\
                                       myVariadic(name, __VA_ARGS__);}\
                               while(0);

void myVariadic(char * name, ...) {
    // your code messing with coordinates and etc.
    (void)name;
}

int main () {

 MY_VARIADIC("test", 1,2,3,4, NULL, NULL); // Ok

 MY_VARIADIC("test", 1,2,3,4, 14,     15); // not compilable
 MY_VARIADIC("test", 1,2,3,4, NULL,   15); // not compilable
 MY_VARIADIC("test", 1,2,3,4, 15,   NULL); // not compilable

 return 0;
}

So if user is only exposed to your macro MY_VARIADIC then he/she will get error in case forgetting to terminate argument list with two null's.

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70