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__)

Ohrenfutter

Ich habe früher Podcasts ignoriert. Meine Einstellung dazu war etwa

Warum soll ich mir das Gequatsche von anderen Leuten anhören, schliesslich kann ich geschriebenen Text viel schneller parsen.

Irgendwann ist mir mal aufgegangen, dass ich in vielen Situationen nichts lesen kann oder will. Etwa beim Autofahren. Beim Einkaufen. Nach 10 Stunden im Büro. Beim Saubermachen – mein neues Hobby seit ich Kinder habe 😉 Mein ADS-versautes Gehirn verlangt nach einem kontinuierlichem Informationsbombardement.
Also Stöpsel ich mir einen Podcase in die Ohren. Und bin begeistert. Weil ich mich so drüber freue, dass ich Podcasts nun doch gut finde, gebe ich hier gerne meine derzeit liebsten Abonnements zum besten:

  • Chaosradio Express. Ok, wer Podcasts hört, hört sehr wahrscheinlich diesen Podcast. Wer keine Ahnung hat, was Podcasts sind: hört Euch eine beliebige Folge zum Thema Eurer Wahl an. Es ist für alle etwas dabei. Highlight für mich war Folge 147: Mikrokosmos, Makrokosmos.
  • Mobile Macs. Der chaotische Podcast für Apple-Fanboys. Ich habe zwar nur einen recht immobilen 25kg-Mac, verpasse aber trotzdem keine Folge. Gegenüber Bits und So erheblich mehr Nerd- aber auch mehr Quatschfaktor. Das Leiden lohnt sich aber für die 5% Informationsgehalt. Unglücklicherweise sind die interessanten Infos immer an einer anderen Stelle versteckt, so dass man gezwungen ist, sich den ganzen Podcast anzuhören.
  • Bits und So. Der „vernünftige“ Podcast für Apple Fanboys. Eigentlich ziemlich langweilig, aber es gibt mitunter gute Tipps für hilfreiche Applikationen. Seit Leo Becker vom fscklog mit dabei ist, gesteigerter Informationsgehalt. Höre ich trotzdem nur, wenn gerade alle anderen Podcasts alle sind.
  • Küchenradio. Ganz toll, ganz anders. Sehr viele gesellschaftliche Themen, je nach Besetzung stark schwankende Qualität. Zum Reinhören empfehle ich: NR. 268: Gentrifizierung.
  • Alternativlos. Die Fnord News Show für die Hosentasche. Hoher Unterhaltungs- und Geekfaktor. Kann man nicht beschreiben, einfach mal reinhören. Anspieltipp: die Folge zur Polizeigewalt.
  • Netzpolitik Podcast. Höre ich nur, wenn mich das Thema interessiert. Hauptsächlich macht die entspannte Art von Markus Beckedahl den Charme des Podcasts aus.
  • Elementarfragen. Leider im Moment inaktiver Podcast, der hautpsächlich für seine großartige Sendung zu Tschernobyl extrem viel positives Feedback erhalten hat. Hört Euch wenigstens diese Folge an. Es lohnt sich. Ich drücke die Daumen, dass die Sommerpause bei den Elementarfragen bald vorbei ist.
  • Pandroid Podcast. Ich war vor einer Weile mal auf der Suche nach einem vernünftigen Podcast zu Android. Aber das was man üblicherweise findet, sind Gadget-lastige Labersendungen („Oh wow, das neue Samsung Bla bla bla mit 12 Megabupsel Kamera…muss ich haben…“). Speziell die US-amerikanische Podcastlandschaft ist in der Beziehung unerträglich.

    Um so erfreuter war ich gestern, als ich gelesen habe, dass Yan Minagawa und Dirk Jäckel mit dem Pandroid Podcast gestartet sind und sich explizit mit der Softwareentwicklung für Android beschäftigen wollen. Sendung 01 ist nur 15 Minuten lang, aber schon sehr vielversprechend. Was dem Podcast noch fehlt, ist lediglich ein flattr-Button.

Ich bin immer noch auf der Suche nach guten Podcasts zur Softwareentwicklung. Es gibt zwar den heise developer Podcast, aber so richtig umhauen tut mich das nicht. Speziell Systemnahe Entwicklung unter Unix scheint ein unbesetztes Nischenthema zu sein. Über Empfehlungen freue ich mich in den Kommentaren.

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.