これがduck typingか……?
ホットみたいですのでhttp://d.hatena.ne.jp/Cryolite/20060108#p1の例をAdaにしてみました。
with Ada.Finalization; generic type Base_Type is abstract tagged limited private; package dsp is type Dynamic_Scoped_Ptr is limited private; function Element (Object : Dynamic_Scoped_Ptr) return not null access Base_Type'Class; generic type Derived_Type is new Base_Type with private; with procedure Finalize (Object : in out Derived_Type) is <>; function Constructor (Data : not null access Derived_Type) return Dynamic_Scoped_Ptr; private type Data_Access is access all Base_Type'Class; type Finalizer is abstract tagged null record; procedure Finalize (Object : in Finalizer; Data : in out Data_Access) is abstract; type Finalizer_Access is access all Finalizer'Class; type Dynamic_Scoped_Ptr is new Ada.Finalization.Limited_Controlled with record Data : Data_Access; Finalizer : Finalizer_Access; end record; overriding procedure Finalize (Object : in out Dynamic_Scoped_Ptr); end dsp;
with Ada.Unchecked_Deallocation; package body dsp is procedure Free is new Ada.Unchecked_Deallocation (Finalizer'Class, Finalizer_Access); function Element (Object : Dynamic_Scoped_Ptr) return not null access Base_Type'Class is begin return Object.Data; end Element; function Constructor (Data : not null access Derived_Type) return Dynamic_Scoped_Ptr is type Derived_Finalizer is new Finalizer with null record; overriding procedure Finalize (Object : in Derived_Finalizer; Data : in out Data_Access) is pragma Unreferenced (Object); type Derived_Access is access all Derived_Type; procedure Free is new Ada.Unchecked_Deallocation (Derived_Type, Derived_Access); D_Var : Derived_Type renames Derived_Type (Data.all); D_Var_Access : Derived_Access := D_Var'Access; begin Finalize (D_Var); Free (D_Var_Access); end Finalize; D_Fin : constant access Derived_Finalizer := new Derived_Finalizer; pragma Suppress (Accessibility_Check); begin return (Ada.Finalization.Limited_Controlled with Data_Access (Data), D_Fin.all'Unchecked_Access); end Constructor; overriding procedure Finalize (Object : in out Dynamic_Scoped_Ptr) is begin Finalize (Object.Finalizer.all, Object.Data); Free (Object.Finalizer); end Finalize; end dsp;
with dsp; with Text_IO; procedure Test is type Base is abstract tagged limited null record; type Derived_1 is new Base with null record; procedure Finalize (Object : in out Derived_1) is begin Text_IO.Put_Line ("Close 1"); end Finalize; type Derived_2 is new Base with null record; procedure Finalize (Object : in out Derived_2) is begin Text_IO.Put_Line ("Close 2"); end Finalize; begin declare package p is new dsp (Base); function ctor is new p.Constructor (Derived_1); function ctor is new p.Constructor (Derived_2); ptr1 : p.Dynamic_Scoped_Ptr := ctor (new Derived_1); ptr2 : p.Dynamic_Scoped_Ptr := ctor (new Derived_2); begin null; end; end Test;
C++との違いは、genericのインスタンス化を明示的にしかできないため、newとは別に、p.Constructorをインスタンス化するところでDerived_1やDeriver_2の型名を書かなければならないところぐらいと思うのですが、これをduck typingと呼ぶのは違和感があるようなないような。単なるラッパーでは……。
私がduck typingを、折角クラス単位に閉じ込めたメソッドのスコープを再びグローバルなものにしてしまうあまり嬉しくない機能としか思ってないのも違和感の原因かも。非侵入的といっても、特定のメソッドについて名前を拘束してしまう時点で侵入してるわけですよ。同名同シグネチャのメソッドを要求する他の用途と被ってしまったら、スコープも何も無いわけで。
まあAdaなら回避できますがねっ!(←これが言いたかっただけ)
type Derived_3 is new Base with null record; procedure Finalize (Object : in out Derived_3); -- 別の用途に使用 procedure Close (Object : in out Derived_3) is -- 終了処理はこっち ... function ctor is new p.Constructor (Derived_3, Finalize => Close); -- 明示的にCloseを渡す
話題そのものについては、それ型クラスでできるよ派や、それMixJuiceでできるよ派や、関数ポインタ渡した方がマシだろう派の登場が期待されます。個人的にはVMT弄ってinterfaceを後付けで実装するテクニックが一番役に立つよ派を応援しています。