LINUX.ORG.RU

История изменений

Исправление dimgel, (текущая версия) :

А вот теперь продолжим срач! =) Я тут повспоминал, что я делал на макросах.

Первое большое, что вспомнилось – это статически-типизированный DSL, транслирующийся в SQL не в рантайме, а во время компиляции макросами. На скале: и сам делал, и в опенсорсе лежит как минимум одно решение. Но потом вспомнил, что для плюсов есть sqlpp, который тоже транслирует в compile-time. Не помню уже, по какой причине я его забраковал (что-то там было то ли дюже громоздко, то ли дюже коряво), но не суть; будем считать что аналог есть.

А потом вспомнил целый огроменный пласт, которому аналогов на плюсовом «МП» не сделать в приниципе: AOP. Потому что тут переписывется тело метода. На жаве (JEE, Spring, Hibernate) оно интенсивно используется для заворачивания функций в обёртки, управляющие транзакциями, логированием, ленивой загрузкой при обращению к полю сущности и т.п. – в рантайме. В результате серверные жава-приложения стартуют медленно, и стек-трейсы исключений у них неприлично огромные (на каждую спринг-обёртку приходится несколько вызовов).

Так вот, доводилось мне делать свою lombok-аннотацию @Transactional, обрабатываемую соответственно в compile-time. Просто потому что мне нужно было протаскивать доп. параметр, который в стандартных @Transactional отсутствовал.

Жавовские фреймворки в рантайме стандратно создают прокси-класс с такими же сигнатурами методов, что и у оборачиваемого класса; его прокси-методы выполняют требуемые действия и делегируют к методам проксируемого класса.

А на макросах в compile-time я переписываю тело метода (в lombok это AST-узел Block, содержащий список statements, т.е. «{ … }»), помещая этот Block внутрь собственного кода. Совсем грубо, было:

// Наличие lombok-аннотации -- триггер, запускающий мою обработку.
// В скале есть макро-аннотации и макро-функции.
@Transactional
R f(A a, B b, C c) {
   ab = a + b;
   return ab - c;
}

стало:

R f(A a, B b, C c) {     // Сигнатуру не трогаю.
   startTransaction();
   try {                 // Исходное тело метода {...} не трогаю.
      ab = a + b;
      return ab - c;
   } catch (Exception e) {
      rollback();
      throw e;
   } finally {
      commit();
   }
}

Этот пример слишком схематичный и корявый: тут муть с commit() после rollback(). В реале мне, чтобы вообще не касаться и сигнатуры, ни тела метода (не анализировать, не переписывать, в т.ч. не подсовывать commit() перед каждым return), пришлось делать два вложенных try.

UPD. И ещё у меня там был цикл по попыткам при дедлоках.

Ну так вот: ЭТО – метапрограммирование. А в плюсах – пое@$#ь.

Исходная версия dimgel, :

А вот теперь продолжим срач! =) Я тут повспоминал, что я делал на макросах.

Первое большое, что вспомнилось – это статически-типизированный DSL, транслирующийся в SQL не в рантайме, а во время компиляции макросами. На скале: и сам делал, и в опенсорсе лежит как минимум одно решение. Но потом вспомнил, что для плюсов есть sqlpp, который тоже транслирует в compile-time. Не помню уже, по какой причине я его забраковал (что-то там было то ли дюже громоздко, то ли дюже коряво), но не суть; будем считать что аналог есть.

А потом вспомнил целый огроменный пласт, которому аналогов на плюсовом «МП» не сделать в приниципе: AOP. Потому что тут переписывется тело метода. На жаве (JEE, Spring, Hibernate) оно интенсивно используется для заворачивания функций в обёртки, управляющие транзакциями, логированием, ленивой загрузкой при обращению к полю сущности и т.п. – в рантайме. В результате серверные жава-приложения стартуют медленно, и стек-трейсы исключений у них неприлично огромные (на каждую спринг-обёртку приходится несколько вызовов).

Так вот, доводилось мне делать свою lombok-аннотацию @Transactional, обрабатываемую соответственно в compile-time. Просто потому что мне нужно было протаскивать доп. параметр, который в стандартных @Transactional отсутствовал.

Жавовские фреймворки в рантайме стандратно создают прокси-класс с такими же сигнатурами методов, что и у оборачиваемого класса; его прокси-методы выполняют требуемые действия и делегируют к методам проксируемого класса.

А на макросах в compile-time я переписываю тело метода (в lombok это AST-узел Block, содержащий список statements, т.е. «{ … }»), помещая этот Block внутрь собственного кода. Совсем грубо, было:

// Наличие lombok-аннотации -- триггер, запускающий мою обработку.
// В скале есть макро-аннотации и макро-функции.
@Transactional
R f(A a, B b, C c) {
   ab = a + b;
   return ab - c;
}

стало:

R f(A a, B b, C c) {     // Сигнатуру не трогаю.
   startTransaction();
   try {                 // Исходное тело метода {...} не трогаю.
      ab = a + b;
      return ab - c;
   } catch (Exception e) {
      rollback();
      throw e;
   } finally {
      commit();
   }
}

Этот пример слишком схематичный и корявый: тут муть с commit() после rollback(). В реале мне, чтобы вообще не касаться и сигнатуры, ни тела метода (не анализировать, не переписывать, в т.ч. не подсовывать commit() перед каждым return), пришлось делать два вложенных try.

Ну так вот: ЭТО – метапрограммирование. А в плюсах – пое@$#ь.