Lumberyard
Guía del usuario (Version 1.21)

Anotaciones del generador de código personalizado

La versión publicada de AZ Code Generator es preliminar y está sujeta a cambios.

Puede proporcionar datos adicionales al controlador de plantilla adjuntando anotaciones y etiquetas al código fuente.

Anotaciones de referencia

Cuando crea anotaciones del generador de código personalizado, es una buena idea consultar los ejemplos de las anotaciones existentes en el archivo dev/Code/Framework/AZCore/AZCore/Preprocessor/CodeGen.h. Las anotaciones existentes usan macros de forma masiva como solución a la falta de anotaciones adecuadas en C++.

Clang proporciona un atributo annotate que se pueden leer en tiempo de análisis. Puede utilizar las macros auxiliares proporcionadas para crear nuevas anotaciones, como en el siguiente ejemplo. 

__attribute__((annotate("<Some string here>")))

Este atributo se integra en una macro que convierte el contenido en cadenas que pueden analizar el código de la utilidad del generador de código AZ.

Macros de ayuda

El generador de código AZ tiene dos macros auxiliares para anotaciones: AZCG_CreateAnnotation y AZCG_CreateArgumentAnnotation.

AZCG_CreateAnnotation

AZCG_CreateAnnotation es la macro principal que expone el atributo annotate de Clang subyacente. A continuación se muestra la definición de la macro.

// AZCG_CreateAnnotation #define AZCG_CreateAnnotation(annotation) __attribute__((annotate(annotation)))

Cualquier argumento que se pase a AZCG_CreateAnnotation debe ser una cadena.

AZCG_CreateArgumentAnnotation

La macro AZCG_CreateArgumentAnnotation suele utilizarse para macros de anotaciones. A continuación se muestra la definición de la macro.

// AZCG_CreateArgumentAnnotation #define AZCG_CreateArgumentAnnotation AZCG_CreateAnnotation(AZ_STRINGIZE(annotation_name) "(" AZ_STRINGIZE((__VA_ARGS__)) ")")

La macro AZCG_CreateArgumentAnnotation toma un argumento annotation_name y una serie de argumentos de variables. Los valores transferidos a los argumentos de variable se contraen en una única cadena para el análisis del generador de código AZ.

Anotaciones de ejemplo

En esta sección se proporcionan ejemplos de anotaciones. Un ejemplo reenvía argumentos a la macro subyacente; otro coloca la anotación dentro de una clase y otro inyecta código de vuelta en el archivo de origen.

Anotación sencilla

En el siguiente ejemplo se crea una nueva anotación llamada AzExample que reenvía los argumentos a la macro subyacente.

//Sample Annotation #define AzExample(...) AZCG_CreateArgumentAnnotation(AzExample, __VA_ARGS__)

En este ejemplo, los nombres públicos y privados de la anotación son los mismos. Sin embargo, los nombres internos y externos no tiene que coincidir.

Puede adjuntar la anotación AzExample a la mayoría de los elementos de C++, como en el siguiente ejemplo.

// Sample Tag Usage class ExampleClass { AzExample(description("I am data!")) int m_myData; }

Las etiquetas dentro de la anotación se colocan en formato JSON en el objeto de datos intermedios generado, como en el siguiente ejemplo. Algunos datos se han eliminado por cuestiones de inteligibilidad.

// Sample Tag JSON { "type": "class", "name": " ExampleClass", "annotations" : {}, "fields": [ { "name": "m_myData", "annotations" : { "description" : "I am data!" } } } ] }

Ejemplo de anotación de clases

En el siguiente ejemplo se dirige la utilidad AZ Code Generator para que adjunte una anotación flotante libre a una clase.

// Class Tag Macro #define AzExampleClass(...) AZCG_CreateArgumentAnnotation(AzExampleClass, Class_Attribute, __VA_ARGS__) int AZ_JOIN(m_azCodeGenInternal, __COUNTER__);

AzExampleClass: especifica el nombre de la anotación AzExampleClass (en lugar de AzExample, como en el ejemplo anterior).

Class_Attribute: provoca que la utilidad AZ Code Generator asocie el atributo a la clase que contiene la anotación. La anotación pertenece a la propiedad annotations del objeto de clase.

__VA_ARGS__: especifica los parámetros adicionales que se convierten en una única cadena y se pasan a la utilidad AZ Code Generator para analizarlos.

int AZ_JOIN(m_azCodeGenInternal, __COUNTER__): AZ_JOIN es una macro auxiliar que toma dos entradas de nivel de macro y las une sin convertirlas en cadenas. Como Clang requiere que los atributos de anotación se adjunten a una función o variable, este ejemplo usa AZ_JOIN y una variable de miembros enteros temporales. Después, se pasa por alto la variable de miembro entero temporal.

Añadir la nueva etiqueta al ejemplo anterior produce el siguiente código:

//Class Tag Example class ExampleClass { AzExampleClass(MyExampleClassTags::description("I am an example class!")); AzExample(MyExamplePropertyTags::description("I am data!")) int m_myData; }

Esto produce el objeto JSON intermedio siguiente. Algunos datos se han eliminado para facilitar la comprensión.

// Class Tag JSON "type": "class", "name": "SampleClass", "annotations" : { "MyExampleClassTags::description" : "I am an example class!" }, "fields": [ { "name": "m_myData", "annotations" : { "MyExamplePropertyTags::description" : "I am data!" } } ]

Tenga en cuenta que el JSON anterior no se ve exactamente igual que el JSON de los archivos intermedios proporcionados como parte del marco de trabajo AZ. Esto se debe a que Lumberyard utiliza espacios de nombres en las etiquetas para proporcionar una jerarquía para las etiquetas en los controladores y plantillas. Le recomendamos que importe el archivo clang_cpp.py y ejecute la función format_cpp_annotations(json_object) en el JSON intermedio. Cuando lo haga, puede utilizar todos los patrones y las funciones adecuados en nuestros controladores y scripts.

El siguiente ejemplo muestra el mismo objeto JSON intermedio después del procesamiento por format_cpp_annotations().

// Output of format_cpp_annotations() "type": "class", "name": "SampleClass", "annotations" : { "MyExampleClassTags": { "description" : "I am an example class!" } }, "fields": [ { "name": "m_myData", "annotations" : { "MyExamplePropertyTags": { "description" : "I am data!" } } } ]

Ejemplo de inyección de código generado

En el siguiente ejemplo se muestra cómo inyectar el código generado automáticamente en el archivo original. El ejemplo amplía la anotación AzExampleClass creada previamente inyectando el código en la clase de ejemplo.

// Code Injection Macro #if defined(AZ_CODE_GENERATOR) # define AzExampleClass(ClassName, ...) AZCG_CreateArgumentAnnotation(AzExampleClass, Class_Attribute, identifier(ClassName), __VA_ARGS__) int AZ_JOIN(m_azCodeGenInternal, __COUNTER__); #else # define AzExampleClass(ClassName, ...) AZ_JOIN(AZ_GENERATED_CODE_,ClassName) #endif // AZ_CODE_GENERATOR

La anotación actualizada añade un nuevo parámetro llamado ClassName, que es un identificador que se utiliza para inyectar el código. El identificador se pasa a Clang como identifier(ClassName) y los datos se proporcionan al JSON intermedio. 

Hasta este momento, la macro de anotación fuera de AZ_CODE_GENERATOR ha estado en blanco. El siguiente paso consiste en hacer que se amplíe al identificador de la macro generada por código. Esto hace que el código generado sustituya la anotación de macro cuando el archivo generado se pone en una declaración #include.

Para implementarlo, el ejemplo establece que la macro se convierte en AZ_JOIN(AZ_GENERATED_,ClassName). Como antes, AZ_JOIN en este ejemplo representa esto como AZ_GENERATED_CODE_ExampleClass. El parámetro ClassName proporciona un nombre en tiempo de compilación para la macro generada.

nota

No es necesario que ClassName sea el nombre real de la clase donde se usa la etiqueta. Otras etiquetas que utilizan este mecanismo puede simplemente requerir un identificador único.

Cuando el código de ejemplo anterior se actualiza, se produce el siguiente código:

// Generated Injection Code class ExampleClass { AzExampleClass(ExampleClass, description("I am an example class!")); AzExample(description("I am data!")) int m_myData; }

Este código produce el objeto JSON intermedio siguiente. Observe la nueva anotación de identificador en la clase. Algunos datos se han eliminado por cuestiones de inteligibilidad.

// Generated Code Injection JSON "type": "class", "name": "SampleClass", "annotations" : { "AzExampleClass" : { "identifier" : "ExampleClass", "description" : "I am an example class!" } }, "fields": [ { "name": "m_myData", "annotations" : { "AzExample" : { "description" : "I am data!" } } } ]

Este resultado no se compila hasta que el siguiente código de plantilla usado con la anotación produzca la macro que se espera.

// Template Code {% if class.annotations.identifier is defined %} #define AZ_GENERATED_CODE_{{ asStringIdentifier(class.annotations.identifier) }}\ public: \ {# This method is injected for all classes with the AzExampleClass tag #} bool IsExampleClass(void) { return true; } {% endif %}

Este código genera el siguiente código para su inyección:

// Generated Code for Injection #define AZ_GENERATED_CODE_ExampleClass \ bool IsExampleClass(void) { return true; }

Si el encabezado generado se coloca en una declaración #include del código original, cualquier código de esta macro se inyecta en ExampleClass.