6

Javaにはfinalizeブロックがあり、ブロックが残った後にいくつかのステートメントを実行できます(例外が発生した場合でも実行されます)。例:

try {
  ...
} catch (Exception e) {
  ...
} finally {
  ... // any code here
}

Adaには、 Finalize操作を実装できる制御されたオブジェクトがありますが、javaのようにfinalizeブロックに相当するものはありません。これは、ログ記録、ファイルのクローズ、トランザクションなどに役立ちます(可能なブロックごとに特定のタグ付きタイプを作成する必要はありません)。

  1. (コードを読みやすくしながら)Ada 2005でそのようなfinalizeブロックをどのように実装しますか?
  2. Ada 2012には、ファイナライズコードを簡単に実行できるようにする計画がありますか?
4

6 に答える 6

2

Adrienがコメントで述べているFinalizeように、デストラクタに似ています。

例外/最終シーケンスに近いものを取得するには、これらの行に沿って何かを行うことができます(警告、コンパイルされていない、入力しただけです-エラーは一緒に解決します:-)AdaRMの例外セクションも参照してください。

with Ada.Exceptions;  use Ada.Exceptions;

procedure Do_Something is

   -- Variables and what-not...

   -- In case you have an exception and want to reraise it after you've done
   -- the 'final' processing.
   Exception_Caught : Exception_Occurrence := Null_Occurrence;

begin
   -- You can have some statements, like initializations, here that will not
   -- raise exceptions.  But you don't have to, it can all just go in the
   -- following block. However you want to do it...

   declare
      -- If you need to declare some entities local to a block, put those here.
      -- If not, just omit this declare section.  Be aware, though, that if
      -- you initialize something in here and it raises an exception, the
      -- block's exception handler will not catch it. Such an exception will
      -- propagate out of the whole procedure (unless it has an outermost
      -- exception handler) because you're _not_ in the block's scope yet.

   begin
      -- Main processing that might raise an exception

      ...

   exception
      when E : others =>
         -- Handle any exception that's raised.  If there are specific
         -- exceptions that can be raised, they should be explicitly
         -- handled prior to this catch-all 'others' one.

         -- Save the exception occurrence, i.e. make a copy of it that can
         -- be reraised in the 'Final' section if needed.  (If you want to
         -- reraise for a specific exception, do this in those handlers as
         -- well.
         Save_Occurrence(Exception_Caught, E);

   end;

   -- Final processing. Everything from here to the end of the procedure is
   -- executed regardless of whether an exception was raised in the above
   -- block.  By it including an others handler, it ensured that no exception
   -- will propagate out of this procedure without hitting this 'Final' code.

   -- If an exception was raised and needs to be propagated:
   if Exception_Caught /= Null_Occurrence then
      Reraise_Exception(Exception_Caught);
   end if;

end Do_Something;
于 2011-01-26T13:53:09.990 に答える
2

私はこのコードがあなたが求めることをするだろうと信じています。42現在raiseまたはで正常に印刷されreturnます。これはTEDの提案の実装です。

Mac OS X、Darwin10.6.0のGCC4.5.0でテスト済み。

with Ada.Finalization;
package Finally is

   --  Calls Callee on deletion.
   type Caller (Callee : not null access procedure)
      is new Ada.Finalization.Limited_Controlled with private;

private

   type Caller (Callee : not null access procedure)
      is new Ada.Finalization.Limited_Controlled with null record;

   procedure Finalize (Object : in out Caller);

end Finally;


package body Finally is

   procedure Finalize (Object : in out Caller)
   is
   begin
      Object.Callee.all;
   end Finalize;

end Finally;


with Ada.Text_IO; use Ada.Text_IO;
with Finally;
procedure Finally_Demo is
begin

   declare

      X : Integer := 21;

      --  The cleanup procedure, to be executed when this block is left
      procedure F
      is
      begin
         Put_Line ("X is " & Integer'Image (X));
      end F;

      --  The controlled object, whose deletion will execute F
      F_Caller : Finally.Caller (F'Access);

   begin

      X := 42;

      raise Constraint_Error;

   end;

end Finally_Demo;
于 2011-01-26T22:43:02.833 に答える
2

ada.finalizationとjavaのfinalizeブロックの違いを理解していると仮定すると、次のようなことを実行します。これは同じ効果があるはずです。

procedure x is 
begin

  -- some code
  begin
    -- more code (your try)
  exception 
    -- handle exception if necessary (caught exception)
  end;
  -- yet more code which is executed regardless of any exception handling.

end x;
于 2011-01-27T13:48:47.353 に答える
1

Marc Cは、それを直線的な手続き型コードでエミュレートしようとするための正しいアプローチを持っています。

ただし、IMHOの構造は、ほとんどの場合、JavaのOOシステムをハックする方法であり、昔ながらの手続き型プログラミングでOOの構造上の利点の1つが必要な場合に役立ちます。Javaでも、ほとんどの場合、代わりに適切なクラスを作成する方が適切です。

したがって、Adaでその機能を取得する適切な方法は、適切なオブジェクトを作成し、オブジェクトをの子にすることであると言っても過言ではないと思いますAda.Finalization.Controlled

実際のオブジェクトを作成する必要がない場合は、ダミーのオブジェクトを作成し、それにファイナライズコードを配置して、実行するブロックの一番上にあるスタックで宣言するだけです。その欠点は、制御された型自体(少なくとも最後にそれらを使用したとき)をパッケージレベルのスコープで宣言する必要があることです。その場合、宣言の低いオブジェクトへの直接参照をその中に入れることはできません。彼らは将来の言語改訂でそれを修正するつもりであると主張しました、しかし私は彼らが修正したかどうか見るために最近それを試していません。

于 2011-01-26T15:36:31.167 に答える
1

別の答えを考えただけです。それは少し重いです(そしておそらくそれが価値があるよりも多くの問題があります)。しかし、それはあなたの古いファイナライズブロックに少し似ているものをあなたに与えるでしょう

アイデアは、「ファイナライズ可能な」コードをタスクに入れることです。タスクが終了するまで、タスクが宣言されているスコープを離れることはできません。したがって、作業コードをタスクに入れ、「最終的に」コードをタスクが定義されているスコープのすぐ外に置くことができます。親タスクはそこに座って、作業タスクが(何らかの方法で)終了するのを待ってから、どのように終了しても、「最終的に」コードを実行します。欠点は、タスクが例外をスローした場合、タスクで停止することです。そのため、例外をスローできる動作はまだ完全には得られず、「finalize」コードの実行中に自動的に伝播されます。ランデブーと2番目のタスクを追加することでその動作を取り戻すことができます(これはタスクの問題です。ポテトチップスのようなものです...常にもう1つ必要です)。

procedure Finalized is
begin
    declare
        task Worker is end Worker;
        task body Worker is begin
            --// Working code in here. Can throw exceptions or whatever. 
            --// Does not matter.
        end Worker;
    begin
    end;

    --// If we get here, we know the task finished somehow (for good or ill)
    --// so the finalization code goes here.

end Finalized;

保護されたオブジェクトでもこのようなことをする方法があるように私には思えます。他の人が理解できるように、それを残しておきます。

于 2011-01-27T14:53:40.790 に答える
0

この問題を展望してみましょう。

プログラミング理論には、オブジェクトの作成と破棄、およびプロシージャの試行と最終の概念があります。どちらも最終的にはリソース管理を扱いますが、異なることに重点を置いています。

  • オブジェクトを使用して、オブジェクトポインタによって参照されるオブジェクトと変数の潜在的に長寿命のツリーを中心にピボットします。
  • プロシージャでは、プロシージャ呼び出しのスコープ内に存在する通常は一時的なオブジェクトと変数を軸にします(注目すべき例外はmainです

ここで、手順によっては、一連のリソースを生成する必要がある場合があります。致命的なエラーによって手順が中断された場合は、生成されたすべてのリソースを逆の順序でロールバックする必要があります。多くの場合、これは、それぞれのリソースの管理専用のオブジェクトを作成することで最も簡単に実現できます。Ada.Finalizationはここで非常に便利になります。

しかし、これはやり過ぎである可能性があり、ケースバイケースでこの手法に反対する議論が行われる可能性があります。したがって、プロシージャに対する自己完結型のリソース管理に関心があり、C ++スタイルのfinallyキーワードを使用する場合は、次のことを考慮してください。

私は最初は便利さの約束に満足していましたが、最終的には、コードを使用した後のコードの複雑さにがっかりしました。複雑なロールバック操作にはネストが必要であり、多くの場合、プロセスは線形ではなく、初期化をどこまで行ったかに応じて、ロールバックの方法を正確に決定するロジックが必要です。ネストされたブロック構造は、線形論理を使用するためにあなたを追い詰めます。したがって、より複雑なものが必要な場合は、gotoの使用を真剣に検討しています。

次の例に示すように、OOPスタイルのファイナライズと同じくらい食欲をそそるのは、私が述べたとおりであるため、Adaですべての頭痛の種なしに、真の試みと最終を達成できることを理解するのに十分な時間を経験しました。注意してください、それは完璧にはほど遠いですが、私はこの戦略で良いものが悪いものを上回っていると思います。この戦略で私が特に気に入っているのは、ファイナライズプロセスがいかに明確になり、すべてのステップがAdaの型システムに対してラベル付けされてチェックされ、適切に編成され、管理しやすいことです。C ++スタイルのtry&finallyはこの種のコードを分散させ、AdaのFinalizeはそれを非表示にし、フェイルオーバーロジックの上位レベルの順序を制御することを困難にします。

procedure Proc is
    
    type Init_Stages_All is (Do_Null, Do_Place, Do_Pour, Do_Drink);
    subtype Init_Stages is Init_Stages_All range Do_Place .. Do_Drink;
    Init_Stage : Init_Stages_All := Do_Null;
    
    procedure Initialize_Next is
    begin
        Init_Stage := Init_Stages_All'Succ(Init_Stage);
        case Init_Stage is
            when Do_Place => ...
            when Do_Pour => ...
            when Do_Drink => ...
            when Do_Null => null;
        end case;
    end Initialize_Next;

    procedure Finally is
    begin
        for Deinit_Stage in reverse Init_Stage .. Init_Stages'Last loop
            case Deinit_Stage is
                when Do_Place => ...
                when Do_Pour => ...
                when Do_Drink => ...
            end case;
        end loop;
    end Finally;

begin
    Initialize_Next; -- Do_Place
    ...
    Initialize_Next; -- Do_Pour
    ...
    Initialize_Next; -- Do_Drink
    ...
    Finally;

exception
    when E : others =>
        ...
        Finally;
        raise;

end Proc;

最後に、この戦略により、ファイナライズの例外への対処も容易になります。例外が発生したときに呼び出されるFinallyでプロシージャを作成し、Init_Stageを操作して、そこに追加のフェイルオーバーロジックを挿入することにより、Finallyを再帰的に呼び出すことをお勧めします。

于 2021-02-16T17:34:58.647 に答える