IOP attributes

IOP attributes were added to allow the implementation of many new features in the iopc compiler and in the usage of IOP in general.

Attributes can be added to declarations (struct, union, enum, rpc, interface and module) and to fields of structure/union. An attribute can have any number of arguments, in which case they all need to be specified.

Here is an example:

@attr1 @attr2(1, 'foo', bar) @attr3(int_arg=1, string_arg="foo", ident_arg=bar)
struct Foo {
    @attrN
    int someField;
}

As you can see, there are 2 ways to specify arguments, positional (e.g. attr2) or named (e.g. attr3).

And 3 types of arguments: int/double, strings (between double quotes), and identifiant ([a-zA-Z][a-zA-Z0-9_]*)

iopc attributes

These attributes only affect the output of iopc.

Name version Arguments Declarations Fields Description

ctype

2.0.0

type(ident)

struct/union/enum

N/A

change the C-Type name with a typedef (the new name must still have a __t suffix)

prefix

2.0.0

name(ident)

enum

N/A

change the prefix name in the C enum

nowarn

2.0.0

value(ident)

ALL

ALL

disable a specific iopc warning

private

2.3.0

N/A

ALL

optional or with default value

make field private (won’t appear in public interfaces and WSDL (Web Service Definition Language))

alias

2.4.0

name(ident)*

rpc

N/A

create alias RPCs with the given name to the RPC on which it is applied

package foo;

@ctype(e_a__t)
@prefix(A)
enum MyEnumA {
    A = 0x0,
    B = 0x1,
    C = 0x2,
};

struct MyStructA {
    @private
    MyEnumA a;
};

@ctype(type = my_union_a__t)
union MyUnionA {
    @nowarn(keyword)
    int     class;
};

interface MyIntfA {
    @alias("bar", "baz")
    foo
        in void
        out null;
};

The usage of ctype will generate the following typedef and define:

typedef foo__my_enum_a__t e_a__t;
#define e_a_to__str(...) foo__my_enum_a__to_str(__VA_ARGS__);
...
typedef foo__my_union_a__t my_union_a__t;
#define my_union_a__init(...) foo__my_union_a__init(__VA_ARGS__);
...

The usage of prefix will generate the following enum values: A_A, A_B, A_C but does not affect the C type.

Finally nowarn(keyword) will make the warning "class is a keyword in some languages" disappear.

Constraints

Constraints add restrictions on the values that an IOP struct/union can contain and on typedef.

They are automatically checked at unpacking time (whether the input is json, xml or binary). This implies that when sending an invalid query over a IOP-Channel or HTTP channel, the RPC server will fail to unpack the query and reply with IC_MSG_INVALID. The error message caused by a constraint can be fetched with iop_get_err_lstr().

At packing time, it’s up to the developer to call the following function:

int iop_check_constraints(const iop_struct_t *desc, const void *val);

Each structure/union also has the following shortcut:

int pfx##__check(pfx##__t *v);
Name Version Arguments Declarations Fields Description

strict

2.0.0

N/A

enum

N/A

values must belong to the enum

min

2.0.0

value(int/double)

N/A

integer/double

inclusive min of the values

max

2.0.0

value(int/double)

N/A

integer/double

inclusive max of the values

minOccurs

2.0.0

value(int)

N/A

arrays

inclusive min of the array length

maxOccurs

2.0.0

value(int)

N/A

arrays

inclusive max of the array length

cdata

2.0.0

N/A

N/A

strings

when packing in XML, choose CDATA over xml quoting

nonEmpty

2.0.0

N/A

N/A

strings

empty string forbidden

nonZero

2.0.0

N/A

N/A

integer/double

0 value forbidden

minLength

2.0.0

value(int)

N/A

strings

inclusive min of the string length

maxLength

2.0.0

value(int)

N/A

strings

inclusive max of the string length

length

2.4.6

value(int)

N/A

strings

length of the string

pattern

2.0.0

value(string)

N/A

strings

force character set (maybe more later)

allow

2.9.14

field(ident)

N/A

unions/enums

restrict the list of allowed union fields (or enum values) for that use case

disallow

2.9.14

field(ident)

N/A

unions/enums

disallow some union fields (or enum values) for that use case

forceFieldName

2021.04

N/A

N/A

all fields

allows the field names that do not respect camel case notation

Example:

@strict
enum MyEnumA {
    A = 0x0,
    B = 0x1,
    C = 0x2,
};

union MyUnionA {
    int a;
    int b;
    int c;
};

struct User {
    /* XML packer will use CDATA for login */
    @cdata
    /* string length must be between 5 and 128, and only alphanum, _ and - characters are accepted */
    @minLength(5) @maxLength(128) @pattern("[a-zA-Z0-9_\-]*")
    string  login;
    /* Only 0,1,2 values accepted here */
    MyEnumA a;

    @allow(A, B) /* Only 0,1 values accepted here */
    MyEnumA abOnly;

    /* this array can contain between 5 and 10 values, each value belonging to [-5;5] */
    @minOccurs(5) @maxOccurs(10)
    @min(-5) @max(5)
    int[]  intTab;
    /* integer arguments can be expression too */
    @max(10*5)
    uint[]  uintTab;
    /* but double expression are not supported */
    @min(-5.5)
    double[]  doubleTab;

    /* allow only fields a and c in that instance of union MyUnion */
    @allow(a, c)
    MyUnion myUnion;
    @allow(a) @allow(c)
    MyUnion myUnion;
    /* disallow field b in that instance of union MyUnion */
    @disallow(b)
    MyUnion myUnion;
};

Multi constraints

The multi constraints are allowed for some numeric attributes(min, max, minOccurs, maxOccurs, minLength, maxLength). The min attribute takes the highest value and vice versa for for the max attribute.

Example:

@max(42) @min(5)
typedef uint Digit;

struct Test {
    @max(21) @min(7)// here min is 7 and  max is 21
    Digit a;

    @max(45) @min(3) // here min is the typedef value (5) and the max too (42)
    Digit b;
};

@minLength(5) @maxLength(128)
typedef string ExStr;

struct StrTest {
    @maxLength(21) @minLength(7)
    ExStr a;
};

@maxOccurs(42) @minOccurs(5)
typedef int[] ExTab;

struct TabTest {
    @maxOccurs(21) @minOccurs(7)
    ExTab a;
};

Generic attributes

One can declare a generic attribute to attache a piece of information to an IOP element which has no meaning for the IOP engine but can be used at run-time with IOP introspection.

A generic attribute is declared as follow:

@(namespace:name, value)

As described in the example, the full name of the generic attribute is in two parts: a namespace and a name, separated by a colon. The namespace is here to avoid collision in attributes name. The value can be either an integer, a double, a boolean or a string. Generic attributes can be declared for IOP structs, classes, fields, enums, interfaces and RPCs.

Here is an example:

package foo;

@(namespace1:genAttr1, 2)
struct MyStruct {
    @(namespace1:genAttr2, "toto");
    @(namespace1:genAttr1, 10);
    @(namespace1:genAttr3, true);
    int field;

    @(namespace1:genAttr2, "toto");
    bool field2;
};

@(namespace1:genAttr1, 1)
interface foo {

    @(namespace1:genAttr3, false)
    myFunction
         in (int a);
}

A generic attribute can’t be defined twice for the same IOP element. However, it can be defined as many time as needed throughout an IOP package. An IOP element can be linked to several different generic attributes.

At run-time, generic attributes can be accessed by lib-common helpers thanks to their names.

See lib-common/iop.h for a description of all availables generic attributes helpers.