2014-08-28 2 views
16

В настоящее время возможно сканирование/запрос/повторение всех функций (или классов) с помощью некоторого атрибута для всех модулей?D: поиск всех функций с определенным атрибутом

Например:


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 ... */) { 
     ... 
    } 
} 

ответ

37

Верьте или нет, но да, это своего рода это ... хотя это действительно Hacky и имеет много отверстий. Код: http://arsdnet.net/d-walk/

Running, который будет печатать:

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

Вы хотите взять быстрый взгляд на c.d, b.d и main.d увидеть использование. Функция onEach в main.d обрабатывает каждое попадание в функции вспомогательной функции, здесь просто печатается имя. В функции main вы увидите сумасшедший вид mixin(__MODULE__) - это хакерский трюк, чтобы получить ссылку на текущий модуль в качестве отправной точки для нашей итерации.

Также обратите внимание, что файл main.d имеет module project.main; состав топ - если имя модуля просто main как это автоматически, без этого заявления, то mixin хака бы запутать модуль для функции main. Этот код действительно хрупкий!

Теперь направьте свое внимание на 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; 
      } 
     } 
    } 
} 

Во-первых, мы имеем определение атрибута и некоторых помощников, чтобы обнаружить его присутствие. Если вы использовал Удас раньше, ничего действительно новое здесь - просто сканируя атрибуты кортежа для типа интересующих нас

В helper шаблонах трюк для сокращения повторных вызовов __traits(getMember). - это просто псевдонимы его к более приятное имя, избегая при этом глупой ошибки синтаксического анализа в компиляторе.

Наконец, у нас есть мясо ходунка. Он зацикливается на allMembers, рабочая лошадка времени компиляции D (если вы не знакомы с этим, возьмите gander в главе примера моей D Cookbook https://www.packtpub.com/application-development/d-cookbook - ссылка «Free Sample» - это глава о отражении времени компиляции)

Далее, первый static if просто гарантирует, что мы действительно можем получить участника, которого хотим получить. Без этого было бы ошибки при попытке получить частных членов автоматически импортированного модуля object.

Конец функции также прост - он просто называет наш onEach предмет на каждом элементе. Но середина - это волшебство: если он обнаруживает модуль (sooo hacky btw, но только так, как я знаю, чтобы это сделать) импортировать в прогулку, он импортирует его здесь, получая доступ к нему через трюк mixin(module), используемый на верхнем уровне ... таким образом, рекурсия через график импорта программы.

Если вы играете, вы увидите, что это действительно работает. (Компиляция все эти файлы вместе в командной строке BTW для достижения наилучших результатов: dmd main.d attr.d b.d c.d)

Но он также имеет ряд ограничений:

  • Идущий в классе/элементы этой структуры можно, но не реализован здесь , Довольно просто: если член является классом, просто возвращайтесь в него рекурсивно.

  • Он может сломаться, если модуль имеет имя с элементом, например, пример с main, упомянутый выше. Работайте с использованием уникальных имен модулей с некоторыми точками пакета, должно быть хорошо.

  • Он не будет входить в функцию-локальный импорт, то есть можно использовать функцию в программе, которая не будет подхвачена этим трюком. Я не знаю о каком-либо решении этого в D сегодня, даже если вы готовы использовать каждый взломать язык.

  • Добавление кода с UDA всегда сложно, но вдвойне здесь, потому что onEach - это функция с ее областью действия. Возможно, вы могли бы создать глобальный ассоциативный массив делегатов в обработчиках для вещей: void delegate()[string] handlers; /* ... */ handlers[memberName] = &localHandlerForThis; вид вещей для доступа к информации во время работы.

  • Я, конечно, не смогу скомпилировать более сложные вещи, я просто хлопнул это вместе теперь как игрушечное доказательство концепции.

Большинство D-код, вместо того, чтобы идти на дерево импорта, как это, просто требует, чтобы вы mixin UdaHandler!T; в индивидуальной совокупности или модуля, в котором он используется, например, mixin RegisterSerializableClass!MyClass; после каждого. Может быть, не супер СУХОЙ, а более надежный.

Редактировать: Есть еще одна ошибка, которую я не заметил при написании ответа изначально: «module b.d;» фактически не подхватили. Переименование его на «модуль b»; работает, но не когда он включает пакет.

ooooh cuz считается «модулем пакета» в stringof .... который не имеет членов. Может быть, если компилятор просто назвал его «module foo.bar» вместо «package foo», мы бы были в бизнесе. (конечно, это не практично для писателей-приложений), которые в настоящее время разрушают полезность трюка)

+2

Как-то просто принять и проголосовать за такой ответ не достаточно. Я думаю, что этот ответ стоит как минимум 2-3 пинты! –

+2

Для записи я внедрил это в конце с вашим предложением использовать mixins ('mixin sillWalk! (10, func)'). Но прихрамывание вашего кода было очень ценным образовательным опытом! –

+0

@Adam, было бы хорошо, если бы вы могли отредактировать свой ответ, чтобы включить код в тело ответа. –

Смежные вопросы