xdrgen Documentation

Purpose of xdrgen

Defining an IPC message requires a format string which corresponds to the data structure of the message. Typically, the designer of the message has defined this format string by hand, as a macro in the same header file which defines the data structure. The xdrgen parser automates this process. It parses an XDR data structure specification (similar to a list of C type definitions), and generates a C header which includes both type definitions and macros defining the IPC format strings.

Automating this process helps to avoid inconsistencies between the C data structure and the IPC format string. In our experience, inconsistencies are often introduced when the data structure is changed, but the person modifying the code is not aware that the format string must also be changed. These inconsistencies can lead to garbled binary messages, which are sometimes very difficult to track down.

Running xdrgen for the first time

Installing IPC will place an xdrgen binary in the same location as the central server. To see an example run of xdrgen, run the following command in the src/XDRGen directory of the IPC distribution:

xdrgen example.xdr example.xdr.h

This will output the C header file example.xdr.h, based on the XDR specification in example.xdr. You can compare the two to get a quick idea of the relationship between XDR and C.

xdrgen command-line options

usage: xdrgen OPTIONS <xdrFile> [outputHeaderFile]
 -h or --help        Print this help
 --lang=[c,c++]      Change language for header output
                     (default: c++)
 --directives=[portable,gcc,none]
                     Control syntax of #line directives
                     that help to trace location of errors
                     (default: portable)
 -Dname=value, -Idirectory
                     These options are passed on to the C
                     pre-processor before xdrgen parses the
                     input file (see cpp(1) or 'info cpp')
When xdrgen is run, it will parse xdrFile and output the resulting C header to outputHeaderFile (or to stdout if outputHeaderFile isn't specified). Specifying C++ for the header output language (the default) will cause the header to use some C++ language features which are not supported by C. The differences will be discussed more later.

Basic xdrgen type declarations

We now move onto the kinds of declarations which xdrgen can parse. There are two basic kinds of declarations. The first is a typedef. The declaration

typedef int foo;

generates a typedef and a macro in the output header file:

typedef int foo;
#define foo_IPC_FORMAT "int"

The second kind of declaration is a struct. The declaration

struct Zoo {
  int foo;
  int goo;
};
generates a struct and a macro in the output header file:

typedef struct _Zoo {
  int foo;
  int goo;
} Zoo;
#define Zoo_IPC_FORMAT "{int,int}"
If the header language is C++, the generated code is slightly different:

struct Zoo {
  int foo;
  int goo;
#define Zoo_IPC_FORMAT "{int,int}"
  static const char *getIPCFormat(void) {
    return Zoo_IPC_FORMAT;
  }
};

If you are using a C++ compiler, the C++ output has the following advantages:

You may nest struct declarations to arbitrary depths, and use previously defined types in declarations of new types, as in the following:

struct MyIncludableStruct {
  int foo;
  struct { int a; } goo;
};

struct MyNestedStruct {
  MyIncludableStruct b;
  struct {
    char a;
    MyIncludableStruct b2;
  } roo;
};

You may not declare multiple fields of the same type in one line, so the C construction int a, b; must be replaced with int a; int b;.

Primitive types

The following struct definition has fields with all of the supported primitive types:

struct PrimitiveTypes {
  string a<>;
  unsigned char b;
  char c;
  unsigned int d;
  int e;
  bool f;
  float g;
  double h;
};
Some notes about how these types are used:

Fixed-length arrays

Fixed length arrays in the XDR file are mapped directly to fixed-length arrays in C. For the XDR declarations

typedef unsigned char ImagePixel[3];

struct Transform {
  double mat[4][4];
};

we get the following header output (abbreviated for clarity):

typedef unsigned char ImagePixel[3];
#define ImagePixel_IPC_FORMAT "[uchar:3]"

struct Transform {
  double mat[4][4];
};
#define Transform_IPC_FORMAT "{[double:4,4]}"

Variable-length arrays

Variable-length arrays are specified in XDR using angle brackets <>. For the XDR declaration

struct Image {
  int rows;
  int cols;
  unsigned char data<><>;
};

we get the following header output (abbreviated for clarity):

struct Image {
  int rows;
  int cols;
  unsigned char *data;
};
#define Image_IPC_FORMAT "{int,int,<uchar:1,2>}"

Variable-length array fields in IPC must be inside a struct, and the length in each dimension of the array must correspond to an int or unsigned int field of the struct. Which fields of the struct are used for each dimension is controlled by the format string. xdrgen has the following stricter requirements:

xdrgen is designed this way to make it clear where the size of each dimension of the array is coming from, and remove the need for extra language features to specify how int fields correspond to array dimensions. This simplicity comes at the cost of discarding some of IPC's flexibility.

Fixed and variable-length array dimensions cannot be mixed, so the following declaration is illegal:

struct Alpha {
  int size;
  char beta[5]<>;
}

However, a similar effect could be achieved with the following declaration:

struct Alpha {
  int size;
  struct { char data[5]; } beta<>;
};

xdrgen allows a maximum possible length to be declared for a variable-length array, as in the following definition:

struct Gamma {
  int delta;
  float epsilon<20>;
};

However, there is no notion of a maximum length for a variable-length array in IPC, so the length doesn't currently appear in the header output.

Enumerated types

Enumerated types in XDR are mapped directly to enumerated types in C. From the declaration

enum Color {
  RED, ORANGE, YELLOW
};

we get the following header output:

enum Color {
  RED,
  ORANGE,
  YELLOW
};
#define Color_IPC_FORMAT "{enum RED,ORANGE,YELLOW}"

Values for the named options of an enumerated type can also be specified, but IPC is only flexible enough to handle a consecutive set of options, so if any values are specified, xdrgen represents the field as "int" to IPC (with the disadvantage that IPC can't expand values to option names during data logging). For the declaration

enum Mixed {
  TWO = 2, FOUR, SIX = 6
};

we get the following header output:

enum Mixed {
  TWO = 2,
  FOUR,
  SIX = 6
};
#define Mixed_IPC_FORMAT "int"

More about the string type

Strings are a special case. A string is a null-terminated array of char. IPC doesn't need to have an integer dimension for the size of the array because it can detect the size from the null termination. Therefore the last variable-length dimension of a string. Also, fixed- and variable-length arrays of strings are allowed, as in the following definition:

struct ExecCall {
  struct {
    int argc;
    string argv<>;
  } args;
  string envVars[20]<>;
};

which produces the header output (abbreviated for clarity):

struct ExecCall {
  struct {
    int argc;
    char **argv;
  } args;
  char *envVars[20];
};
#define ExecCall_IPC_FORMAT "{{int,<string:1>},[string:20]}"

Arbitrary code sections

Your XDR file can include arbitrary code sections wrapped in the delimiters %{ and %}. Arbitrary code can be placed at the beginning or end of the file, between declarations, or at the end of a struct declaration, just before the closing } character. The arbitrary code will be copied into the generated header at the corresponding point in the C code.

The XDR file text

%{
#include "my_arbitrary_code.h"
#define N 3
extern int foo;
%}

typedef double Meters;

%{
extern Meters length;
%}

struct Roo {
  int a;
  char b;
%{
  Roo(int _a, char _b) { a = _a; b = _b; }
%}
};

generates the header output (abbreviated for clarity):

#include "my_arbitrary_code.h"
#define N 3
extern int foo;

typedef double Meters;
#define Meters_IPC_FORMAT "double"

extern Meters length;

struct Roo {
  int a;
  char b;
  Roo(int _a, char _b) { a = _a; b = _b; }
};
#define Roo_IPC_FORMAT "{int,char}"

External format definitions

You can use external format definitions if the structs you define using XDR contain other datatypes which are not defined using XDR. This could happen if you want to manually define the IPC format for a datatype using an IPC feature not supported by xdrgen. The declaration

ipc_type ExternalStruct1;

tells xdrgen to expect that the type ExternalStruct1 has an IPC format defined in the macro ExternalStruct1_IPC_FORMAT. The macro definition must appear before the generated code for any struct which includes ExternalStruct1, which means that it should either appear in an arbitrary code section of the XDR file, or it should be included via a #include directive in an arbitrary code section.

Once ExternalStruct1 is declared using ipc_type, it can be included in subsequent struct declarations without causing a warning. The declaration

struct IncExternalStruct {
  int a;
  ExternalStruct1 s1;
}

generates the header output (abbreviated for clarity):

struct IncExternalStruct {
  int a;
  ExternalStruct1 s1;
}
#define IncExternalStruct_IPC_FORMAT "{int," ExternalStruct1_IPC_FORMAT "}"

You can also manually define the IPC format for a type using ipc_type. The declaration

ipc_type ExternalStruct2 = "{char,double}";

tells xdrgen to use the given format string instead of trying to refer to the macro ExternalStruct2_IPC_FORMAT.

Formal XDR language definition

The xdrgen parser aims to parse the XDR language (as specified by Sun Microsystems in RFC 1014 ) wherever this makes sense. However, there are both unsupported features and extensions in the xdrgen input language:

Those interested in a full grammar for the xdrgen input language can look at bison input file src/XDRGen/XDR.y in the IPC distribution.


Trey Smith, <trey@ri.cmu.edu> [ Last modified: Mon Apr 29 17:10:45 2002 ]