d

D: encontrar todas las funciones con cierto atributo



(1)

Lo creas o no, pero sí, es un poco ... aunque es REALMENTE hacky y tiene muchos agujeros. Código: http://arsdnet.net/d-walk/

Ejecutando que se imprimirá:

Processing: module main Processing: module object Processing: module c Processing: module attr test2() has sillyWalk main() has sillyWalk

main.d echar un vistazo rápido a cd , bd y main.d para ver el uso. La función onEach en main.d procesa cada golpe que encuentra la función auxiliar, aquí solo imprime el nombre. En la función main , verás una mixin(__MODULE__) aspecto loco mixin(__MODULE__) : este es un truco para obtener una referencia al módulo actual como punto de partida para nuestra iteración.

Observe también que el archivo main.d tiene un module project.main; line up top - si el nombre del módulo fuera main como automáticamente sin esa declaración, el hack de mixin confundiría el módulo para la función main . ¡Este código es realmente frágil!

Ahora, dirija su atención a attr.d : http://arsdnet.net/d-walk/attr.d

module attr; struct sillyWalk { int i; } enum isSillyWalk(alias T) = is(typeof(T) == sillyWalk); import std.typetuple; alias hasSillyWalk(alias what) = anySatisfy!(isSillyWalk, __traits(getAttributes, what)); enum hasSillyWalk(what) = false; alias helper(alias T) = T; alias helper(T) = T; void allWithSillyWalk(alias a, alias onEach)() { pragma(msg, "Processing: " ~ a.stringof); foreach(memberName; __traits(allMembers, a)) { // guards against errors from trying to access private stuff etc. static if(__traits(compiles, __traits(getMember, a, memberName))) { alias member = helper!(__traits(getMember, a, memberName)); // pragma(msg, "looking at " ~ memberName); import std.string; static if(!is(typeof(member)) && member.stringof.startsWith("module ")) { enum mn = member.stringof["module ".length .. $]; mixin("import " ~ mn ~ ";"); allWithSillyWalk!(mixin(mn), onEach); } static if(hasSillyWalk!(member)) { onEach!member; } } } }

Primero, tenemos la definición del atributo y algunos ayudantes para detectar su presencia. Si ha usado UDA antes, no hay nada realmente nuevo aquí, solo escanee la tupla de atributos para el tipo que nos interesa.

Las plantillas de helper son un truco para abreviar las llamadas repetidas a __traits(getMember) : simplemente le __traits(getMember) un nombre más agradable y evita un error de análisis tonto en el compilador.

Finalmente, tenemos la carne del andador. allMembers todos los allMembers , el caballo de batalla de la reflexión del tiempo de compilación de D (si no está familiarizado con esto, eche un vistazo al capítulo de muestra de mi libro de cocina D https://www.packtpub.com/application-development/d-cookbook - el El enlace "Muestra gratis" es el capítulo sobre la reflexión del tiempo de compilación.

A continuación, la primera static if nos aseguramos de que podamos obtener el miembro que queremos obtener. Sin eso, generaría errores al intentar obtener miembros privados del módulo de object importado automáticamente.

El final de la función también es simple: simplemente llama a cada cosa onEach en cada elemento. Pero el punto central es donde está la magia: si detecta un módulo (muy mal por cierto, pero solo sé cómo hacerlo) importar en la caminata, lo importa aquí, obteniendo acceso a él mediante el truco de mixin(module) utilizado en El nivel superior ... recursiva a través del gráfico de importación del programa.

Si juegas, verás que realmente funciona un poco. (Compile todos esos archivos juntos en la línea de comando por cierto para obtener mejores resultados: dmd main.d attr.d bd cd )

Pero también tiene una serie de limitaciones:

  • Entrar en los miembros de clase / estructura es posible, pero no está implementado aquí. Aunque bastante sencillo: si el miembro es una clase, simplemente descienda recursivamente a él también.

  • Es probable que se rompa si un módulo comparte un nombre con un miembro, como el ejemplo con main mencionado anteriormente. Resolver el problema utilizando nombres de módulos únicos con algunos puntos de paquetes también, debería estar bien.

  • No descenderá a las importaciones de función local, lo que significa que es posible utilizar una función en el programa que no se detectará mediante este truco. No tengo conocimiento de ninguna solución a esto en D hoy, ni siquiera si está dispuesto a usar todos los trucos en el lenguaje.

  • Agregar código con UDA siempre es complicado, pero doblemente aquí porque onEach es una función con su alcance. Sin embargo, quizás podría crear una matriz asociativa global de delegados en manejadores para las cosas: void delegate()[string] handlers; /* ... */ handlers[memberName] = &localHandlerForThis; void delegate()[string] handlers; /* ... */ handlers[memberName] = &localHandlerForThis; tipo de cosas para el acceso en tiempo de ejecución a la información.

  • Apuesto a que no podrá compilar en cosas más complejas también, solo puse esto juntos como una prueba de concepto de juguete.

La mayoría del código D, en lugar de intentar recorrer el árbol de importación de esta manera, solo exige que mixin UdaHandler!T; en el agregado individual o módulo donde se usa, por ejemplo, mixin RegisterSerializableClass!MyClass; después de cada uno Tal vez no sea súper SECO, sino mucho más confiable.

edición: hay otro error que no noté al escribir la respuesta originalmente: el "módulo bd;" En realidad no fueron recogidos. Renombrándolo a "módulo b;" Funciona, pero no cuando incluye el paquete.

ooooh cuz se considera "paquete mod" en stringof .... que no tiene miembros. Tal vez si el compilador simplemente lo llamara "módulo foo.bar" en lugar de "paquete foo", estaríamos en el negocio. (por supuesto, esto no es práctico para los escritores de aplicaciones ... lo que arruina la utilidad del truco en este momento)

¿Es posible actualmente escanear / consultar / iterar todas las funciones (o clases) con algún atributo en los módulos?

Por ejemplo:

source/packageA/something.d:

@sillyWalk(10) void doSomething() { }

source/packageB/anotherThing.d:

@sillyWalk(50) void anotherThing() { }

source/main.d:

void main() { for (func; /* All @sillWalk ... */) { ... } }