Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 16:17
Оценка:
Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).

Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.


Для чего это может быть нужно?
Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
1. Выделить все условия
2. Вставить в код исполняемой функции автологирование факта захода в ветку
3. Запомнить в некотором файле ветки, в которые всё это вставлено

Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.
Re: Как передать в макрос параметр времени компиляции
От: para  
Дата: 22.08.11 16:34
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).


FDS>Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.



FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия
FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку
FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено

FDS>Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.

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

или вам надо скопировать (по шаблону ) код одной функции в другие?

я так понимаю вас интересует что-то типа АОП 1 2
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 16:40
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).


Он скорее странный.

FDS>Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.


Ну, если функция к этому времени уже скомпилирована, то особой проблемы быть не должно.
Не ясно зачем только передавать в качестве параметра функцию. Обычно просто делают некий набор флагов. Эти флаги анализируются в макросе и вызваеются те или иные функции (расположенные в некотором, заранее известном, модуле той же макро-библиотеки).

Но можно конечно и функции вызвать. Для этого в качестве параметра макросу нужно передавать имя функции, а саму функцию вызвать динамически (например, через макрос late).

FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия

Что значит "выделить"?

FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку


А может проще использовать макрос log из стандартной библиотеки? Или хотя бы создать свой на его основе?

FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено


FDS>Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.


Не пойму зачем тут функция? Достаточно просто ввести некоторые флаги и уже в соответствии с ними легировать все что нужно, кода нужно.

Посмотри как это сделано в том же макросе log. Там есть макроатрибуты уровня сборки: LogFlag, LogFunction, LogCondition и LogFormat, которые позволяют многое настроить.

ЗЫ

Может быть я не понял идею, но зачем передавать функцию я не понял.

Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.

Если речь о передачи кода который нужно динамически скомпилировать, то мне кажется ты на неверном пути. Хотя конечно можно скомпилировать код динамически или вызвать его через late.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Как передать в макрос параметр времени компиляции
От: para  
Дата: 22.08.11 16:53
Оценка:
Здравствуйте, para, Вы писали:

P>я так понимаю вас интересует что-то типа АОП


ещё интереснее aop/src/ftests/Target_positive.n
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:06
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.


Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)
При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]

Что-то такое можно сделать?
Re[3]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:28
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Здравствуйте, VladD2, Вы писали:


VD>>Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.


FDS>Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


Ты точно правильно понимаешь значение словосочетания "целевая программа"? Целевая программа — это программа в при компиляции которой раскрывается макрос (генерируется код). Чтобы в ней что-то вызвать нужно всего лишь сгенерировать код вызова. Макрос ниже генерирует код вызова функции имя которой предано в качестве параметра макроса.
macro X(func)
{
  <[ func() ]>
}



FDS>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


Ну, логировать ты же хочешь что-то из целевой программы (той где макрос применяется)? Тогда опиши нужную функцию логирования в целевой программе (или одной из библиотек подключенных к ней) и генери (в макросе) код обращения к нужной функции.

FDS>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

Указывай. Кто тебе мешает?

Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.

FDS>А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]


FDS>Что-то такое можно сделать?


Да, все возможно. Ты просто пока не можешь поднять свое сознание на мета-уровень .

Похоже, что ты просто не верно понимаешь саму суть макросов. Макрос генерирует код. Ему плевать, скомпилированно, что-то или нет. Ему даже плевать есть ли функция вызов которой он пытается сгенерировать. Макрос опирирует выражениями. Они не имеют семантики. Только конечный, сгенерированный код что-то значит.

По сему ты можешь передавать в качестве параметров макросов любой код который с синтаксической точки зрения является корректным кодом немерле. А уже то как макрос будет интерпретировать и использовать эти выражения зависит только от создателя макроса. По сему ты можешь передвать в макрос код вроде "x => x * x", а макрос уже будет интерпретировать его как хочет. Захочет подставит туда где требуется лямбда с одним аргументом, а захочет, разберет его на запчасти, и произведет замену все вхождений x (в некотором, другом выражении) на "x * x", или еще как-то по другому проинтерпретирует.

Главное понять, что до возвращения из макроса ложки нет семантики нет. Ее ты задаешь сам. Скомпилированных функций тоже нет. Но ты можешь генерировать код вызова чего угодно. И все что нужно вызовется, если в конечном итоге в скомпилированной программе это что-то будет присутствовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:38
Оценка:
Здравствуйте, FDSC, Вы писали:

Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:44
Оценка:
Здравствуйте, VladD2, Вы писали:

FDS>>Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


VD>Ты точно правильно понимаешь значение словосочетания "целевая программа"? Целевая программа — это программа в при компиляции которой раскрывается макрос (генерируется код). Чтобы в ней что-то вызвать нужно всего лишь сгенерировать код вызова. Макрос ниже генерирует код вызова функции имя которой предано в качестве параметра макроса.

VD>
VD>macro X(func)
VD>{
VD>  <[ func() ]>
VD>}
VD>



В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.
Т.е. то же, но без квазицитирования.

FDS>>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


VD>Ну, логировать ты же хочешь что-то из целевой программы (той где макрос применяется)? Тогда опиши нужную функцию логирования в целевой программе (или одной из библиотек подключенных к ней) и генери (в макросе) код обращения к нужной функции.


Нет, логирование идёт дважды: один раз на этапе компиляции генерируется эталон, а второй раз генерируется реальный обход ветвей при тестах. Грубо говоря, я просто хочу залогировать таким образом покрытие.

FDS>>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

VD>Указывай. Кто тебе мешает?


И вызвать это на этапе компиляции

VD>Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.


Насколько я понимаю, это всё делается во время исполнения. Там очень много квазицитирования и, вроде бы, оно всё вставляется в исполняемый код, если я правильно понял (условия логирования и print_expr).

FDS>>А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]


FDS>>Что-то такое можно сделать?


VD>Да, все возможно. Ты просто пока не можешь поднять свое сознание на мета-уровень .


Пока не могу.

VD>Похоже, что ты просто не верно понимаешь саму суть макросов. Макрос генерирует код. Ему плевать, скомпилированно, что-то или нет. Ему даже плевать есть ли функция вызов которой он пытается сгенерировать. Макрос опирирует выражениями. Они не имеют семантики. Только конечный, сгенерированный код что-то значит.


Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


VD>Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.


У меня инет не такой хороший, так что не получится. Если только ICQ
Re[5]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:53
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.

FDS>Т.е. то же, но без квазицитирования.

Зачем, на этапе компиляции то?

Еще раз. Логирование то где ведется?

FDS>>>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


FDS>Нет, логирование идёт дважды: один раз на этапе компиляции генерируется эталон, а второй раз генерируется реальный обход ветвей при тестах.


Что за эталон?

FDS>Грубо говоря, я просто хочу залогировать таким образом покрытие.


Опять таки, покрытие вызовов в целевой программе?
Если да, то зачем что-то вызывать во время компиляции? Нужно сгенерировать код который это самое покрытие вычислит во время тестирования. (хотя это, вроде как, не плохо делают внешние утилиты).

FDS>>>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>>>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

VD>>Указывай. Кто тебе мешает?


FDS>И вызвать это на этапе компиляции


Можно конечно и так извернуться, но это явно неверный дизайн и много не нужного траха.

VD>>Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.


FDS>Насколько я понимаю, это всё делается во время исполнения. Там очень много квазицитирования и, вроде бы, оно всё вставляется в исполняемый код, если я правильно понял (условия логирования и print_expr).


Естественно.

FDS>Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.


Я все же не могу понять, что ты понимаешь под этим эталоном покрытия. Приведи код который ты хочешь увидеть так если бы ты был вынужден написать его без макросов (за макросы). Ну, и за одно приведи этот самый эталонный файл (опять таки, написанный вручную).

Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:54
Оценка:
Здравствуйте, FDSC, Вы писали:

VD>>Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.


FDS>У меня инет не такой хороший, так что не получится. Если только ICQ


Я на даче иной раз через ЖПРС выхожу. И скайп работает нормально. По нему можно и в режиме чата общаться. Скажу больше, в основном в этом режиме и общаемся.

А ICQ я давно не держу. Скайп его (и все остальное) с успехом заменяет.

Как вариант, есть еще гугльтолк.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 18:08
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


FDS>>В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.

FDS>>Т.е. то же, но без квазицитирования.

VD>Зачем, на этапе компиляции то?


VD>Еще раз. Логирование то где ведется?


Логирование ведётся два раза: эталонное на этапе компиляции и реальное на этапе выполнения. Проще говоря, на этапе компиляции макрос генерирует все возможные варианты меток логирования (все возможные трассы), а затем, после выполнения программы в runtime, эти метки могут быть сравнены и указано, что какая-то очерёдность меток не достигнута.
Грубо говоря

для кода
if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

when (условие3)
  ...


на этапе компиляции макрос сгенерирует следующие трассы (возможные потоки исполнения)
условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).
Т.е. логом, в некотором смысле, это называть некорректно: это информация об эталонном покрытии, которое мы ожидаем встретить в логах (грубо говоря, часть лога).

FDS>>Грубо говоря, я просто хочу залогировать таким образом покрытие.


VD>Опять таки, покрытие вызовов в целевой программе?

Да

VD>Если да, то зачем что-то вызывать во время компиляции? Нужно сгенерировать код который это самое покрытие вычислит во время тестирования. (хотя это, вроде как, не плохо делают внешние утилиты).


Да, но как я узнаю, что нужно покрыть? Кто сгенерирует для меня варианты покрытия, которые я должен покрыть?

FDS>>Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.


VD>Я все же не могу понять, что ты понимаешь под этим эталоном покрытия. Приведи код который ты хочешь увидеть так если бы ты был вынужден написать его без макросов (за макросы). Ну, и за одно приведи этот самый эталонный файл (опять таки, написанный вручную).


без макросов нужно делать синтаксический анализатор, который как я указал выше выведет в файл покрытия все возможные или все интересные нам сочетания меток в зависимости от того, какой критерий покрытия указал программист (например, программист может указать, что выражение условие2&&условие3 истинно).

VD>Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.


Хочу на немерле повторить основную идею вот этого http://www.unitesk.ru/ http://ru.wikipedia.org/wiki/%D2%E5%F5%ED%EE%EB%EE%E3%E8%FF_UniTESK
unitesk
Re[7]: Мелкая ошибка
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 18:20
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>(например, программист может указать, что выражение условие2&&условие3 истинно).


условие2||условие3 — истинно. Тогда макрос не должен сгенерировать путь "условие1"
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 18:30
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия
FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку
FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено

Попробую заняться телепатией и сам додумать задачу.

Итак у тебя есть желание сделать нечто вроде системы проверки покрытия кода тестами.

Для реализации этого ты хочешь макросам инструментировать код методов целевой программы так чтобы каждая его инструкция (или блоки инструкций гранулированные по некоторому признаку) была обернута в некий код который запоминает куда-то информацию о том, что этот участок кода был вызван. Например, если у нас есть следующая функция в целевой программе:
module X
{
  Foo(arg : int) : void
  {
    if (arg > 0)
      Foo(arg - 1);
    else
     Bar();
  }
}

и ты хочешь чтобы программа была переписана следующим образом:
static _visitedFlags : uint = 0;
static _locations : array[Nemerle.Compiler.Location] = array[
    Location("Foo.n", 5, 5, 5, 17), // местоположение "if (arg > 0)"
    Location("Foo.n", 6, 7, 6, 20), // местоположение "Foo(arg - 1);"
    ...
  ];

module X
{
  Foo(arg : int) : void
  {
    { _visitedFlags |= 0x01; 
    if ({ _visitedFlags |= 0x02; arg > 0})
      { _visitedFlags |= 0x04; Foo(arg - 1); }
    else
      { _visitedFlags |= 0x08; Bar(); }
    }
  }
}

DisplayUnvisitedFor_Foo() : void
{
  WriteLine("Not covered code locations of method 'Foo'");

  foreach (locIndex in BitsToIndexes(_visitedFlags))
    WriteLine(_locations[locIndex]);

  WriteLine();
}


Теперь, чтобы понять есть ли места в Foo не покрытые тестами нужно всего лишь прогнать тесты и вызвать метод DisplayUnvisitedFor_Foo(), который выведет список участков кода которые не были покрыты тестами.

Естественно, что в реальной реализации все должно быть несколько сложнее, так как:
1. В некоторых функциях кода будет так много, что одного поля uint или даже ulong не хватит и придется создавать составные поля.
2. Не имеет смысла выводить список непокрытых подвыражений, если выражение в которое они вложены так же непокрыто. Тут достаточно вывести только самый верхний непокрытый узел. Стало быть кроме флагов непокрытия еще нужно иметь информацию о вложенности веток. Тогда можно будет убрать все лишние ветки оставив только верхние.

Но это уже детали реализации — рутина. Принципиально это уже ничего не меняет.

Так вот, я не вижу зачем в этой задачи нужно что-то вызывать динамически, во время компиляции макроса. И зачем создавать какие-то внешние файлы.

Все что нужно сделать — это добавить (сгенерировать) код который будет вычислять покрытие и выводить о нем информацию. Код этот можно добавлять прямо в исходные типы. Так как он статический, он не будет сильно мешать. Кроме того, так как он нужен только в тестах, то и генерировать его можно на базе некоторых условий (например, когда задан некий символ препроцессора). Так что в релизе код мешаться не будет.

Что касается гибкости вывода информации о покрытии, то этого можно добиться дав возможность задать имя функции используемой для выводи информации о непокрытых частях кода (в моем примере используется WriteLine). Впрочем, тут много вариантов которые ограниченны только твоей фантазией.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 18:59
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Логирование ведётся два раза: эталонное на этапе компиляции и реальное на этапе выполнения. Проще говоря, на этапе компиляции макрос генерирует все возможные варианты меток логирования (все возможные трассы), а затем, после выполнения программы в runtime, эти метки могут быть сравнены и указано, что какая-то очерёдность меток не достигнута.


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

Здесь
Автор: VladD2
Дата: 22.08.11
я описал вариант решения твой задачи. Он прост, эффективен и не требует какой-либо алхимии.

FDS>Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).


А далее ты хочешь сравнивать текст логов? А как потом определять чему в коде соответствует несовпадение лога?

FDS>Т.е. логом, в некотором смысле, это называть некорректно: это информация об эталонном покрытии, которое мы ожидаем встретить в логах (грубо говоря, часть лога).


Вот именно! Логом — это назвать нельзя. Это информация о покрытии. Ну так на фиг ее куда-то там пихать, и потом еще к тому же с чем-то сравнивать. Решай основную задачу — собирай информацию о покрытии.

Логи же могут быть тупо разными в зависимости от параметров. И как ты при этом будешь их сравнивать со своим эталоном?

Твоя главная проблема сделать сбор и накопление информации о покрытии максимально эффективным (с точки зрения памяти и процессорного времени), чтобы твои тесты не начали жутко тормозить. Это наталкивает на мысль, что формат хранения информации о покрытии должен быть компактным и не требовать динамического заема памяти.

Что же нам нужно запоминать? А всего лишь информацию о том, что некоторый блок был посещен). Блоки можно тупо пронумеровать в пределах метода, класса или даже всей программы. Лучше ограничиться методом, так как иначе объем структуры для хранения информации может быть слишком велик и начнутся промахи мимо кэша.

Тут подойдут два варианта:
1. Использование статического массива.
2. Использование битовых полей.

Битовые поля эффективнее, с массивами несколько проще работать.

FDS>Да, но как я узнаю, что нужно покрыть? Кто сгенерирует для меня варианты покрытия, которые я должен покрыть?


Ты же сам сказал "инструментировать код". Ну, дык и делай это!

Просто пробеги по коду метода и оберни все блоки приводящие к ветвлению в некий код собирающий оинформацию о их посещении. Каждый такой блок будет иметь порядковый номер (в рамках метода). Далее останется только прогнать тесты, и вывести список блоков не посещенных в них.

Информацию о блоках можно запоминать в виде структуры хранящей местоположение блока. У компиляторе немерла уже есть такая структура. Она называется Location и находится в пространстве имен Nemerle.Compiler. Можно или сделать ее аналог и поместить его в целевое приложение, или подключить к проекту Nemerle.Compiler.dll и использовать Nemerle.Compiler.Location.

Далее остается только отобразить порядковый номер на местоположение блока. Это легко сделать, если во время инструментирования генерировать Location-ов. Тода индекс в этом массиве будет соответствовать номеру блока в функции.

Ну, а сделать функцию которая выводит список Location-ов для не посещенных блоков — это уже совсем ерунда.

FDS>без макросов нужно делать синтаксический анализатор, который как я указал выше выведет в файл покрытия все возможные или все интересные нам сочетания меток в зависимости от того, какой критерий покрытия указал программист (например, программист может указать, что выражение условие2&&условие3 истинно).


У тебя исходно не верная стратегия. Ты зачем-то прилепляешь к задаче логирование. Хотя логирование здесь вообще не причем.

А от тебя я просил чего-то вроде примеров что я привел здесь
Автор: VladD2
Дата: 22.08.11
.

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


VD>>Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.


FDS>Хочу на немерле повторить основную идею вот этого http://www.unitesk.ru/ http://ru.wikipedia.org/wiki/%D2%E5%F5%ED%EE%EB%EE%E3%E8%FF_UniTESK


Ну, это что-то очень абстрактное. Что ни понимаю под формальной моделью я вообще не понимаю. В общем, случае это какая-то фигня. Модель можно придумать только для конкретной задачи и далеко не для всех случаев.

В общем, это опять не то. Ты же собрался решать конкретную задачу? Вот ее и надо было описать. Покрытие, так покрытие.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:05
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Итак у тебя есть желание сделать нечто вроде системы проверки покрытия кода тестами.

верно

VD>Для реализации этого ты хочешь макросам инструментировать код методов целевой программы так чтобы каждая его инструкция (или блоки инструкций гранулированные по некоторому признаку) была обернута в некий код который запоминает куда-то информацию о том, что этот участок кода был вызван.

Верно

VD>Например, если у нас есть следующая функция в целевой программе:

VD>
VD>module X
VD>{
VD>  Foo(arg : int) : void
VD>  {
VD>    if (arg > 0)
VD>      Foo(arg - 1);
VD>    else
VD>     Bar();
VD>  }
VD>}
VD>

VD>и ты хочешь чтобы программа была переписана следующим образом:
VD>
VD>static _visitedFlags : uint = 0;
VD>static _locations : array[Nemerle.Compiler.Location] = array[
VD>    Location("Foo.n", 5, 5, 5, 17), // местоположение "if (arg > 0)"
VD>    Location("Foo.n", 6, 7, 6, 20), // местоположение "Foo(arg - 1);"
VD>    ...
VD>  ];

VD>module X
VD>{
VD>  Foo(arg : int) : void
VD>  {
VD>    { _visitedFlags |= 0x01; 
VD>    if ({ _visitedFlags |= 0x02; arg > 0})
VD>      { _visitedFlags |= 0x04; Foo(arg - 1); }
VD>    else
VD>      { _visitedFlags |= 0x08; Bar(); }
VD>    }
VD>  }
VD>}

VD>DisplayUnvisitedFor_Foo() : void
VD>{
VD>  WriteLine("Not covered code locations of method 'Foo'");

VD>  foreach (locIndex in BitsToIndexes(_visitedFlags))
VD>    WriteLine(_locations[locIndex]);

VD>  WriteLine();
VD>}
VD>


VD>Теперь, чтобы понять есть ли места в Foo не покрытые тестами нужно всего лишь прогнать тесты и вызвать метод DisplayUnvisitedFor_Foo(), который выведет список участков кода которые не были покрыты тестами.


Не совсем верно, хотя близко.
Если наш тест упал, то мне хотелось бы иметь информацию, в каком именно условии он упал, а для этого мне нужно логировать трассу прямо при входе в условие (если упадёт — уже нечему будет логировать потом).
DisplayUnvisitedFor — функция, очевидно, внешняя, должна анализировать лог. Сам лог сложнее, т.к. покрытие мы используем не по операторам, а по возможным путям потока управления.

Очевидно, что для того, чтобы DisplayUnvisitedFor сработала, она должна иметь список всех возможных путей потока управления, чтобы проанализировать, все ли они были достигнуты ( об этом я писал в http://rsdn.ru/forum/nemerle/4389580.1.aspx
Автор: FDSC
Дата: 22.08.11
, видимо, ты не успел прочитать ).

VD>Так вот, я не вижу зачем в этой задачи нужно что-то вызывать динамически, во время компиляции макроса. И зачем создавать какие-то внешние файлы.

VD>Все что нужно сделать — это добавить (сгенерировать) код который будет вычислять покрытие и выводить о нем информацию.

1. Файл нужен, чтобы функция DisplayUnvisitedFor могла провести вычисления, т.к. у нас есть более сложный набор комбинаций веток и функции нужно знать, что должно было быть покрыто (некоторые условия могут быть взаимосвязаны).
2. Функции-параметры нужны чтобы
1) Определить имя файла-эталона и, если нужно, его формат (если кто-то захочет его ещё и прочитать, а не только вывести для парсинга)
2) Для индивидуализации критерия покрытия. Я могу передавать соотв. PExpr из макроса в эту функцию, а она сама будет определять, что именно логировать и что именно требовать для покрытия. Грубо говоря, исходный макрос может ничего не знать о переменных типа int и обработать выражение x > 10 как одно условие. Функция индивидуализации преобразует его, скажем, в условия обхода для любого x в виде: <0, 0, >0, >10; т.е. добавит ещё условия проверки при нуле, и учёт знаков, чтобы в функцию были переданы отрицательные значения (возможно, программист забыл, что числа могут быть меньше нуля, а не только меньше десяти, но больше нуля)
3) Для указания способа вспомогательного логирования величин. Например, у меня есть некоторый объект someType, как его залогировать в лог, чтобы программист в случае, если код покрыт, но работает неверно, смог понять, собственно, что было передано в метод?

VD>2. Код этот можно добавлять прямо в исходные типы. Так как он статический, он не будет сильно мешать.


VD>Что касается гибкости вывода информации о покрытии, то этого можно добиться дав возможность задать имя функции используемой для выводи информации о непокрытых частях кода (в моем примере используется WriteLine).


И что с этим именем дальше делать при получении эталона?
У тебя эталон прост: 0001111
А в реале эталон — это перебор флагов: в это вошли, в это нет, в это не вошли, в это и не должны были и т.п.
Re[7]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 19:07
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>на этапе компиляции макрос сгенерирует следующие трассы (возможные потоки исполнения)

FDS>Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).

Невнимательно прочитал в прошлый раз...

Сейчас перечитал и понял, что это какая-то ерунда. Зачем собирать информацию о этих трассах, если можно просто собирать информацию о том какие из блоков кода не были посещены?

Просто, если тесты покроют 100% кода метода, то все эти трассы однозначно будут покрыты. Если же часть кода не будет покрыта, то и часть этих трасс не будет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Отступление в Unitesk
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я не вижу смысла заниматься каким-то там логированием во время компиляции. Твоя задач собрать информацию о том какие из участков кода не были посещены? Ну, так и решай эту задачу, а не какую-то другую.


VD>Здесь
Автор: VladD2
Дата: 22.08.11
я описал вариант решения твой задачи. Он прост, эффективен и не требует какой-либо алхимии.


Ответил там (есть разные критерии покрытия — твой не годится).

VD>А от тебя я просил чего-то вроде примеров что я привел здесь
Автор: VladD2
Дата: 22.08.11
.


VD>Ну, это что-то очень абстрактное. Что ни понимаю под формальной моделью я вообще не понимаю. В общем, случае это какая-то фигня. Модель можно придумать только для конкретной задачи и далеко не для всех случаев.


VD>В общем, это опять не то. Ты же собрался решать конкретную задачу? Вот ее и надо было описать. Покрытие, так покрытие.


У них это решается в конкретном виде, извини, не посмотрел внимательно по ссылкам.

Дело в том, что мы берём код нашего метода, и, грубо говоря, по нему делаем формальную модель просто исключая всё, кроме условий:

if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

// это спец. ключевое слово, описывающее нечто в коде модели, что не может быть из неё извлечено
tautology условие2 ^ условие3;

when (условие3)
  ...


Очень грубо, формальная модель в Unitesk — это код, который описывает такие условия, а затем инструмент тестирования говорит мне, какие участки кода мне нужно покрыть с учётом указанной тавтологии и комбинаторного покрытия кода.

Т.е. он мне скажет, что я должен пройти тестами по следующим трассам

условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

не условие1

Но, с учётом тавтологии, если я зашёл в условие2, то условия условие3 уже быть не может и наоборот, но при этом одно из этих условий должно быть, поэтому он мне скажет, что я должен покрыть следующие трассы

условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

не условие1

Вот это он должен описать в некотором файле, чтобы затем я мог по логам покрытия тестами, сравнить, покрыты все эти условия или нет.
Re[9]: Исправление ошибки
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:22
Оценка:
FDS>не условие1
Это тоже должно быть покрыто
Re[8]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:23
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Просто, если тесты покроют 100% кода метода, то все эти трассы однозначно будут покрыты. Если же часть кода не будет покрыта, то и часть этих трасс не будет.


Это неверно. Если тесты покроют 100% кода метода, то это значит, что будут покрыты по крайней мере по одной трассе, содержащей соотв. условия. Но вот все или не все — неизвестно.

Пример
if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

// это спец. ключевое слово, описывающее нечто в коде модели, что не может быть из неё извлечено
tautology условие2 ^ условие3;

when (условие3)
  ...


Трассы к покрытию:
условие1
условие2

условие1
условие3

не условие1
условие3

не условие1

Допустим, у нас в одном тесте верны условия условие1 и условие2, а в другом — не условие1 и условие3

Получаем, что в первом тесте мы покрыли код условия1 и условия2, во втором код не условия1 и условия3, т.е. весь код. В то же время мы не покрыли следующие трассы
условие1
условие3

не условие1
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.