Как я могу перегрузить оператор '=' в Ada, не создавая рекурсивную функцию? - PullRequest
4 голосов
/ 22 мая 2011
FUNCTION "=" (lString1, lString2 : IN lString) RETURN boolean IS


     IF lString1 = NULL AND lString2 = NULL THEN 
        RETURN true;
      ELSIF lString1 = NULL OR lString2 = NULL THEN
        RETURN false;
      END IF;

Я пытаюсь перегрузить оператор равенства в Аде.Каждый раз, когда я использую оператор '=' внутри функции, он вызывает рекурсию, которая приводит к переполнению стека, вместо того, чтобы использовать нужный мне ada-оператор.Есть ли способ отличить его от моего перегруженного оператора?

Ответы [ 4 ]

4 голосов
/ 23 мая 2011

Внедряя не перегруженную функцию утилиты для сравнения типов доступа, можно заставить работать определение функции OP с необходимыми исправлениями синтаксиса и изменить его для использования функции утилиты.

Я все еще озадачен тем, почему вызов "=" в качестве стандарта. "=" Отклонен компилятором (GNAT) для указания "несовместимых аргументов".

with Text_IO; use Text_IO;

procedure non_recursive_equals is

   type Lstring is access String;

   -- Be aware, the ordering of the functions here is important!
   function Is_Equal(Lstring1, Lstring2 : in Lstring) return Boolean is
   begin
      return Lstring1 = Lstring2;
   end Is_Equal;

   function "=" (lString1, lString2 : in Lstring) return Boolean is
   begin
      if Is_Equal(LString1, null) and Is_Equal(LString2, null) then
         return True;
      elsif Is_Equal(LString1, null) or Is_Equal(LString2, null) then
         return False;
      end if;
      return False;
   end "=";

   L1, L2 : Lstring := null;

begin
   Put_Line("L1 and L2 null: " & Boolean'Image(L1 = L2));
   L2 := new String(1..10);
   Put_Line("L2 not null   : " & Boolean'Image(L1 = L2));
end non_recursive_equals;

Edit:

Вот еще один способ, используя предложение переименования:

with Text_IO; use Text_IO;

procedure non_recursive_equals is

   type Lstring is access String;

   function Is_Equal (lString1, lString2 : in Lstring) return Boolean is
   begin
      if lString1 = null and lString2 = null then
         return True;
      elsif lString1 = null or lString2 = null then
         return False;
      end if;
      return False;
   end Is_Equal;

   function "=" (Lstring1, Lstring2 : in Lstring) return Boolean renames
     Is_Equal;

   L1, L2 : Lstring := null;

begin
   Put_Line ("L1 and L2 null: " & Boolean'Image (L1 = L2));
   L2 := new String (1 .. 10);
   Put_Line ("L2 not null   : " & Boolean'Image (L1 = L2));
end non_recursive_equals;
3 голосов
/ 25 мая 2011

Вот еще один способ, использующий только Ada83 ... и ужасный пример / злоупотребление исключениями:

  Type LString is Access String;

  Function "=" (Left, Right: IN LString) Return Boolean is
     Subtype Constrained_LString is Not Null LString;

     Function Is_Equal( Left : LString; Right : String ) Return Boolean is
     begin
        Return Right = Left.All;
     exception
        When CONSTRAINT_ERROR => Return False;
     end Is_Equal;

  Begin
     Return Is_Equal(Left, Right.All);
  Exception
     When CONSTRAINT_ERROR =>
        begin
           Return Is_Equal(Right,Left.All);
        Exception
           When CONSTRAINT_ERROR => Return True;
        end;
  End "=";

Что происходит, если он вызывается и Right = Null, попытка де-ссылки на неговызывает исключение;в этом случае мы пытаемся отменить ссылку на Left, и если это тоже не получается, то оба должны иметь значение Null.В случае неудачи только одного равенство должно быть ложным, а в случае, когда оба параметра могут быть разыменованы, результатом является проверка на равенство в этих строках.

2 голосов
/ 23 мая 2011

Мне удалось воспроизвести то же поведение с похожим кодом. Я позволил себе предположить, что lString это какая-то строка access type

Я полагаю, что рекурсия вызвана тем фактом, что ваша новая функция = маскирует изначально предоставленную функцию. Поскольку они имеют одно и то же имя, параметры и возвращаемое значение, простого способа их различения не существует.

Не элегантный способ обойти это - полностью избежать перегрузки и определить новую функцию с тем же поведением, что и у вашей перегруженной функции, с другим именем, например Is_Equal.

1 голос
/ 23 мая 2011

Я не уверен, почему "=" используется рекурсивно;возможно, присутствует несчастный пункт use.Приведенный ниже пример перегружает "=" и выдает следующий вывод.Перегруженная функция неявно вызывает Standard."=" для сравнения.Обратите внимание, что вы можете указать renames, чтобы упростить имена пакетов, и вы можете use type, чтобы показать только операторы, применимые к типу.

Добавление: я добавил альтернативный способinvoke Standard."=" в комментарии ниже.

Консоль:

********************
********************
TRUE
TRUE

Код:

with Ada.Strings.Bounded;
with Ada.Strings.Unbounded;
with Ada.Text_IO;

procedure UseType is
   package String20 is new Ada.Strings.Bounded.Generic_Bounded_Length(20);
   use type String20.Bounded_String;

   package StringN renames Ada.Strings.Unbounded;
   use type StringN.Unbounded_String;

   function "=" (Left  : String20.Bounded_String;
                 Right : StringN.Unbounded_String) return Boolean is
   begin
      return String20.To_String(Left) = StringN.To_String(Right);
      -- return Standard."="(String20.To_String(Left), StringN.To_String(Right));
   end "=";

   SB : constant String20.Bounded_String := 20 * '*';
   SN : constant StringN.Unbounded_String := 20 * '*';

begin
   Ada.Text_IO.Put_Line(String20.To_String(SB));
   Ada.Text_IO.Put_Line(StringN.To_String(SN));
   Ada.Text_IO.Put_Line(Boolean'Image(SB = SN)); -- infix operator
   Ada.Text_IO.Put_Line(Boolean'Image("="(SB, SN))); -- named operator
end UseType;
...