Pull to refresh

Comments 17

Компилятор классов использует определение класса для генерации MAC кода
На самом деле, компилятор классов генерирует INT-код.

Стоило бы упомянуть и о том, что команда $$$Многострочного_Макроса должна быть единственной в строке.
В остальном — неплохое введение в макросы, спасибо!
>Стоило бы упомянуть и о том, что команда $$$Многострочного_Макроса должна быть единственной в строке.

ClassMethod Test6()
{
    
#define HW Write "Hello, World!"
    
#define Five 5
    #define 
WriteLn(%str,%cnt) for ##unique(new)=1:1:%cnt { ##Continue
        $$$HW,! ##Continue
         Write %str,! ##Continue
    }
     
    $$$HW $$$WriteLn
("Hello, World!",$$$Five$$$HW
}

Компилируется в:

zTest6(public {
    Write "Hello, World!" for %mmmu1=1:1:5 { 
        Write "Hello, World!",
         Write "Hello, World!",
    } Write "Hello, World!" }

И в терминале выводит:
Hello, World!Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Так можно, потому что фигурные скобки инвариантны по отношению к концам строк.
Если же в макроопределении (или при вызове макроса) используются традиционные блоки do с точками, то нельзя. Вот
простой пример:
Test7
#define WR(%arg) Write «Result is „_%arg,!
#define FindStr(%ref,%ind,%str,%result) s %ind=“» for { ##Continue
set %ind=$o(@%ref@(%ind)) ##Continue
if %ind="" s %result=0 quit ##Continue
if $f(@%ref@(%ind),%str) s %result=1 quit ##Continue
} ##Continue

#define FindStr1(%ref,%ind,%str,%result) s %ind="",%result=-1 for do quit:%result'<0 ##Continue
. set %ind=$o(@%ref@(%ind)) ##Continue
. if %ind="" s %result=0 quit ##Continue
. if $f(@%ref@(%ind),%str) s %result=1 quit ##Continue

if 1 { $$$FindStr1(«a»,i,«jo»,result) $$$WR(result) } else { w «nop»,! } // так нормально

#if 0 // если поменять 0 на 1, будет ошибка компиляции
if 1 do // some condition
. set a(1)=«11»,a(2)=«joker»
. $$$FindStr(«a»,i,«jo»,result)
. $$$WR(result)

#else // ошибки компиляции нет, однако имеем бесконечный цикл
if 1 do // some condition
. set a(1)=«11»,a(2)=«joker»
. $$$FindStr1(«a»,i,«jo»,result)
. $$$WR(result)

#endif

Если писать макросы только для себя, и никогда не использовать блоки do, то можно этим пренебречь. Если же пишется макрос общего назначения, то лучше об этом помнить и рекомендовать его к применению только в виде единственной команды в строке.
Генерировать в Caché блоки «традиционные» блоки do с точками — не могу представить ни одного случая, зачем это может понадобиться. Синтаксис с фигурными скобками как раз и пришел на смену точек, чтобы устранить не писать такие потенциально ошибочные, затрудняющие чтение и наполненные лишними точками места.
Как видно, вы не можете представить себе проекта с тоннами унаследованного кода, куда, тем не менее, продолжает добавляться новый функционал )))
Если уж на то пошло, гораздо труднее представить себе ситуацию, когда многострочные макросы привносят что-то полезное, недостижимое при использовании обычных вызовов функций (методов класса), А ограничений и дополнительных затруднений немало. Попробуйте, например, вернуть значение из многострочного макроса (как его может вернуть однострочный макрос-выражение).
>Если уж на то пошло, гораздо труднее представить себе ситуацию, когда многострочные макросы привносят что-то полезное, >недостижимое при использовании обычных вызовов функций (методов класса),
Они упрощают чтение INT кода.

> А ограничений и дополнительных затруднений немало.
Какие, кроме проблем с точками?

>Попробуйте, например, вернуть значение из многострочного макроса (как его может вернуть однострочный макрос-выражение).
Пример 3 оперирует со значением, возвращаемым многострочным макросом.
Они упрощают чтение INT кода.
При «нормальном» программировании читать INT-код практически никогда не надо. А когда используешь макросы, то таки-да ))) По сути, это и есть наиболее важная причина, по которой я более не являюсь сторонником макросов. Время программиста дороже, чем сэкономленные милли- (или уже микро- ?) секунды на скорости выполнения при замене вызова подпрограммы вызовом макроса.
Пример 3 оперирует со значением, возвращаемым многострочным макросом.
Понятно, что через переменную вернуть можно (нарушая все правила приличия ))). Не увидел в вашем примере конструкций вида: set result=$$$SomeMacro(...)
Я являюсь сторонником макросов, так как после однократного выверения макроса с использованием INT кода, CLS код становится понятней, и на INT больше смотреть не надо. Кроме того вызов функций при компиляции может ощутимо уменьшить нагрузку в рантайме. Подробнее об этом я расскажу в следующей статье.

>Не увидел в вашем примере конструкций вида: set result=$$$SomeMacro(...)
Например:
ClassMethod Test8()
{
    
#define SomeMacro(%name) "Hello "_ ##Continue
    %name
    set 
result=$$$SomeMacro("World")
}
#define SomeMacro(%name) «Hello „_ ##Continue
%name
Это наверное шутка — оценил ))) Имелась в виду функционально оправданная многострочность, а не одно разбитое на несколько строк выражение.
Думаю, спорить дальше не стоит, у каждого свой опыт и свои предпочтения. Мне когда-то приходилось кодить на ассемблере для компов нескольких архитектур, на этом уровне макросы действительно большое подспорье, а в ЯВУ, в большинстве случаев, скорее анахронизм и источник ненужных проблем. Мы читаем не тот код, который выполняется — вот главная из них.
Как видно, вы не можете представить себе проекта с тоннами унаследованного кода, куда, тем не менее, продолжает добавляться новый функционал )))

Представить не сложно ) Непонятно только, зачем в новую функциональность вносить точечный синтаксис? Читабельность от этого точно не повышается, а вероятность ошибок увеличивается.
Это уже вопрос философский. Представьте, что целый комплекс программ уже написан «в точках», а тут кто-то продвинутый решит развивать его (без полного переписывания), используя скобки. Какой будет результат? Улучшится читаемость?
К тому же, корпоративный стандарт может предписывать программирование на классическом M (даже в Cache').
Несколько многострочных макросов в одной строке тоже возможно:
ClassMethod Test6()
{
    
#define HW Write "Hello, World!"
    
#define Five 5
    #define 
WriteLn(%str,%cnt) for ##unique(new)=1:1:%cnt { ##Continue
        $$$HW,! ##Continue
         Write %str,! ##Continue
    }
     
    $$$HW $$$WriteLn
("Hello, World!",$$$Five$$$HW $$$WriteLn("Hello, World!",$$$Five)
}
}

Компилируется в:

zTest6(public {
    
Write "Hello, World!" for %mmmu1=1:1:

        
Write "Hello, World!",
         
Write "Hello, World!",
    
Write "Hello, World!" for %mmmu2=1:1:
        
Write "Hello, World!",
         
Write "Hello, World!",
    
} }

И в терминале выводит
Hello, World!Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!

На самом деле, компилятор классов генерирует INT-код.
Он так же может генерировать MAC код, который в дальнейшем будет генерировать INT. Так делаете codemode = objectgenerator. И вот для кодогенераторов можно определить отдельные макросы с помощью IncludeGenerator
Это твои скрытые знания ))) По тексту статьи можно понять, что сначала генерится .MAC, а потом из него .INT. Что неправильно, ибо тот .MAC, который в objectgenerator-е, имеет отношение к генерации кода, а не к его выполнению. В общем случае .MAC не порождается, лишь в частном случае objectgenerator-а. Стоило бы это уточнить, статья ведь рассчитана на начинающих.
На самом деле, компилятор классов генерирует INT-код.

Тоже думал так, но похоже что сначала mac? Источник.
> На самом деле, компилятор классов генерирует INT-код.
Поговорил с разработчиком компилятора классов COS. Класс преобразуется в MAC-подобный набор методов, на основе которого генерируется INT код.
Sign up to leave a comment.