Merge pull request #1844 from jrw972/monterey
[ACE_TAO.git] / TAO / TAO_IDL / docs / annotations.md
blob7dd447a5b011f1a0cfa4db880eae3eed218d8a47
1 # Implementing Annotations
3 **How to use the internal API to implement built-in annotations in `tao_idl` or a
4 compiler that uses `tao_idl`.**
6 **Table of Contents:**
8 * [IDL Annotations](#idl-annotations)
9 * [What Can Be Annotated](#what-can-be-annotated)
10   * [Special Cases of Annotations](#special-cases-of-annotations)
11     * [Union Discriminators](#union-discriminators)
12     * [Base Types in Sequences](#base-types-in-sequences)
13     * [Base Types in Arrays](#base-types-in-arrays)
14 * [Defining Annotations](#defining-annotations)
15   * [`@document` Example](#document-example)
16   * [What Can Go in Annotations](#what-can-go-in-annotations)
17 * [Reading Annotations in the AST](#reading-annotations-in-the-ast)
18   * [Reading `@document` Annotations](#reading-document-annotations)
19     * [Reading Annotations Manually](#reading-annotations-manually)
20   * [Reading Special Cases of Annotations](#reading-special-cases-of-annotations)
21     * [Union Discriminators](#union-discriminators-1)
22     * [Base Types in Sequences](#base-types-in-sequences-1)
23     * [Base Types in Arrays](#base-types-in-arrays-1)
24 * [Extending Annotation Support](#extending-annotation-support)
25 * [Limitations](#limitations)
26 * [History](#history)
27   * [TAO 2.5.5](#tao-255)
28   * [TAO 2.5.6](#tao-256)
29   * [TAO 2.5.7](#tao-257)
30   * [TAO 2.5.10](#tao-2510)
32 ## IDL Annotations
34 Annotations are a feature of IDLv4 that allows IDL authors to pass hints to the
35 IDL compiler that can change compiler behavior and code generation. They are
36 similar to some uses of `#pragma`, but are more powerful because they are
37 integrated with IDL and are more expressive. In the latest IDL specification as
38 of writing, version 4.2, they are described in section 7.4.15.1.
40 The concept behind annotations exists in other languages like Java and Python,
41 as decorators, and in C++11 and C#, as attributes. Like Java and Python,
42 annotations can appear in front of declarations, have `@` at the beginning, and
43 look like function calls.
45 Here is an example of what IDL using some OMG standard annotations might look
46 like:
48 ```
49 enum Urgency_t {
50   SUPPLEMENTARY,
51   @default_literal INFORMATIVE,
52   CRITICAL
55 @unit("Hour(s)")
56 @range(min=0,max=23)
57 typedef short Hours_t;
59 @unit("Day(s)")
60 typedef unsigned long Days_t;
62 struct Time_t {
63   Hours_t hours;
64   Days_t days;
67 @mutable
68 struct Report {
69   @key
70   unsigned long index;
72   @optional
73   Time_t expiration;
75   @optional
76   Urgency_t urgency;
78   string message;
80 ```
82 ## What Can Be Annotated
84 Annotations "may be applied to any IDL constructs or sub-constructs", as
85 defined by the OMG. This is very vague and the OMG has not clarified this as of
86 IDL 4.2 [(Limitation #1)](#limitation1). Because of the lack of standardization
87 of what can be annotated and how, annotations for specific IDL elements have to
88 be added on a as-needed basis.
90 You can put annotations before the following things in IDL and a backend using
91 TAO IDL's front-end library can read them:
93 - modules
94 - `typedef`s
95 - constants
96 - structures and their member values
97 - unions and their cases
98 - enumerations and their enumerators
99 - interfaces, porttypes, eventtypes, components and all of their direct
100   contents
101 - valuetypes and most of their direct contents with the exception of these
102   kinds of valuetype statements:
103   - import: not supported by TAO
104   - typeid: not supported by TAO within valuetypes
105   - typeprefix: No corresponding AST Node to attach annotations to
107 These are the general cases. The rest of the cases are defined in the next
108 section. If an annotation application isn't listed in the general case list or
109 the special case section, it is almost certainly not supported and might cause
110 warnings if the usage is a general case and syntax errors if the usage a
111 special case. See ["Extending Annotation
112 Support"](#extending-annotation-support) if you're interested in adding it and
113 are familiar with GNU Bison.
115 ### Special Cases of Annotations
117 The annotations on all the elements in the list above can be accessed using the
118 `annotations()` method covered later in [Reading Annotations in the
119 AST](#reading-annotations-in-the-ast). In the cases listed below, the
120 annotation is used within the declaration and therefore require special access
121 methods. See ["Reading Special Cases of
122 Annotations"](#reading-special-cases-of-annotations) for how to have a backend
123 read these kinds of annotations.
125 #### Union Discriminators
127 **[See Compiler Example](#union-discriminators-1)**
130 enum GradingSystem_t {
131   PASS_FAIL,
132   PASS_70,
133   PASS_80
135 union Grade_t switch (@key GradingSystem_t) {
136 case PASS_FAIL:
137   boolean pass;
138 case PASS_70:
139 case PASS_80:
140 default:
141   short grade;
145 #### Base Types in Sequences
147 **[See Compiler Example](#base-types-in-sequences-1)**
150 struct Event {
151   short data;
153 typedef sequence<@external Report, 12> Dozen_Events;
156 #### Base Types in Arrays
158 **[See Compiler Example](#base-types-in-arrays-1)**
161 struct Event {
162   short data;
164 typedef Dozen_Events Event @external [12];
167 ## Defining Annotations
169 Annotations should be defined after the AST is initialized and ready to be
170 used, but before any user defined IDL is processed. The recommended place for
171 this is `BE_post_init ()` which is located in `be/be_init.cpp` in `tao_idl`.
172 Annotations are nodes in the AST and could be defined by hand, simulating what
173 happens in `fe/idl.yy`. However a string parsing utility has been added just
174 for this purpose, `idl_global->eval (const char* idl, bool disable_output =
175 false)`. `eval ()` processes IDL as if it were in an IDL file so annotations
176 can be defined using the IDL annotation notation. You can disable warning and
177 error messages generated by parsing by passing `true` as the second argument.
179 ### `@document` Example
181 As a simple example, if we wanted to make an annotation that inserted comments
182 into the product files for documentation purposes, we could design an
183 annotation like this:
186 @annotation document {
187   enum API_Type {
188     INTERNAL_API,
189     USER_API,
190     LEGACY_API
191   };
192   string comment;
193   API_Type api_type default INTERNAL_API;
194   boolean deprecated default FALSE;
198 To use it without defining it in every IDL file, we need to embed it into
199 `BE_post_init()`.
201 ```C++
202 void BE_post_init (char *[], long)
204   // ...
205   if (idl_global->idl_version_ > IDL_VERSION_3)
206     {
207       idl_global->eval (
208         "@annotation document {\n"
209         "  enum API_Type {\n"
210         "    INTERNAL_API,\n"
211         "    USER_API,\n"
212         "    LEGACY_API\n"
213         "  };\n"
214         "  string comment;\n"
215         "  API_Type api_type default INTERNAL_API;\n"
216         "  boolean deprecated default FALSE;\n"
217         "};\n"
218       );
219     }
220   // ...
224 The new lines aren't strictly necessary but might help if a error occurs during
225 `eval` because it will refer to the line number of this string as though it was
226 a file called `builtin-N`, where `N` is times `eval` has been called.
228 By default TAO\_IDL uses IDL3 and this will cause an error when parsing the
229 annotations. Version is controlled using `--idl-version` command line argument
230 and ultimately `idl_global->idl_version_`. In the example above we would have
231 to pass `--idl-version 4`.
233 We can set it to use IDL4 by default in `BE_init ()`:
235 ```C++
236 int BE_init (int &, ACE_TCHAR *[])
238   // ...
239   idl_global->default_idl_version_ = IDL_VERSION_4;
240   // ...
243 void BE_post_init (char *[], long)
245   // Same as above ...
249 In TAO\_IDL, `idl_global->default_idl_version_` sets `idl_global->idl_version_`
250 after `BE_init` is called but before arguments are parsed. This gives the user
251 a chance to override it if they really want to and allows them to query the
252 version we're setting using `--default-idl-version`.
254 Alternatively if it is desired to retain compatibility with older versions of
255 TAO, use the `TAO_IDL_HAS_ANNOTATIONS` macro.
257 ```C++
258 int BE_init (int &, ACE_TCHAR *[])
260   // ...
261 #ifdef TAO_IDL_HAS_ANNOTATIONS
262   idl_global->default_idl_version_ = IDL_VERSION_4;
263 #endif
264   // ...
267 void BE_post_init (char *[], long)
269   // ...
270 #ifdef TAO_IDL_HAS_ANNOTATIONS
271   if (idl_global->idl_version_ > IDL_VERSION_3)
272     {
273       idl_global->eval (
274         // ...
275       );
276     }
277 #endif
278   // ...
282 This would also be used when reading the annotations later.
284 <a name="document-usage"></a>
285 Once the annotation is declared, it can be used in IDL:
288 @document("Struct with 1 member")
289 struct struct1 {
290   short x;
293 @document(
294   comment="Struct with 2 members",
295   api_type=USER_API
297 struct struct2 {
298   short x, y;
301 @document(
302   comment="Struct with 3 members",
303   api_type=LEGACY_API,
304   deprecated=TRUE
306 struct struct3 {
307   short x, y, z;
311 However it won't do anything because nothing using the AST is looking for it,
312 so it will be ignored. To make the program aware of the annotations, see
313 ["Reading Annotations in the AST"](#reading-annotations-in-the-ast) below.
315 ### What Can Go in Annotations
317 - Annotation members can be of any type that constants can be. This includes
318 booleans, integers, floats, enumerations, characters, and strings.
319 - Enumerations, constants, and typedefs can be declared inside the annotation
320 declaration, however they can not used outside the annotation expect for when
321 passing them as parameters to the same annotation. Otherwise normal scope rules
322 apply: Valid constant types and values from outside the annotation can be used
323 inside it.
325 ## Reading Annotations in the AST
327 To get the annotations for most nodes types, use
328 `node->annotations ().find (annotation_decl)` where `annotation_decl` can be the
329 `AST_Annotation_Decl` object or its canonical internal TAO IDL name (see next
330 paragraph). This will return the last `AST_Annotation_Appl*` of that type on
331 the node or `NULL` if there no annotation of that type. Because
332 `AST_Annotation_Appls::find` can take a `AST_Annotation_Decl`, they can be looked
333 up after `idl_eval` creates them and cached for a slightly faster
334 `find`.
336 Internally, annotation local names are prefixed with `@` to prevent clashes
337 with other elements in IDL with the same name. For example when trying to use
338 `AST_Annotation_Appls::find (const char*)` with annotation named `bar` in a module
339 named `foo`, the proper internal scoped name to pass as the second argument is
340 either `foo::@bar` or `::foo::@bar` if we want to be explicit that `foo` is in
341 the root module. In IDL, this annotation's full name would be `@foo::bar`or
342 `@::foo::bar`.
344 After that check, you can use index operators `[const char*]` on the annotation
345 to get the individual members and `value()` to get the value.
347 The last part is not straightforward, as the value is an `AST_Expression` object
348 and `AST_Expression` is a complex class that handles constant values in
349 TAO\_IDL. There are examples below but see `AST_Expression::AST_ExprValue` for
350 how values can be accessed.
352 See `include/ast_expression.h` and `ast/ast_expression.cpp` for how
353 to use `AST_Expression`.
355 ### Reading `@document` Annotations
357 In this example we will use the [`@document` annotation defined
358 above](#document-example) to generate Doxygen comments in the C++ code
359 generated. For simplicity's sake, we will limit this example to `struct`s defined
360 in TAO client headers. This can be accomplished by modifying the struct
361 visitor in `be/be_visitor_structure/structure_ch.cpp`.
363 At the top of the file, these includes should be added:
365 ```C++
366 #include "ast_annotation_member.h"
367 #include "utl_string.h"
368 #include "ast_enum_val.h"
371 About midway though the file, in
372 `int be_visitor_structure_ch::visit_structure (be_structure *node)`
373 right before
374 ```C++
375 *os << be_nl_2
376     << "struct " // ...
378 these lines would also need to be added:
380 ```C++
381   AST_Annotation_Appl *document = node->annotations ().find ("::@document");
382   if (document)
383     {
384       const char *comment =
385         dynamic_cast<AST_Annotation_Member *> ((*document)["comment"])->
386           value ()->ev ()->u.strval->get_string ();
388       bool deprecated =
389         dynamic_cast<AST_Annotation_Member *> ((*document)["deprecated"])->
390           value ()->ev ()->u.bval;
392       /*
393        * This is more complicated because we are trying to get the name of
394        * the enumerator. If we just wanted the number value, we could treat the
395        * AST_Expresssion from the annotation member as an unsigned long by using
396        * ev ()->u.ulval.
397        */
398       const char *api_type = 0;
399       AST_Expression *api_type_val =
400         dynamic_cast<AST_Annotation_Member *> ((*document)["api_type"])->
401           value ();
402       AST_Enum *api_type_enum = api_type_val->enum_parent();
403       if (api_type_enum)
404         {
405           AST_EnumVal *enum_val =
406             api_type_enum->lookup_by_value (api_type_val);
407           if (enum_val)
408             {
409               api_type = enum_val->local_name ()->get_string ();
410             }
411         }
413       *os
414         << "/**" << be_nl
415         << " * " << comment << be_nl
416         ;
418       if (api_type)
419         {
420           *os
421             << " *" << be_nl
422             << " * API_TYPE: " << api_type << be_nl
423             ;
424         }
426       if (deprecated)
427         {
428           *os
429             << " *" << be_nl
430             << " * \\deprecated This is deprecated" << be_nl
431             ;
432         }
433       *os << " */";
434     }
437 Using the [`@document` use example from above](#document-usage), these are
438 inserted into the client header file:
440 ```C++
441 // ...
443  * Struct with 1 member
445  * API_TYPE: INTERNAL_API
446  */
447 struct struct1
449   // ...
452 // ...
455  * Struct with 2 members
457  * API_TYPE: USER_API
458  */
459 struct struct2
461   // ...
465  * Struct with 3 members
467  * API_TYPE: LEGACY_API
469  * \deprecated This is deprecated
470  */
471 struct struct3
473   // ...
475 // ...
478 #### Reading Annotations Manually
480 `AST_Annotation_Appls::find ()` is convenient but only returns the last
481 annotation of the passed annotation type. If we want the first one, handle
482 multiple annotations of the same type, read all the annotations, or some other
483 subset, we will have to do what `find ()` is doing for us, which is just
484 iterating over the `AST_Annotation_Appl`s and checking `annotation_decl ()`.
486 ```C++
487   AST_Annotation_Appls &annotations = /* ... */;
488   AST_Annotation_Decl *annotation = /* ... */;
489   for (AST_Annotation_Appls::iterator i = annotations.begin ();
490     i != annotations.end (); ++i)
491     {
492       AST_Annotation_Appl *appl = i->get ();
493       if (appl && appl->annotation_decl () == annotation)
494         {
495           // Do work with annotation application
496         }
497     }
500 ### Reading Special Cases of Annotations
502 Annotations placed before a definition in a scope are interpreted as annotating
503 the node that is being defined. Annotations in other places require special
504 grammar and special handling in the API.
506 The following cases show how to read annotations from these special cases.
508 **NOTE:** To access these methods on a type that has been "`typedef`-ed", it
509 must be resolved completely using `AST_Type *primitive_base_type ()` and a
510 `dynamic_cast` to the correct type as these methods are specific to these
511 classes.
513 #### Union Discriminators
515 **[See IDL Example](#union-discriminators)**
517 ```C++
518   AST_Union *node = /* ... */;
519   AST_Annotation_Appl *document = node->disc_annotations ().find ("::@key");
522 #### Base Types in Sequences
524 **[See IDL Example](#base-types-in-sequences)**
526 ```C++
527   AST_Sequence *node = /* ... */;
528   AST_Annotation_Appl *document = node->base_type_annotations ().find ("::@external");
531 #### Base Types in Arrays
533 **[See IDL Example](#base-types-in-arrays)**
535 ```C++
536   AST_Array *node = /* ... */;
537   AST_Annotation_Appl *document = node->base_type_annotations ().find ("::@external");
540 ## Extending Annotation Support
542 **NOTE: This section assumes familarity with GNU Bison and only covers the
543 general concept of extending annotation support.**
545 How to extend support for annotations on a particular IDL element depends on a
546 few things. In the `fe/idl.ypp` bison file, if the annotation would be matched
547 by the `at_least_one_definition` token, like it would when annotating a
548 structure, or by the `at_least_one_export` token, like it would be when
549 annotating a interface operation, the change is simple in principle:
551 1. Make sure the `AST_Decl*` node of what you want to annotate is being passed
552    up the to generic annotation code in `at_least_one_export` or
553    `at_least_one_definition`.
555 1. Implement `virutal bool annotatable() const` in the `AST_*` files of the
556    node type to return `true`. The default implementation of `AST_Decl` returns
557    `false`.
559 If you want to implement a annotation that goes within an IDL element, like
560 annotating a union discriminator, that is more complicated. It will involve
561 modifying the grammar to accept the annotations and adding a special cases
562 method to the node's class, like in ["Reading Special Cases of
563 Annotations"](#reading-special-cases-of-annotations).
565 Finally, if you do extend annotation support, please update the annotation test
566 in `$TAO_ROOT/tests/IDLv4/annotations/be_init.cpp` and this file, specifically
567 the ["What Can Be Annotated"](#what-can-be-annotated) and ["History"](#history)
568 sections. Also update ["Reading Special Cases of
569 Annotations"](#reading-special-cases-of-annotations) if you've added support for
570 a special case.
572 ## Limitations
574 The current limitations exist in TAO\_IDL annotation implementation as of writing:
576 <a name="limitation1"></a>
577 1. Because of lack of any specification in IDL for where annotations can go,
578    annotations in places other than what's listed in ["What Can be
579    Annotated"](#what-can-be-annotated) can cause syntax errors, even if the IDL
580    works with other IDL tools.
582 2. Even though this is implicitly allowed by the IDL specification, Annotations
583    whose local names clash with IDL keywords are not supported. This includes
584    the OMG standard annotations `default` and `oneway`.
586 ## History
588 ### TAO 2.5.5
590 **Any usage of `UTL_find_annotation(X, Y)` should be replaced with `X.find(Y)`.**
592 After TAO 2.5.4 was released, which introduced annotations, there was a change
593 to fix memory leaks caused by annotations. This change involved replacing
594 `typedef ACE_Vector<AST_Annotation_Appl> AST_Annotation_Appls` with a class of
595 the same name. This also allowed for moving `UTL_find_annotation` into
596 `AST_Annotation_Appls` as `find` for a nicer design.
598 ### TAO 2.5.6
600 The TAO IDL Frontend no longer internally prefixes annotation names and
601 annotation member names with `_cxx_` if they are also a C++ keyword.
603 ### TAO 2.5.7
605 - The TAO IDL Frontend now supports annotations on interfaces, operations, and
606   attributes.
608 - `idl_global->eval` now will produce error and warning messages. This can be
609   silenced by passing `true` as a second argument.
611 - Expanded documentation on what can be annotated and how to extend annotation
612   support.
614 ### TAO 2.5.10
616 - Annotation support extended:
617   - All the direct contents of interfaces
618   - The porttypes, eventtypes, components and all their direct contents
619   - Valuetypes and most of their direct contents