Использование макросов для генерации кода
Опустившись на пару уровней, можно говорить о макросах, которые шут код во время компиляции. На протяжении всей книги мы предостерегали вас от использования макросов и условной компиляции, поскольку этот стиль программирования вызывает множество проблем, все же они все равно существуют, и иногда текстуальная подстановка – это именно то, что нужно для решения данной проблемы. Один из таких примеров – использование препроцессора C/C++ для компоновки программы с большим количеством повторяющихся частей.
Например, программа из главы 7, оценивающая скорость конструкций простейшего языка, использует препроцессор С для компоновки тестов, составляя из них последовательность стереотипных кодов. Суть теста – заключить фрагмент кода в цикл, который запускает таймер, выполняет этот фрагмент много раз, останавливает таймер и сообщает о результатах. Весь повторяющийся код заключен в пару макросов, а измеряемый код передается в качестве аргумента.
Первичный макрос имеет такой вид:
Обратная косая черта (\) позволяет записывать тело макроса в нескольких строках. Этот макрос используется в "операторах", которые имеют такой вид:
Для инициализации иногда применяются и другие операторы, но основная часть, производящая замеры, представлена в этих одноаргументных фрагментах, которые преобразуются в значительный объем кода.
Иногда макросы могут использоваться и для генерации нормального коммерческого кода. Барт Локанти (Bart Locanthi) однажды написал эффективную версию оператора двумерной графики. Этот оператор, называемый bitblt, или rasterop, трудно было сделать быстрым, поскольку он использовал большое количество аргументов, которые могли комбинироваться самыми хитрыми способами.
Проведя тщательный разбор вариантов, Локанти уменьшил комбинации до независимых циклов, которые можно было оптимизировать по отдельности. Затем каждый случай был воссоздан с помощью макроподстановки, аналогичной той, что показана в примере на тестирование производительности, и все варианты были перебраны в одном большом выражении switch. Оригинальный исходный код представлял две-три сотни строк, после выполнения макроподстановок он разрастался до многих тысяч строк. Этот конечный код был не самым оптимальным, но, учитывая сложность задачи, весьма практичным и простым в написании. И кстати, как и весь код самого высокого уровня, неплохо переносимым.
Упражнение 9.16
В упражнении 7.7 вам предлагалось написать программу, оценивающую траты на различные операции в C++. Используя идеи, изложенные в последнем параграфе, попробуйте написать новую версию этой программы.
Упражнение 9.17
В упражнении 7.8 надо было построить модель оценки затрат для tva, а в этом языке нет макросов. Попробуйте решить эту проблему, написав другую программу – на любом другом языке (или языках), которая создавала бы Java-версию и автоматизировала бы запуск тестов на производительность.