Показаны сообщения с ярлыком type. Показать все сообщения
Показаны сообщения с ярлыком type. Показать все сообщения

четверг, 20 июня 2019 г.

Ловля CX_SY_CONVERSION_OVERFLOW

Поймал странное (на мой взгляд) поведение кода при ловле CX_SY_CONVERSION_OVERFLOW.
Допустим есть метод, который принимает строку и возвращает из нее число. Тип возвращаемого числа DECFLOAT34, чтобы влезло как можно больше. Результат метода присваивается переменной имеющей меньшую разрядность. В случае, когда итоговый результат превышает максимально возможный для этого типа переменной, должно выбрасываться CX_SY_CONVERSION_OVERFLOW. Это вроде как все учтено.
При присвоении результата вызова метода напрямую в переменную CX_SY_CONVERSION_OVERFLOW не ловится и лезет дамп.
Но, если переписать присвоение через промежуточную переменную, CX_SY_CONVERSION_OVERFLOW ловится, дампа нет.
В чем прикол?

REPORT ztest_overflow.
DATA gv_value TYPE string VALUE '123456789123456'.
DATA gv_wrbtr TYPE wrbtr_d.

CLASS lcl_test DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS convert
      IMPORTING iv_value        TYPE clike
      RETURNING VALUE(rv_value) TYPE decfloat34
      RAISING   cx_static_check.
ENDCLASS.

CLASS lcl_test IMPLEMENTATION.
  METHOD convert.
    DATA(lv_value) = iv_value.
    DO 2 TIMES.
      TRY .
          rv_value = lv_value.
          EXIT.

        CATCH cx_root INTO DATA(lx_error).
          IF sy-index = 1.
            TRANSLATE lv_value USING ',..,'.
          ELSE.
            RAISE EXCEPTION TYPE cx_sral.
          ENDIF.
      ENDTRY.
    ENDDO.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  BREAK-POINT.
  TRY .
      DATA(lv_value) = lcl_test=>convert( gv_value ).
      gv_wrbtr = lv_value.
    CATCH cx_root.
      WRITE 'Overflow'. NEW-LINE.
  ENDTRY.


  TRY .
      gv_wrbtr = lcl_test=>convert( gv_value ). "!!!!DUMP
    CATCH cx_root.
      WRITE 'Overflow'. NEW-LINE.
  ENDTRY.

суббота, 26 мая 2012 г.

Строковые литералы и символьные литералы. Пробел

Хотя ABAP позволяет практически незаметно и безболезненно конвертировать одни типы  данных в другие, а типы  STRING и C вообще считает чуть ли не синонимами, все таки бывают ситуации, когда в их поведении есть отличия. Одним из таких отличий является то, как типы string  и char  воспринимают пробел.

На само деле С (char) - это всего 1 символ, так что правильнее говорить об элементе данных  большей длины, но с внутренним типом C - например CHAR10. Фактически CHAR10 - это "статический" массив из 10 символов типа C, в тоже время STRING - это "динамический" массив ( то есть с переменной длиной) символов типа C.

Рассмотрим следующий код:
DATA lv_char TYPE char10 VALUE '12345'.
DATA lv_str TYPE string VALUE '12345'.

Здесь переменная lv_char хранит значение, равное '12345     '- то есть символы от 1 до 5 и еще 5 пробелов, потому что у нее фиксированная длина 10 символов.  Переменная lv_str хранит только символы '12345'.

А теперь следующий код:
DATA lv_char TYPE char10 VALUE  space.
DATA lv_str TYPE string VALUE space.


space - это заменитель ' '.
Здесь переменная lv_char хранит значение, равное 10 пробелам.  Переменная lv_str хранит '', то есть ничего. Не один пробел, а именно ничего. Длина строки = 0.

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

Рассмотрим пример, в котором проявляются особенности переменных типа CHAR* и сжатия пробелов.
Допустим у нас извне приходит некое значение в переменной lv_char типа CHAR10 - пользователь что-то вводит, IDOC пришел - что угодно. Нам нужно проверить, что это значение содержит только цифры. Если это не так, выдавать ошибку, писать логи - что угодно. Не суть важно.
Как это можно проверить? Например, так:
IF lv_char CO '0123456789'.
  "только цифры.
ELSE.
  "что-то еще
ENDIF.

Теперь допустим  нам приходит в переменной lv_char число 42. Но после проверки мы попадаем в ветку ELSE. Удивительно? На первый взгляд да - ведь в переменной только цифры 4 и 2, проверка должна проходить. Но потом становится понятно, что на самом деле не только 4 и 2, а еще и 8 пробелов. Ладно, меняем условие на такое, что в переменной допускаются цифры и пробелы:
IF lv_char CO '0123456789 '. "После 9 стоит пробел

Но программа  все равно упорно лезет в ветку ELSE. Ну поскольку про сжатие пробелов Вы уже в курсе, то можете догадаться, что наш трюк с добавлением пробела не сработал, потому что компилятор его все равно выкинул и в итоге в скомпилированном коде у нас все также проверка на 1-9. О пробелах ни слова, ни байта.

Ну это все замечательно, но что делать? Вариантов несколько:

  1. Поставить пробел не в конце, а, например, посередине:
    IF lv_char CO '01234 56789'"После 4 стоит пробел. 
    Поможет в данном случае, поскольку пробелы нам фактически не нужны - мы просто обходим особенность CHAR, но могут быть случаи, когда пробел ключевая фигура - ниже я приведу пример.
  2. Помещать lv_char предварительно в переменную типа string. Это также прокатит, если вам фактически пробел не нужен. Но если нужен - то нет. Причем в отличии от п.1 это еще и лишняя переменная, лишнее присвоение, да и лишняя писанина.
  3. Использовать не символьный литерал, а строковый. Строковый в коде задается набором символов ограниченным апострофами (на русской клавиатуре он обычно на кнопке с буквой Ё). Напомню, символьный ограничивается одинарной кавычкой (на кнопке с Э). В строковых литералах пробелы НЕ сжимаются - что ввели, то и будет в коде. То есть, в нашем примере код может быть таким:
    IF lv_char CO  `0123456789 `"После 9 стоит пробел
Теперь обещанный пример, когда пробел ключевая фигура. Задача найти первую позицию с пробелом в строке . Пишем например так:
FIND ' ' IN '12345 Text' MATCH OFFSET lv_offset MATCH LENGTH lv_length.
В результате в lv_offset у нас будет 0, а не ожидаемое 5. Почему? Все потому же - ' ' сжался до пустого места, а пустое место у нас где угодно, а первое пустое место в начале строки. Оба первых пункта из решения выше нам не подойдут здесь, только 3ий:
FIND ` ` IN '12345 Text' MATCH OFFSET lv_offset MATCH LENGTH lv_length.