|
1 | 1 | # Tutorial 23 |
2 | 2 | Reusing definitions from other schemas. |
3 | 3 |
|
| 4 | +Sometimes a need may arise to reuse some elements (usually fields) of one schema / protocol in the definition of another. |
| 5 | +It can be useful when common complex encoding rules, like [ASN.1](https://en.wikipedia.org/wiki/ASN.1) requiring |
| 6 | +custom code injection need to be reused in multiple independent protocols. Version **v5.0** of the |
| 7 | +[CommsDSL Specification](https://commschamp.github.io/commsdsl_spec) allow use of multiple independent |
| 8 | +schema names when processing multiple schema files and referencing fields of one in another. |
| 9 | + |
| 10 | +Version **v5.0** of the **commsdsl2comms** code generator supports this multi-schema feature, but |
| 11 | +requires extra command line parameter `-s` to allow |
| 12 | +use of multiple schemas names in the definition of the protocol. |
| 13 | + |
| 14 | +This tutorial uses two schema files: |
| 15 | + |
| 16 | +- [dsl/schema_ext.xml](dsl/schema_ext.xml) which defines external schema with name (and main namespace) "t23_ext". |
| 17 | +- [dsl/schema.xml](dsl/schema.xml) which defines schema of this tutorial. |
| 18 | + |
| 19 | +The inter-schema field reference is prefixing the usual field reference with `@ + schema name`. |
| 20 | +```xml |
| 21 | +<ref field="@t23_ext.ns1.I1" /> |
| 22 | +``` |
| 23 | +The generated code (in [include/tutorial23/field/I1.h](include/tutorial23/field/I1.h)) defines |
| 24 | +the field (which inherits the name of the referenced field) as an alias type to the field in the `t23_ext` namespace: |
| 25 | +```cpp |
| 26 | +template <typename TOpt = tutorial23::options::DefaultOptions, typename... TExtraOpts> |
| 27 | +using I1 = |
| 28 | + t23_ext::ns1::field::I1< |
| 29 | + TOpt, |
| 30 | + TExtraOpts... |
| 31 | + >; |
| 32 | +``` |
| 33 | +The definition of the `t23_ext::ns1::field::I1` field itself is generated in |
| 34 | +[include/t23_ext/ns1/field](include/t23_ext/ns1/field) folder, i.e. the [include](include) |
| 35 | +folder has two sub-folders for the different namespaces. |
| 36 | + |
| 37 | +The member field of the `Msg` message definition also references field in the external schema. |
| 38 | +```xml |
| 39 | +<message name="Msg1" id="MsgId.M1" displayName="^Msg1Name"> |
| 40 | + <ref field="I1" name="F1" /> |
| 41 | + <ref field="@t23_ext.ns1.S1" name="F2" /> |
| 42 | +</message> |
| 43 | +``` |
| 44 | + |
| 45 | +The definition of the `Length` field in the external `t23_ext` schema is taken from the |
| 46 | +previous [tutorial22](../tutorial22). Instead of referencing it with the **<ref>** |
| 47 | +field definition (which is also possible) the schema just copies its definition using **reuse** |
| 48 | +property: |
| 49 | +```xml |
| 50 | +<bundle reuse="@t23_ext.ns1.Length" reuseCode="true" /> |
| 51 | +``` |
| 52 | +Please note the usage of the **reuseCode** property. The **reuse** one just copies the XML definition of the |
| 53 | +field without copying any of the custom injected code of the field. Setting **reuseCode** boolean property |
| 54 | +ensures that the custom code is also copied. As the result, the `Length` field definition can be |
| 55 | +found in the `tutorial23` namespace ([include/tutorial23/field/Length.h](include/tutorial23/field/Length.h)) and |
| 56 | +cannot be found in `t23_ext` because it's not really referenced. The `Length` in the `tutorial23` namespace |
| 57 | +is referenced by the frame: |
| 58 | +```xml |
| 59 | +<frame name="Frame"> |
| 60 | + <sync name="Sync"> |
| 61 | + <int name="SyncField" type="uint16" defaultValue="0xabdc" /> |
| 62 | + </sync> |
| 63 | + <size name="Size" field="Length" /> |
| 64 | + <id name="Id" field="MsgId" /> |
| 65 | + <payload name="Data" /> |
| 66 | +</frame> |
| 67 | +``` |
| 68 | + |
| 69 | +Also note that the custom injected code of the `Length` field is located in the folder specifying its |
| 70 | +original location ([dsl_src/include/t23_ext/ns1/field](dsl_src/include/t23_ext/ns1/field)). |
| 71 | + |
| 72 | +Using multiple schemas requires extra attention to specifying protocol options |
| 73 | +(the classes defined in the [include/tutorial23/options](include/tutorial23/options) folder). |
| 74 | +The option classes are designed to be folded and outer classes may extend or override options defined |
| 75 | +by the inner ones. Every schema defines its own independent set of options: |
| 76 | + |
| 77 | +- [include/t23_ext/options](include/t23_ext/options) - Options for the elements inside `t23_ext` schema. |
| 78 | +- [include/tutorial23/options](include/tutorial23/options) - Options for the elements inside `tutorial23` schema. |
| 79 | + |
| 80 | +In order to combine them the `DefaultOptions` of the protocol (`tutorial23`) schema are expected to wrap and extend |
| 81 | +the external (`t23_ext`) one. As the result the [ClientSession](src/ClientSession.h) defines its |
| 82 | +options in the following way: |
| 83 | +```cpp |
| 84 | +using ClientProtocolOptions = |
| 85 | + tutorial23::options::ClientDefaultOptionsT< |
| 86 | + tutorial23::options::DefaultOptionsT< |
| 87 | + t23_ext::options::ClientDefaultOptions |
| 88 | + > |
| 89 | + >; |
| 90 | +``` |
| 91 | +Without using options from the `t23_ext::options` the compilation will fail because |
| 92 | +`t23_ext::ns1::field::S1` [field](include/t23_ext/ns1/field/S1.h) attempts to |
| 93 | +access the options referencing `typename TOpt::t23_ext::ns1::field::S1`, which |
| 94 | +does not existing in the options provided by the `tutorial23`. |
| 95 | + |
| 96 | +```cpp |
| 97 | +template <typename TOpt = t23_ext::options::DefaultOptions, typename... TExtraOpts> |
| 98 | +class S1 : public |
| 99 | + comms::field::String< |
| 100 | + t23_ext::field::FieldBase<>, |
| 101 | + TExtraOpts..., |
| 102 | + typename TOpt::t23_ext::ns1::field::S1, |
| 103 | + comms::option::def::SequenceSerLengthFieldPrefix<typename S1Members<TOpt>::LengthPrefix> |
| 104 | + > |
| 105 | +``` |
| 106 | +
|
| 107 | +In addition to the complications around the protocol options definitions, which if not done right may |
| 108 | +result in difficult to understand compilation errors, referencing the fields in another schema may |
| 109 | +cause problems in different build or packaging systems when multiple independent schemas (protocols) reusing the |
| 110 | +common one may attempt to generate and overwrite the same common files. |
| 111 | +To cope with this problem the code generation utilities (**commsdsl2comms** and others) allow |
| 112 | +renaming different namespaces to avoid clashing. |
| 113 | +
|
| 114 | +However, as was shown in this tutorial the fields which are **reuse**-ed rather than |
| 115 | +**<ref>**-erenced don't generate code in the external namespace. In case the **all** |
| 116 | +the schema fields defined this way, the external namespace will be empty and no code for it |
| 117 | +will be generated. As the result the definition of the protocol options are also getting |
| 118 | +much simpler. |
| 119 | +
|
| 120 | +The bottom line, it is highly recommended to **reuse** the fields from the external schema rather |
| 121 | +than **<ref>**-erence them. |
4 | 122 |
|
5 | 123 | ## Summary |
6 | 124 |
|
7 | | -- |
| 125 | +- External schema definitions reuse is supported since **v5.0** of the [CommsDSL](https://commschamp.github.io/commsdsl_spec) |
| 126 | + and [commsdsl](https://github/commschamp/commsdsl) code generators. |
| 127 | +- Inter-schema referencing is allowed using `@<schema_name>.` prefix. |
| 128 | +- When using multiple schemas it is hight recommended to **reuse** the fields rather |
| 129 | +than **<ref>**-erence them. |
8 | 130 |
|
9 | 131 |
|
10 | 132 | [Read Previous Tutorial](../tutorial22) <----------------------- |
0 commit comments