Implementación de una función intrínseca personalizada de NatVis para C++
En este artículo, obtendrá información sobre las directrices para implementar una función intrínseca personalizada dentro de una visualización de NatVis. Para obtener más información sobre NatVis, vea Crear vistas personalizadas de objetos de C++.
Sintaxis
Un archivo NatVis puede definir una función intrínseca mediante la sintaxis siguiente:
<Intrinsic Name="Func" Expression="arg + 1">
<Parameter Name="arg" Type="int" />
</Intrinsic>
Una vez que exista la definición, cualquier expresión del depurador puede llamar a la función, como cualquier otra función. Por ejemplo, con la definición de NatVis anterior, la expresión Func(3)
se evalúa como 4.
Un elemento <Intrinsic>
puede aparecer en el nivel de archivo o dentro de un elemento <Type>
, antes de cualquier otro elemento. Las funciones intrínsecas definidas dentro de un <Type>
definen funciones miembro de ese tipo, mientras que las funciones intrínsecas definidas fuera de un <Type>
definen funciones globales. Por ejemplo:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="std::vector<*>">
<Intrinsic Name="size" Expression="_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst" />
<Intrinsic Name="capacity" Expression="_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst" />
</Type>
<Intrinsic Name="LOWORD" Expression="arg & 0xFFFF">
<Parameter Name="arg" Type="unsigned int" />
</Intrinsic>
</AutoVisualizer>
En el ejemplo anterior, size()
y capacity()
se definen como funciones miembro de la clase std::vector
, por lo que cada vez que una expresión evalúa vector.size()
, realmente evaluará vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst
, en lugar de realizar una func-eval.
Del mismo modo, LOWORD(0x12345678)
devuelve 0x5678
, también sin func-eval.
Para otro ejemplo, consulte la expansión intrínseca.
Directrices para usar una función intrínseca
Tenga en cuenta las instrucciones siguientes al usar una función intrínseca:
Las funciones intrínsecas se pueden sobrecargar, ya sea con funciones definidas por PDB o entre sí.
Cuando una función intrínseca entra en conflicto con una función definida por PDB con el mismo nombre y lista de argumentos, la función intrínseca ganará. No se puede evaluar la función PDB si existe una función intrínseca equivalente.
No se puede tomar la dirección de una función intrínseca; solo puede llamarla.
Las funciones miembro intrínsecas deben pertenecer a la vista predeterminada del tipo respectivo para evaluarse en una expresión. Por ejemplo, una entrada de tipo con una restricción
IncludeView
puede no especificar una función intrínseca.Se puede llamar a funciones intrínsecas desde cualquier expresión, incluidas las expresiones NatVis. Las funciones intrínsecas también pueden llamarse entre sí. Sin embargo, actualmente no se admiten funciones intrínsecas que usan recursividad. Por ejemplo:
<!-- OK --> <Intrinsic Name="Func2" Expression="this->Func3(arg + 1)"> <Parameter Name="arg" Type="int" /> </Intrinsic> <Intrinsic Name="Func3" Expression="arg + 1"> <Parameter Name="arg" Type="int"/> </Intrinsic> <!-- Unsupported --> <Intrinsic Name="Func2" Expression="this->Func3(arg + 1)"> <Parameter Name="arg" Type="int" /> </Intrinsic> <Intrinsic Name="Func3" Expression="Func2(arg + 1)"> <Parameter Name="arg" Type="int"/> </Intrinsic> <!-- Also unsupported--> <Intrinsic Name="Fib" Expression="(n <= 1) ? 1 : Fib(n - 1) + Fib(n - 2)"> <Parameter Name="n" Type="int"/> </Intrinsic>
De forma predeterminada, se supone que las funciones intrínsecas son libres de efectos secundarios. Es decir, se las puede llamar en contextos que no permiten efectos secundarios y la expresión implementada no puede contener efectos secundarios.
La definición puede invalidar este comportamiento especificando el atributo
SideEffect
en la declaración. Si la función está marcada como que tiene efectos secundarios, entonces los efectos secundarios en la expresión de implementación están permitidos. Sin embargo, ya no se permite invocar la función en contextos en los que ya no se permiten efectos secundarios.Cuando las definiciones de dos funciones intrínsecas entran en conflicto entre sí (el mismo nombre, la misma firma), la última gana (dentro del mismo archivo).
Dentro de archivos diferentes, la instancia del archivo con mayor prioridad gana (el proyecto es mayor que el directorio de usuario, que es mayor que el directorio de instalación). Si una definición de prioridad superior contiene una expresión que no analiza, esa definición se omite y se usa la definición de prioridad más alta en su lugar.
Directrices para implementar una función intrínseca
Las funciones intrínsecas admiten dos formas posibles de implementación:
Basado en expresiones
El archivo NatVis define una expresión que se evalúa como el valor devuelto de la función. La expresión puede usar cualquier argumento pasado a ella, declarado como elemento
<Parameter>
. También se supone que las funciones definidas dentro de una clase son funciones de "instancia" y también pueden tener acceso al puntero "this".Basado en extensiones
El archivo NatVis proporciona instrucciones para invocar una extensión del depurador para evaluar realmente la función. Una extensión del depurador tiene acceso completo a la API de Concord y tiene la capacidad de realizar tareas que no son posibles dentro de una expresión NatVis.
Para proporcionar una implementación basada en extensiones, el elemento <Intrinsic>
debe omitir el atributo Expression
y, en su lugar, proporcionar SourceId
, LanguageId
, Id
y atributos ReturnType
, como se proporciona en el ejemplo siguiente:
<Intrinsic Name="MyFunc" SourceId="a665fa54-6e7d-480e-a80b-1fc1202e9646" LanguageId="3a12d0b7-c26c-11d0-b442-00a0244a1dd2" Id="1000" ReturnType="double">
<Parameter Type="int" />
<Parameter Type="int" />
<Parameter Type="int" />
</Intrinsic>
Para implementar la función, la extensión del depurador debe implementar la interfaz de IDkmIntrinsicFunctionEvaluator140
, con filtros LanguageId
y SourceId
que coincidan con los valores correspondientes del elemento <Intrinsic>
en el archivo NatVis. Cuando se llama a la función , la llamada se traduce en el método Execute()
del componente:
STDMETHOD(Execute)(
_In_ Evaluation::IL::DkmILExecuteIntrinsic* pExecuteIntrinsic,
_In_ Evaluation::DkmILContext* pILContext,
_In_ Evaluation::IL::DkmCompiledILInspectionQuery* pInspectionQuery,
_In_ const DkmArray<Evaluation::IL::DkmILEvaluationResult*>& Arguments,
_In_opt_ DkmReadOnlyCollection<Evaluation::DkmCompiledInspectionQuery*>* pSubroutines,
_Out_ DkmArray<Evaluation::IL::DkmILEvaluationResult*>* pResults,
_Out_ Evaluation::IL::DkmILFailureReason* pFailureReason
);
El componente recibe los bytes de cada argumento a través del argumento Arguments
. Si la función en cuestión es una función miembro, el puntero this
viene primero, seguido de los argumentos explícitos. El componente debe devolver el resultado asignando una matriz de un solo elemento en pResults
, almacenando los bytes del valor devuelto.
Use las instrucciones siguientes para implementar funciones:
No es válido "mezclar y hacer coincidir" las dos formas de implementación. Es decir, no se puede incluir una expresión ni un identificador de origen.
Se permite especificar un tipo de valor devuelto para una implementación basada en expresiones, pero no es necesario. Si se especifica un tipo de valor devuelto, el tipo de valor devuelto de la expresión debe coincidir exactamente con él (no se permite ninguna conversión implícita). Si no se especifica un tipo de valor devuelto, el tipo de valor devuelto se deduce de la expresión. Cualquier implementación basada en extensiones debe indicar un tipo de valor devuelto en el archivo NatVis.
Se permiten llamadas no recursivas a otras funciones intrínsecas. No se permite la recursividad.
Si la función tiene efectos secundarios, debe especificar
SideEffect="true"
en la declaración. Es ilegal que una implementación basada en expresiones tenga efectos secundarios en la expresión sin declarar que la función tenga efectos secundarios. Invocar una implementación basada en extensiones para tener efectos secundarios sin declarar la función como tener efectos secundarios es un comportamiento indefinido y debe evitarse.Se permiten funciones intrínsecas de Varargs. Para declarar una función varargs, especifique
Varargs="true"
en la declaración. Aunque es legal que una implementación basada en expresiones declare una funciónvararg
, actualmente, solo las implementaciones basadas en extensiones tienen una manera de acceder a los argumentos de variable. Con una implementación basada en extensiones, la funciónExecute()
recibe todos los argumentos que se pasan realmente, no solo los argumentos declarados.No se admiten funciones intrínsecas que consumen un tipo de clase, estructura o unión como tipo de argumento. Devolver un tipo de clase, estructura o unión está bien. (Un puntero o referencia a un tipo class/struct/union es OK como un tipo de argumento).
Personalice el icono para las llamadas a funciones intrínsecas.
De forma predeterminada, cuando se llama a una función intrínseca, la expresión recibe el icono de diamante rosa en la ventana Inspección asociada a las llamadas de función. Puede invalidar este comportamiento especificando el atributo Category
mediante uno de los siguientes valores:
- Método. Use el icono de diamante rosa, que normalmente se usa con llamadas de método (valor predeterminado).
- Propiedad. Utilice el icono de llave inglesa negra, que normalmente se utiliza con las propiedades.
- Datos. Use el icono de diamante azul, que normalmente se usa con datos.
Al combinar funciones intrínsecas con el elemento <Item>
, es posible crear un archivo NatVis donde las expresiones del elemento tengan el ícono de propiedad de llave inglesa:
<Type Name="MyClass">
<Intrinsic Name="GetValue" ReturnType="int" Expression="m_value" Category="Property" />
<Expand>
<Item Name="Value">this->GetValue()</Item>
</Expand>
</Type>
Nota
Al colocar la opción de icono en el nivel de función, en lugar del nivel de <Item>
, se evitan problemas en los que se pierde la personalización del icono cuando se evalúa el nombre completo. Dado que el nombre completo incluye una llamada a la función, tiene la misma personalización de icono que la <Item>
.