STRUCTS_TYPE_UNION(3)

NAME
structs_type_union - structs types for C unions

LIBRARY
PDEL Library (libpdel, -lpdel)

SYNOPSIS
#include <sys/types.h>
#include <stddef.h>
#include <pdel/structs/structs.h>
#include <pdel/structs/type/union.h>
DEFINE_STRUCTS_UNION(struct_name, union_name);
STRUCTS_UNION_TYPE(union_name, field_list);
STRUCTS_UNION_FIELD(field_name, field_type);
int
structs_union_set(const struct structs_type *type, const char *name, void *data, const char *field_name);

DESCRIPTION
The STRUCTS_UNION_TYPE() macro defines a structs(3) type (i.e., a struct structs_type) for describing a struct structs_union, which is an intermediate structure describing a union union_name.

    struct structs_union {
        const char  *const field_name; /* name of field currently in use */
        void        *un;               /* pointer to the union itself */
    };

The field_name always indicates which of the union fields is currently in use. It should never be modified directly; instead, use the function structs_union_set() (see below). un points to the actual union, which is stored in a separately allocated buffer typed_mem(3) type union union_name. This buffer is only large enough to hold the specific field currently in use.

To define a structure equivalent to a struct structs_union that declares un to have the correct C type for the union used (instead of void *), use the DEFINE_STRUCTS_UNION() macro, where struct_name is the name of the structure (or empty if no name is desired) and union_name is the name of the represented union. Then the un field will be declared to have type union union_name *.

The union's fields are accessible by name. Of course, only one field may be accessed at a time, and changing the current union field causes the previous field contents to be lost.

As a special case, the field named “field_name” is also read-only accessible and always returns the name of the union field currently in use. The union itself must not contain a field named “field_name,” or else it will not be accessible. The “field_name” field does not appear in the output of structs_xml_output(3) or structs_traverse(3).

STRUCTS_UNION_TYPE() defines a structs(3) type for a C union. The field_list parameter must point to an array of struct structs_ufield structures describing each union field:

    /* This structure describes one field in a union */
    struct structs_ufield {
        const char                  *name;       /* field name */
        const struct structs_type   *type;       /* field type */
    };

The STRUCTS_UNION_FIELD() macro should be used to define an entry in the field array: field_name is the name of the field and field_type is a pointer to the structs(3) type describing the field.

The fields need not be listed in the array in the same order as they are declared in the C union. However, the array must be terminated with an entry have name equal to NULL.

The first field in the list is the “default field” and it is chosen as the current field when a union data type is initialized. When a union data type is read in as XML by structs_xml_input(3), if the innermost XML tag is omitted (i.e., the one that specifies the field name) and the default field has primitive type, then the default field is assumed.

structs_union_set() should be used to change the union field currently in use, to guarantee that the old field's contents are properly deallocated and the new field's contents are properly initialized. The new field will be initialized to its default value. The sub-field name of the object pointed to by data must correspond to a struct structs_union (or equivalent structure), and field_name must match one of the union's field names. The structs_find(3) function may also be used to change the current union field.

RETURN VALUES
structs_union_set() returns zero if successful, otherwise -1 with errno set appropriately.

SEE ALSO
libpdel(3), structs(3), structs_type(3), structs_type_struct(3), structs_xml_input

EXAMPLES
The program below sets a union field using the field name and value specified on the command line:

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <err.h>
    #include <pdel/structs/structs.h>
    #include <pdel/structs/type/union.h>
    #include <pdel/structs/type/string.h>
    #include <pdel/structs/type/ip4.h>
    #include <pdel/structs/type/int.h>
    #include <pdel/util/typed_mem.h>
    /* My union */
    union foobar {
            char            *name;
            u_int16_t       index;
            struct in_addr  ipaddr;
    };
    /* My structs_union structure */
    DEFINE_STRUCTS_UNION(foobar_union, foobar);
    /* Structs type for a 'struct foobar_union' */
    static const struct structs_ufield foobar_fields[] = {
            STRUCTS_UNION_FIELD(name, &structs_type_string),
            STRUCTS_UNION_FIELD(index, &structs_type_uint16),
            STRUCTS_UNION_FIELD(ipaddr, &structs_type_ip4),
            { NULL }
    };
    static const struct structs_type foobar_type =
            STRUCTS_UNION_TYPE(foobar, &foobar_fields);
    static void
    show_union(struct foobar_union *un)
    {
            /* Show the result */
            if (strcmp(un->field_name, "name") == 0)
                    printf("name=\"%s\"\n", un->un->name);
            else if (strcmp(un->field_name, "index") == 0)
                    printf("index=%u\n", un->un->index);
            else if (strcmp(un->field_name, "ipaddr") == 0)
                    printf("ipaddr=%s\n", inet_ntoa(un->un->ipaddr));
            else
                    printf("unknown field \"%s\"\n", un->field_name);
    }
    int
    main(int argc, char **argv)
    {
            struct foobar_union un;
            const char *name;
            char *value;
            char ebuf[64];
            /* Initialize union */
            if (structs_init(&foobar_type, NULL, &un) == -1)
                    err(1, "structs_init");
            printf("Default value: ");
            show_union(&un);
            /* Get the requested field's name and value from command line */
            if (argc != 3)
                    errx(1, "usage: setfield <name> <value>");
            name = argv[1];
            value = argv[2];
            /* Set the requested field's value */
            if (structs_set_string(&foobar_type, name,
                value, &un, ebuf, sizeof(ebuf)) == -1)
                    errx(1, "%s: %s", name, ebuf);
            /* Show the result */
            printf("New value: ");
            show_union(&un);
            /* Done, clean up */
            structs_free(&foobar_type, NULL, &un);
            return (0);
    }

HISTORY
The PDEL library was developed at Packet Design, LLC. http://www.packetdesign.com/

AUTHORS
Archie Cobbs <archie@freebsd.org>