Поделиться через


Макросы в X++

Замечание

Группы интересов сообщества теперь переехали из Yammer в Microsoft Viva Engage. Чтобы присоединиться к сообществу Viva Engage и принять участие в последних обсуждениях, заполните форму " Запрос доступа к финансам и операциям Viva Engage Community " и выберите сообщество, к которому вы хотите присоединиться.

В этой статье описывается создание и использование макросов в X++.

Директивы precompiler, то есть макросы, обрабатываются концептуально перед компиляцией кода. Директивы объявляют и обрабатывают макросы и их значения. Директивы заменяются указанным содержимым, поэтому компилятор никогда не встречает их. Компилятор X++ видит только последовательность символов, записанных в код X++ директивами.

Предупреждение

Макросы являются устаревшими функциями и могут быть устаревшими в будущих выпусках. Вместо этого используйте конструкции языка: вместо макросов используйте такие конструкции языка:

  • Константы
  • Sysda для запросов.

Определение макросов

Вы определяете именованный макрос с помощью синтаксиса, показанного ниже.

  • #define. MyMacro(Value) создает макрос с необязательным значением.
  • #if. MyMacro проверяет, определен ли макрос.
  • #undef. MyMacro удаляет определение макроса.

директивы #define и #if

Все директивы и символы предварительной компиляции начинаются с символа # .

Определите макрос со следующим синтаксисом:

#define.MyMacro(Value) // creates a macro with a value.
#define.AnotherMacro() // creates a macro without a value.

Макрос можно определить в любом месте кода. Макрос может иметь значение, которое является последовательностью символов, но не требуется иметь значение. Директива #define предписывает прекомпилатору создать переменную макроса, при необходимости включив значение.

Директива #if проверяет, определена ли переменная и, при необходимости, имеет ли она определенное значение, как показано в следующем примере:

#if.MyMacro
  // The MyNaacro is defined.
#endif

#ifnot.MyMacro
  // The MyNaacro is not defined.
#endif

Директивы предварительной компиляции X++, имена макросов, которые они определяют, и #if тесты значений директивы не учитывает регистр. Однако определите имена макросов, начинающиеся с прописной буквы.

Директива #undef

Используйте директиву #undef , чтобы удалить определение макроса, существующее из предыдущего #define.

#undef.MyMacro
#if.MyMacro
   // The macro is not defined, so this is not included
#endif

Вы можете переопределить имя макроса, которое вы удалили с #undef помощью другого #define.

Использование значения макроса

Можно определить имя макроса для получения значения.

#define.Offset(42)
...
print #Offset; // 42

Значение макроса не имеет определенного типа данных — это всего лишь последовательность символов. Назначьте значение макросу, указав значение, заключенное в скобки в конце #define.MyMacro директивы. Используйте символ макроса, где бы вы ни хотели, чтобы значение выполнялось в коде X++. Символ макроса — это имя макроса с символом, # добавленным в качестве префикса. В следующем примере кода показан символ макроса #MyMacro. Символ заменяется значением макроса.

Проверка значения макроса

Вы можете проверить макрос, чтобы определить, имеет ли он значение. Вы также можете определить, равно ли его значение определенной последовательности символов. Эти тесты позволяют условно включать строки кода в программу X++. Невозможно проверить, имеет ли определенный макрос значение. Можно проверить, соответствует ли значение макроса определенному значению. Рекомендуется всегда определять значение для любого определяемого имени макроса или никогда не определять значение. При переключениях между этими режимами код становится трудно понять.

директивы #defInc и #defDec

#defInc и #defDec являются единственными директивами, которые интерпретируют значение макроса. Они применяются только к макросам, имеющим значение, которое прекомпилатор может преобразовать в формальный тип int . Эти директивы изменяют числовое значение макроса во время компиляции. Значение может содержать только числа. Единственным нечисленным символом является ведущий отрицательный знак (-). Целочисленное значение рассматривается как int X++, а не как int64. Для имен макросов, которые #defInc использует директива, директива, создающая макрос, #define не должна находиться в объявлении класса. Поведение в этих случаях #defInc непредсказуемо. Вместо этого определите такие макросы только в методе. #defInc Используйте директивы только #defDec для макросов, имеющих целочисленное значение. Прекомпилатор следует специальным правилам, #defInc если значение макроса не является целым числом, или когда значение является необычным или экстремальным. В следующей таблице перечислены значения, которые #defInc преобразуются в ноль (0), а затем увеличиваются. При #defInc преобразовании значения в 0 не удается восстановить исходное значение, даже не используя #defDec.

Значение макроса Значение defInc Поведение
(+55) 56 Положительный знак (+) префикс делает прекомпилатор рассматривать это значение как нечисловую строку. Прекомпилатор обрабатывает все нечисловые строки как 0 при обработке #defInc директивы (или #defDec) .
("3") 1 Целые числа, заключенные в кавычки, обрабатываются как 0.
( ) 1 Строка пробелов рассматривается как 0.
() 1 Строка нулевой длины обрабатывается как 0.
(Случайная строка.) 1 Любая нечисленная строка символов обрабатывается как 0.
(0x12) 1 Шестнадцатеричные числа рассматриваются как нечисловые строки. Поэтому прекомпилатор преобразует их в 0.
(-44) -43 Отрицательные числа допустимы.
(2147483647) -2147483648 Максимальное положительное значение инта переполняется до минимального отрицательного значения int.#defInc
(999888777666555) 1 Любое большое число, за пределами емкости int и int64.
(5.8) 1 Реальные числа интерпретируются как 0.
1 Если для директивы #define.MyValuelessMacro не указано значение и скобки, значение равно 0.

директива #globaldefine

Директива #globaldefine аналогична директиве #define . Используйте #define вместо #globaldefine.

директивы #localmacro и #macro

Директива #localmacro является хорошим выбором, если вы хотите, чтобы макрос содержал значение, которое имеет несколько строк длиной, или когда значение макроса содержит закрывающую скобку, что делает их хорошими кандидатами для хранения фрагментов исходного кода.

    #macro.RetailMatchedAggregatedSalesLine(
                %1.price            == %2.price
        &&      %1.businessDate     == %2.businessDate
        &&      %1.itemId           == %2.itemId
        &&      ((((%3) && (%1.qty <= 0)) || ((! %3) && (%1.qty > 0))) || (%4))
    )
    #endmacro

Директива #localmacro может быть записана как #macro. #localmacro Однако рекомендуется использовать этот термин. С помощью #if директивы можно проверить, объявляется ли имя макроса с помощью директивы #define . Однако нельзя проверить, объявляется ли имя макроса с помощью директивы #localmacro . Директива влияет #define только на макросы, объявленные с помощью #undef директивы. В директиве #define можно указать имя, которое уже находится в области в качестве #localmacro. Эффект заключается в том, чтобы отменить #localmacro и создать #define макрос. Это также относится к противоположной последовательности, что означает, что #localmacro может переопределить a #define. A #localmacro (с именем макроса и значением) всегда переопределяет предыдущее #localmacro имя с одинаковым именем. Эта же проблема возникает с #globaldefine. Основное различие между макросом и #define макросом заключается в том#localmacro, как их синтаксис завершается. Терминаторы приведены следующим образом:

  • #define — завершается с помощью: )
  • #localmacro — завершается с помощью: #endmacro

#localmacro лучше выбрать макросы с несколькими значениями строк. Как правило, несколько строк являются строками кода X++ или SQL. X++ и SQL содержат много круглых скобок, и они преждевременно завершают #define. И то #define , и #localmacro другое можно объявить и завершить в одной строке или в последующих строках. На практике он #define завершается в той же строке, на которую она объявлена. На практике он #localmacro завершается на следующей строке.

Параметры макроса

Можно определить значения макросов для включения символов параметров. Первый символ параметра : %1второй — %2и т. д. Значения параметров передаются при ссылке на имя символа макроса для расширения. Значения параметров макроса — это символьные последовательности без формального типа, и они разделены запятыми. Нет способа передать запятую в рамках значения параметра. Число переданных параметров может быть меньше, чем, больше или равно числу параметров, которые предназначено для получения значения макроса. Система допускает несоответствия в количестве переданных параметров. Если меньше параметров передаются, чем ожидает макрос, каждый опущенный параметр обрабатывается как последовательность символов нулевой длины.

Вложенные символы макросов

Директивы определения предварительной компиляции можно вложить в директиву внешнего определения. Основными директивами определения являются #define и #localmacro.

Директива #define может быть задана внутри #localmacro директивы, а #localmacro также может находиться внутри #define.

директива #macrolib

В обозревателе приложений под узлом макросов под узлом кода существует множество узлов библиотеки, содержащих наборы директив макросов. Оба #define и #localmacro часто отображаются в содержимом этих библиотек макросов. Вы можете использовать #macrolib. MyAOTMacroLibrary , чтобы включить содержимое библиотеки макросов в код X++. #if Директивы #undef не применяются к #macrolib именам. Однако они применяются к #define директивам, которые являются содержимым #macrolib макроса. Директива #macrolib. MyAOTMacroLibrary также можно записать как #MyAOTMacroLibrary.#macrolib Префикс рекомендуется, так как это никогда не неоднозначно для человека, который позже считывает код.

Директива #linenumber

Директиву #linenumber можно использовать во время разработки и отладки кода. Он заменяется физическим номером строки в файле кода до любого расширения макроса.

Область макроса

Диапазон, в котором можно ссылаться на макрос, зависит от того, где определяется макрос. В классе можно ссылаться на макросы, определенные в родительском классе. Когда прекомпилатор обрабатывает дочерний класс, он сначала трассирует цепочку наследования к корневому классу. Затем прекомпилатор обрабатывает все директивы из корневого класса в скомпилированный класс. Он сохраняет все макросы и их значения во внутренних таблицах. Результаты директив в каждом объявлении класса применяются к внутренним таблицам, которые уже заполнены директивами, найденными ранее в цепочке наследования.

Однако прекомпилатор обрабатывает каждый метод отдельно. Он обновляет свои внутренние таблицы, чтобы он смог восстановить состояние таблиц, как они были до обработки текущего метода. После того как прекомпилатор обрабатывает первый метод, он восстанавливает внутренние таблицы перед обработкой следующего метода.

В этом контексте метод определяется как содержимое узла метода в дереве объектов приложения (AOT). В AOT можно развернуть узел "Классы", развернуть узел класса, щелкнуть правой кнопкой мыши узел метода и выбрать пункт "Изменить". Затем можно добавить строку #define.MyMacro("abc") перед объявлением метода. Прекомпилатор обрабатывает эту #define директиву как часть метода, даже если #define происходит вне {} блока метода.