Delphi’de Prosedürel Tipler ve Metod Pointer

Öncelikle bu yazının pointer konusunun devamı niteliği taşıdığını belirteyim. Pointer ve Referans konusuna bir göz atmak isterseniz:
http://akcakir.net/delphide-pointer-ve-referans/

Metod pointerı prosedürel tipler altında bir başlık gibi düşünebilirsiniz. “ve” ayracı koymuş olmamın sebebi ayrı bir şey olması değil, bu yazıda asıl anlatmak istediğim noktanın o olmasıdır. Ayrıca başlığın “Prosedürel” olması sadece Prosedürleri ilgilendirdiğini belirtmemekte. Yani prosedür için geçerli olan her şey, fonksiyon için de geçerlidir.

Konuya teorik olarak başlarsam, bu yazıda bir çeşit değişken tipini anlatacağım. Ama bu sefer değişkenimiz bir veri değil bir fonksiyonu, adresini tutacak. Aslında bu da bir çeşit veri. Çok basit bir şekilde göstermek istersek. Öncelikle bir fonksiyon tanımlıyorum;

function Topla(x,y: Integer): Integer;
begin
  Result:=x+y;
end;

Bu bildiğimiz 2 sayıyı toplayan bir fonksiyon. Şimdi bu fonksiyonu bir değişkene atayalım. Burada atadığımız şey fonksiyonun döndürdüğü sonuç değil, fonksiyonun kendisidir.

var
  F: function(x,y: Integer): Integer;
begin
  F:=Topla;
end;

Daha sonra fonksiyonu şu şekilde çağırabiliriz;

F(10,15);

Tabi bunu şu biçimde kullanmak daha güzel olacak.

type
  TFunc=function(x,y: Integer): Integer;
var
  F: TFunc;

type bloğunda tanımını yaparak fonksiyonu bir fonksiyon parametresi olarak da kullanabiliriz.

procedure Proc(F: TFunc); 

Şimdi saçma sapan bir örnekle gösterelim:

type
  TFunc=function(x,y: Integer): Integer;

function Topla(x,y: Integer): Integer;
begin
  Result:=x+y;
end;

function Carp(x,y: Integer): Integer;
begin
  Result:=x*y;
end;

procedure Proc(F: TFunc);
var
  i:Integer;
begin
  ShowMessage(IntToStr(F(10,15)));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Proc(Topla);
  Proc(Carp);
end;

Burada prosedürümüze hangi fonksiyonu kullanacağını göstermiş olduk.

Şimdiye kadarki kısımda değişkenlerimiz, birer prosedür işaretçisiydi. Şimdi ise prosedür ve fonksiyonların adresini işaret edenlere bakalım.

Metod Pointer:

Bir nesnedeki metodu referans almak istersek kullanırız. Tanımımızın sonuna “of object” ekliyoruz.

TFunc=function(x,y: Integer): Integer of object;

Buradaki önemli ayrım noktası metodun nesneye ait olmasıdır. Statik metodlar da buna dahildir. Önceki tanım türünde nesneye ait olan bir fonksiyonu işaret edemeyiz. Burada ise nesneye ait olmayan bir fonksiyonu işaret edemeyiz.

Bütün fark bu mu? Evet kullanım bazında tek fark “of object” eklemek. Bu arada nesnenin yaratılmış olması gerektiğini belirtmeme gerek yok sanırım. (statik metodlar hariç tabi.)

Teknik olarak bakarsak, Metod Pointer aslında bir çift pointerdan ibarettir.
1. Pointer: Metod Adresini tutar.
2. Pointer: Metodun ait olduğu nesnenin referansını tutar.

Şimdi yukarıdaki saçma sapan örneğimizde Topla ve Carp fonksiyonlarımızı bir nesneye ait yapıp uygulamasını yapabilirsiniz.

Ek bilgi olarak söyleyeyim, yazının başında gösterdiğim tip, yani “of object” olmadan yapılan tanım, literasyonda “Regular Procedure” veya “Procedure Pointer” olarak geçer.

Uyumluluk şartları;
1. Aynı tip dönüş değeri (veya her ikisi de prosedür)
2. Aynı sayıda parametre ve parametreler sırasıyla aynı tiplerde olmalı. (Parametre isimleri önemsizdir)
3. Aynı çağırma düzeni (Calling Convention)

Her hangi bir şekilde Regular Procedure ile Method Pointer arasında uyumluluk söz konusu değildir.

İç içe yazılan, yerel fonksiyon ve prosedürler atanamaz.

Ön tanımlı rutin fonksiyon ve prosedürler de atanamaz. (Ör: Length()) Bunun ataması için aşağıdaki gibi bir aracı fonksiyon yazabilirsiniz.

function FLength(S: string): Integer;
begin
  Result := Length(S);
end;

Tüm bunları anlattıktan sonra bir kaç noktaya değinmek istiyorum. Şu örneğe bir bakalım,

function Fonksiyon: Integer;
begin
  Result:=10;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  F, G: function: Integer;
  i: Integer;
begin
  //Fonksiyon'u F'e atar
  F := Fonksiyon;

  //F'teki fonksiyonun referansını G'ye atar.
  G := F;

  //G fonksiyonunu çağırıp gelen değeri i'ye atar.
  i := G;
end;

Buradaki önemli nokta aslında son iki komut. Yani G’yi F’e atarken F’teki fonksiyonun referansını verirken, i’yi G’ye atarken G’deki fonksiyonunu çağırıp sonucunu i’ye atıyor. Bunu atama operatörümüzdeki overload ile açıklayabiliriz. Yani sol taraftaki değişken bir prosedürel tipse referans verilir. Değilse fonksiyon çağırılır.

Peki, bu fonksiyonun dönüş tipi bir prosedürel tipse. Buna da bakalım;

type
  TFonk=function: Integer;
function Fonksiyon2: Integer;
begin
  Result:=10;
end;
function Fonksiyon: TFonk;
begin
  Result:=Fonksiyon2;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
  F, G: TFonk;
  i: Integer;
begin
  F := Fonksiyon();//Fonksiyonu çağır
  G := F;
  i := G;
end;

İşte burada parametre olmasa dahi () parantez açıp kapamamız gerekir. Bu derleyiciye fonksiyon referansı değil, fonksiyonu çağırmak istediğimizi gösterir.

Şimdi son örnek üzerinden devam edelim. F ile G referansları arasında karşılaştırma yapmak istesek,

if F = G then

Bu yanlış olurdu bizim için. Burada F ile G fonksiyonlarını çağırıp gelen değerleri karşılaştırır. Fonksiyonlar aynı mı diye bakacaksak,

if @F = @G then

Şeklinde yazmamız gerekir. @ operatörünü pointer’dan hatırlarsanız, adres alıyordu. Burada Regular Procedure’ün de aslında pointer olduğunu gördük. Eğer ki prosedürel değişkenin saklandığı adres alınacaksa, @@ kullanılması gerekir (pointerın pointerı)
Yani @@F, F değişkeninin adresini verir.

Buna ilave olarak @ operatörü tipsiz pointerı prosedürel değişkene atarken de kullanılır,

var
  KernelHandle: THandle;
  StrComp: function(Str1, Str2: PChar): Integer;
begin
  KernelHandle := GetModuleHandle('KERNEL32');
  @StrComp := GetProcAddress(KernelHandle, 'lstrcmpi');

Bahsettiğim kullanımı en alt satırda görüyorsunuz.

Son olarak prosedürel değişkenin değeri nil olduğu zaman çağıramayız, bu yüzden doğru çağırma şekli, çağırmadan önce nil olup olmadığına bakmaktır;

if Assigned(Fonksiyon) then Fonksiyon(x);

Ayrıca anonim metodları da bu başlık altında incelemek mümkündür. Ancak bunun için ayrı bir yazı yazıp detaylı bir şekilde konuyu ele almak istiyorum. Başka bir yazıda görüşmek üzere, hoşçakalın..

Tags: , , , , , , , , , , , , ,

No comments yet.

Leave a Reply

*