xdrgen
Documentationxdrgen
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.
xdrgen
for the first timexdrgen
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 optionsusage: 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.
xdrgen
type declarationsxdrgen
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:
xdrgen
allows arbitrary code
to be inserted at the end of a struct definition. In C++, this
can be used to define member functions. Defining the
struct
starting with struct Zoo
instead
of typedef struct _Zoo
allows us to define
constructors for Zoo
in the arbitrary
code section.
Zoo_IPC_FORMAT
or the member function
Zoo::getIPCFormat()
, which may enable you to write
cleaner code.
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;
.
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:
string
field is followed by <> because
strings are always variable-length arrays in XDR. This will be
discussed more later.
bool
type isn't defined by default in C.
Therefore, whenever xdrgen
creates C-language
output, it includes a definition for bool
as an enumerated type compatible with the built-in C++ definition.
In C++, a bool
is a 4-byte data structure which
takes on the values false
=0 or true
=1.
In the IPC format string, the bool
field is
represented as "int"
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]}"
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:
int
or unsigned int
int
fields of the struct correspond to
dimensions of the array in order from left to right (most
significant dimension to least significant).
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.
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"
string
typechar
. 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.
int
field
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]}"
%{
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}"
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
.
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:
xdrgen
tries to capture. The IPC format string for a datatype is
generated from the C datatype according to the rules in the
Defining Message Data Formats section.
There is no reason to expect that IPC network
messages will follow the XDR binary packing rules.
union
types. There are no
corresponding types in IPC.
hyper
(8-byte integer) types.
There are no corresponding types in IPC.
*
syntax). Supported in IPC, but not implemented by xdrgen
.
opaque
replaced with char
and unsigned char
. A char
type need
not be in an array.
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.