Portable __VA_ARGS__ macros for Linux, HP-UX, Solaris and AIX

I recently needed a macro with variable arguments that work on all mainstream compilers. I use gcc only on Linux, on the other platforms I want to use the „native“ compiler that belongs to the OS. Let’s start with Linux.

Linux

If you’re inside the gnu/gcc world, things are easy. This is what everybody tries first:

1
2
3
4
5
6
7
8
9
10
11
#include "stdio.h"
/* I use "stdio.h" because my blog software cant't handle
   angle brackets inside a preformated text blog ... :( */
 
#define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  f "\n" , __VA_ARGS__);
 
int main(void)
{
    PRINTERROR("Hello %s!", "world");
    return 0;
}

The problems start, if you want to use the macro without variable arguments.

9
    PRINTERROR("Hello world!");

gcc will not accept the code, because it resolves the macro to

9
    fprintf(stderr, "ERRORMESSAGE = "  "Hello world!" "\n" ,);

(note the comma)

test.c: In function ‘main’:
test.c:9: error: expected expression before ‘)’ token

The gcc workaround for this kind of a situation is:

5
#define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  f "\n" , ## s);

If „s“ is null, this erases the comma to the left of the „##“ in the macro definition. The resulting expansion is:

9
    fprintf(stderr, "ERRORMESSAGE = "  "Hello world!" "\n" );

The comma is gone, the code compiles. Great.

HP-UX

However, this approach fails on HP-UX

cc -o test test.c
cc: "test.c", line 9: error 1000: Unexpected symbol: ")".

I found the solution for this problem in the HP-docs:

you must insert a space to the left of the comma to make it clear to the preprocessor that the comma is the left operand of the „##“ operator.

Yes. A space. Left of the comma. Sigh….

The definition that works on Linux and HP’s cc -AC99 compiler looks like this:

5
#define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , ## s);

Solaris

Solaris c89 compiles the above definition with a warning:

"test.c", line 9: warning: argument mismatch

Solaris c99 compiler is very strict and allows

5
#define FOO(...) printf(__VA_ARGS__)

only. The documentation points out, that it is possible to use #__VA_ARGS__ which seems to be similar to GNU’s/HP’s ## Operator:

5
#define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );

Ok, the code compiles, but calling PRINTERROR results in a „argument mismatch“-warning. I found no other solution than disabling the warning. Dirty but it works:

5
6
7
#define PRINTERROR(x, ...) printf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );
/* optional: disables "warning: argument mismatch" */
#pragma error_messages (off, E_ARGUEMENT_MISMATCH)

If course it is bad to hide warnings, but if your code is multi-platform, you can use very strict compiler settings on another platform to make the warnings visible at least there.

AIX

The original AIX-compiler (cc) does not complain, but c89 fails with

"test.c", line 3.16: 1506-1128 (S) The GNU variable argument identifier "s" of macro "FOO" is not permitted in the current langlvl mode.
"test.c", line 7.29: 1506-041 (E) The invocation of macro FOO contains fewer arguments than are required by the macro definition.

There is a langlvl #pragma to switch the compiler language level. All levels that allow implementation-specific language extensions are sufficient. I used extc99 which allows to use the c99 compiler. Simply add this to the beginning of your sourcefile:

#pragma langlvl (extc99)

The final result

This is the result of some hours browsing the compiler docs. The macro works on all c99-compilers mentioned above and allows zero arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifdef _AIX
#pragma langlvl (extc99)
#endif
 
#include "stdio.h"
 
#ifdef _SOLARIS
  #define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );
  /* optional: disables "warning: argument mismatch" */
  #pragma error_messages (off, E_ARGUEMENT_MISMATCH)
#else
  #define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , ## s);
#endif
 
int main(void)
{
        PRINTERROR("NO PARAM");
        PRINTERROR("Param: %s", "foo");
 
        return 0;
}

Of course you have to use the compiler switch -D_AIX, -D_SOLARIS or -D_HPUX on the apropriate platform. Let me know what do you think and how it could be done better.

Update: André suggests this solution (see his comment):

void PRINTERROR_verbose(const char * file, int line, int level, char *format, ...) {
	int size;
	char *cp;
    va_list args;
 
	if (level < = 2) {
 
	    va_start(args, format);
    	size = vsnprintf(NULL, 0, format, args) + 1;
    	va_end(args);
 
    	va_start(args, format);
		cp = (char *)calloc(size, sizeof(char));
		if (cp != NULL && vsnprintf(cp, size, format, args) > 0) {
			/* Ausgabe von cp hier via syslog, stderr, stdout, etc. */
			/* oder cp noch mal umformatieren fuer Sonderzeichen und/oder */
			/* zusaetzliche Zeitangabe */
 
			/* bei Debuglevel 1 (z.B. ERROR) wird dann noch etwas mehr angezeigt */
			if (level < = 1) {
				fprintf(stderr, "\tFile '%s' in line %i\n", file, line);
			}
		}
	    va_end(args);
		free(cp);
	}
}
 
#define PRINTERROR(...) PRINTERROR_verbose(__FILE__, __LINE__, __VA_ARGS__)

gdb attach fails with ptrace: Operation not permitted

Today I ran into a weird problem. I could not attach to my own process with gdb. The process ran under my UID, but gdb refused to attach. This is a problem of wrong permissions, although /proc/[pid]/status looked ok:

...
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
...

I am the owner but cannot attach? Well, I launched gdb as root and could attach. Strange. Without digging deeper into this, my dirty workaround was this:

sudo chmod +s /usr/bin/gdb

Update: Thanks to Mario, who pointed out, that the reason is the Kernel hardening stuff build into the Ubuntu kernel. See his comment how to fix the problem permanently.