PDA

View Full Version : سوال: ارجاع به پروسیجر و اجرای آن با اشاره گر



یوسف زالی
چهارشنبه 16 فروردین 1391, 08:13 صبح
سلام.
آیا روشی هست که بشه یک روال رو از طریق آدرسش اجرا کرد؟
منظور من اینه:
procedure P1(Arg: integer); // test
.
.
Ptr := P1(5); // psudo
.
.
ExecuteProc(Ptr); // sample

توجه کنید که پارامتر ها چه موقع ست می شوند.
ممنون از همه

mbshareat
پنج شنبه 17 فروردین 1391, 10:59 صبح
سلام
چرا یه پروسیجر بدون پارامتر که از متغیرهای عمومی استفاده کنه توی برنامه استفاده نمی کنین؟
این کد رو برای اونهایی می ذارم که نمی دونن چطور میشه یه پروسیجر رو بدون تعیین نامش فراخوانی کنن!:


var
Form1: TForm1;
P:Procedure(S:String);
implementation

{$R *.dfm}
Procedure ProcSample(S:String);
Begin
ShowMessage(S);
End;
procedure TForm1.FormCreate(Sender: TObject);
begin
P:=ProcSample;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
P('Life''s Good!');
end;

Mask
پنج شنبه 17 فروردین 1391, 11:21 صبح
آیا ایونتها رو هم میشه اینجوری کرد؟

Ananas
پنج شنبه 17 فروردین 1391, 13:02 عصر
سلام.

آیا ایونتها رو هم میشه اینجوری کرد؟
بله میشه . معمولا به شکل های زیر هستند:
TNotifyEvent, TShortCutEvent, THelpEvent, TCloseQueryEvent, TCloseEvent.

Mask
پنج شنبه 17 فروردین 1391, 13:07 عصر
سلام.

بله میشه . معمولا به شکل های زیر هستند:
TNotifyEvent, TShortCutEvent, THelpEvent, TCloseQueryEvent, TCloseEvent.
مثل دوستمون میشه یه مثال بزنید.

یوسف زالی
پنج شنبه 17 فروردین 1391, 21:54 عصر
نه.
اون که شما نوشتید رو می دونم.
حتی می شه یک نوع ازش گرفت.
منظور و تاکید من دقیقا همون طوره که نوشتم.
علتش هم اینه که نمی خوام مجبور به رعایت امضای پروشیجر باشم و بتونم "هر" پروسیجری رو با مقادیر از قبل تعیین شده اجرا کنم.
Exec P; // P=procedure(120); // test
Exec P; // P=procedure(Edit1, 14.5); // test
و ..

در جواب جناب Gold:
روی فرم راست کلیک کرده و View As Text کنید و رویداد ها رو نگاه بندازید.
مثال:
procedure OnMyClick(Sender: TObject); begin DoSomething end
.
.
Button1.OnClick := OnMyClick
.
.
Button1.OnClick(Self) // test

نکته ای رو که باید حواستون بهش باشه اینه که رویداد ها می باید درون یک کلاس تعریف شوند تا بتوان به یک شی منسوبش کرد.
در اینجا OnMyClick باید بخشی از تعریف مثلا فرمتون باشه.

اخوی Indy !!!

Ananas
پنج شنبه 17 فروردین 1391, 23:48 عصر
مثل دوستمون میشه یه مثال بزنید.
بله میشه.

ر اینجا OnMyClick باید بخشی از تعریف مثلا فرمتون باشه.
ممنون از توضیحاتتون ولی لازم نیست حتما بخشی از فرم باشه مثال :

procedure fn(Sender: TObject);
begin
ShowMessage('Ok.');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
f : TNotifyEvent;
begin
@f := @fn;
f(nil);
end;


علتش هم اینه که نمی خوام مجبور به رعایت امضای پروشیجر باشم و بتونم "هر" پروسیجری رو با مقادیر از قبل تعیین شده اجرا کنم.
فکر میکنم بهترین کار این باشه که اونو تو یک تابع دیگه فراخونی کنی مثال :

function Sin45:Extended;
begin
Result := Sin(Pi / 4.0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(FloatToStr(Sin45));
end;

یوسف زالی
جمعه 18 فروردین 1391, 00:53 صبح
اون وقت باید برای هر امضا یک پروسیجر نوشت..
در مورد شیوه آدرس دهی حق با شماست.

Ananas
شنبه 19 فروردین 1391, 04:11 صبح
اون وقت باید برای هر امضا یک پروسیجر نوشت..
دیگه به هر حال شما که میخوای اون رو تو قسمت const تعریف کنی، عوضش تو یک procedure تعریف کن البته شاید سرعتش یکمکی بیاد پایین که فکر میکنم خیلی مهم نباشه.

یوسف زالی
شنبه 19 فروردین 1391, 07:10 صبح
پس اصلا متوجه عمق فاجعه نشدید.
قراره یک پراپرتی از یک کلاس بتونه تمامی امضاهای پروسیجر رو بپذیره و اونها رو کال کنه. البته بدون تخصیص پارامتر هاش.
Obj.X := proc(13, edit1.text, nil); // test
بتونه این رو هم بگیره:
Obj.X := proc2(PI, []); // test2

SAASTN
شنبه 19 فروردین 1391, 08:54 صبح
برای چی یه همچین چیزی لازم داری؟ اگه قضیه اینه که یه متدی در شرایط مختلف به مقادیر متفاوت، باتعداد و انواع داده ای متفاوت نیاز داره میشه از array of const استفاده کرد، مثل Format. تو پردازشای سنگین خیلی بهینه نیست ولی برای کارای جنرال جواب میده.

Ananas
شنبه 19 فروردین 1391, 14:11 عصر
خوب اینکه خوبه میتونی یه type جدید به شکل زیر تعریف کنی و همه ی امضا ها رو از نوع همین type تعریف کنی :
type
TMyProcedure = procedure;
TMyFunction = function : Boolean;

و امضا ها رو میتونی به شکل تابع تعریف کنی و در صورت درست اجرا شدن یک مقدار True به فراخوان برگردونی.

یوسف زالی
یک شنبه 20 فروردین 1391, 04:01 صبح
SAASTN جان دارم یک کامپوننت می نویسم که یکی از پراپرتی هاش باید همچین چیزی داشته باشه.
اول آرایه گرفتم اما این طوری باید در زمان فراخوانی دوباره پارامتر ها داده شوند.
لازم دارم پارامتر ها ست بشه و در زمان فراخوانی فقط اسم کال شه.
Ananas جان فکر کنم باز هم نگرفتی مطلب رو.
امضاهای مختلف چجوری به یک اشاره گر پاس بشه؟ فقط یه اشاره گر داریم.

Ananas
یک شنبه 20 فروردین 1391, 07:46 صبح
Ananas جان فکر کنم باز هم نگرفتی مطلب رو.
امضاهای مختلف چجوری به یک اشاره گر پاس بشه؟ فقط یه اشاره گر داریم.
جانت بی بلا. خوب متد OnClick مگه چیه؟ اونم یه جور اشاره گر هست دیگه که به یک TNotifyEvent اشاره میکنه یا شایدم یک متغیره! حالا هر چی... به هر حال میتونی OnClick های مختلف رو به پراپرتی مثلا button نسبت بدی. اگه میخوای پارامتر های ورودی برای هر تابع رو در زمان اجرا از کاربر بگیری و چند جا به شکل ثابت ازشون استفاده کنی که یکمی پیچیده تر میشه فکر کنم باید یک کلاس براش تعریف کنی. اما اگه می خوای از قبل یعنی در زمان ساخت برنامه خودت اونها رو مشخص کنی، همون روشی که عرض کردم جواب میده.
منظورت از این که فقط یک اشاره گر داریم چیه؟ مگه چه چیز دیگه ای احتیاج داریم؟ خوب ما می خوایم بدون وارد کردن هیچ پارامتری، یک تابع رو که قبلا پارامتر های ثابت بهش دادیم رو فراخونی کنیم.اگه می خوای مثال بزنم. یک بار دیگه دقیق به تعریف OnClick برای form یا button توجه کن ببین چطور اونها رو تو یونیت forms و StdCtrls تعریف کرده. فکر میکنم شما هم همچین چیزی لازم داری. اگه نه بیشتر توضیح بده اشکال چیزی که میگم دقیقا کجاست؟

یوسف زالی
یک شنبه 20 فروردین 1391, 19:11 عصر
ببین برادر!
OnClick فقط یک ورودی داره به نام Sender که می تونه آدرس شی یا نال باشه.
نمی تونه دو تا یا سه تا پارامتر داشته باشه.
برای این کار باید دو تا OverLoad نوشت.
از طرفی برای فراخوانی اون نمی گی مثلا Object1.OnClick بلکه می گی OnClick(SomeObj) // sample
من می خوام مقادیر و تعداد پارامتر ها مثلا در یک پراپرتی از شی از قبل داده بشه و برای کال شدن متد فقط بگی متد همین!
مثال:
Obj.Parameters := [edit1.text, 15, 90]; // pseudo
Obj.MethodAddress := @SomeMethod; // pseudo
.
.
Obj.ExecMethod; // just like that, bro

Ananas
یک شنبه 20 فروردین 1391, 21:07 عصر
خوب من دفعه چندومه که همه ی پست های شما رو از اول تا اینجا میخونم. الان بهتر فهمیدم چی می فرمایید.
سوال: اول اینکه شما این امضا ها یا توابعی که قراره یک متد از شی اونها رو فراخونی کنه رو میدونی که دقیقا چه چیزایی هستن و ورودی ها از چه جنس هایی هستن یا اینکه در زمان اجرا ممکنه هر چیزی رو بخواد call کنه و مشخص نیست ورودی ها چی هستن؟
اگه بخواد هر چیزی رو بدون اینکه از لیست پارامترهاش اطلاع داشته باشی call کنه شرمنده من نمی دونم چطور این کارو بکنی ولی اینجا رو ببین:

http://barnamenevis.org/showthread.php?334617-%D8%A8%D8%A7%D8%B2-%D8%AA%D8%B9%D8%B1%DB%8C%D9%81-%DB%8C%DA%A9-%D8%AA%D8%A7%D8%A8%D8%B9-%D9%86%D9%88%D8%B4%D8%AA%D9%87-%D8%B4%D8%AF%D9%87-%D8%AF%D8%B1-%D9%81%D8%A7%DB%8C%D9%84-%D8%B3%D8%B1%D8%A7%DB%8C%D9%86%D8%AF-c-%D8%AF%D8%B1-%D8%AF%D9%84%D9%81%DB%8C
اما اگه مشخصه که مثلا 20 تا تابع داری که نسبت به هر کدوم ورودی ها متفاوت هست ولی معلومه مثلا تابع پنجم قراره فلان نوع پارامتر ها رو بگیره. خوب برای این چند روش تو نظرم هست:
اگه همه ی امضا ها و توابع رو خودت بخوای تعریف کنی خیلی راحت تر هست. مثلا شما میای همه ی ورودی ها رو که ممکنه تابع ها لازم داشته باشن تو یک record یا یک کلاس میریزی بعد یک نمونه از اون record رو همراه برای همه ی امضا ها میفرستی تا هر کدوم از اطلاعات مورد نیاز خودشون تو اون record استفاده کنن. و یا از وراثت استفاده میکنی مثلا همون OnClick که یک Sender رو دریافت میکنه میتونه Sender رو به عنوان یک Button و یا یک Form و یا ... استفاده کنه و باید برنامه نویس خودش بدونه چی داره فرستاده میشه و یا از طریق case of نسبت به هر شی یک جور خاص عمل کنه. این یک روش و حالا روش بعد:
اینو از دایرکت ایکس یاد گرفتم که چند مورد از این روش استفاده کرده که یک تابع داره که مثلا دو یا سه پارامتر ورودی داره و مثلا با این دو تا ورودی هر کاری لازم داشته باشه انجام میده به این شکله که یک نوع شمارشی داره که نوع عمل درخواستی رو مشخص میکنه و یک عدد DWORD که میتونه به عنوان هر چیزی استفاده بشه یعنی میتونه خودش یک مقدار عددی باشه، میتونه از جنس نوع شمارشی باشه و یا میتونه اشاره گری به ابتدای یک record باشه و یا اشاره گری به یک Single باشه و یا اشاره گری به ابتدای یک رشته و هر چیزی که لازم باشه میتونه به این شکل استفاده بشه مثال :

xdev.SetRenderState(D3DRS_ZENABLE, iTrue);
xdev.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
xdev.SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
xdev.SetRenderState(D3DRS_LIGHTING, iFalse);
xdev.SetRenderState(D3DRS_NORMALIZENORMALS, iTrue);
xdev.SetRenderState(D3DRS_SPECULARENABLE, iFalse);
xdev.SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, iFalse);

xdev.SetRenderState(D3DRS_ALPHABLENDENABLE, iTrue);
xdev.SetRenderState(D3DRS_SRCBLEND , D3DBLEND_SRCALPHA);
xdev.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

x := 4.0;
xdev.SetRenderState(D3DRS_POINTSIZE, Cardinal(Pointer(x)));
xdev.SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, iTrue);
xdev.SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, iTrue);

xdev.SetRenderState(D3DRS_STENCILENABLE, iFalse);

این در صورتی هست که شما نوع توابعتون مشخص باشه.
یک کار دیگه هم میتونید بکنید :
برای هر تابع یک record داشته باشید که متغیر های ورودی رو در اون ذخیره کنید و برای فراخانی تابع باید یک اشاره گر به تابع و یک اشاره گر به record مخصوص اون تابع رو به متد فراخوان بفرستید و در یک case of مناسب هر تابع رو با record مخصوصش فراخونی کنید برای یک بار در متد خاصتون بعد دیگه همیشه برای فراخوانی هر کدوم از امضا ها یک اشاره گر به امضا و یک اشاره گر به record ذخیره کننده ی پارامتر به متد بفرستید. تقریبا همون روش دایرکت ایکس که گفتم.
فعلا چیز دیگه ای به نظرم نمیاد.

Ananas
یک شنبه 20 فروردین 1391, 22:20 عصر
اینم مثال :

unit Unit2;

interface

uses Math;

type
TVec2D = packed record
x, y : Single;
end;
TVec3D = packed record
x, y, z : Single;
end;
TFUNCTION_NAME =
(
FN_SIN,
FN_ARCTAN2,
FN_VEC2D_NORMALIZE,
FN_VEC3D_NORMALIZE,
FN_VEC2D_LENGTH,
FN_VEC3D_LENGTH
);

function CalcSin(out pOut : Pointer; const param : Pointer):Boolean;
function CalcArcTan2(out pOut : Pointer; const param : Pointer):Boolean;
function CalcVec2DNormalize(out pOut : Pointer; const param : Pointer):Boolean;
function CalcVec3DNormalize(out pOut : Pointer; const param : Pointer):Boolean;
function CalcVec2DLength(out pOut : Pointer; const param : Pointer):Boolean;
function CalcVec3DLength(out pOut : Pointer; const param : Pointer):Boolean;

function Calculate(out pOut: Pointer; const fn : TFUNCTION_NAME; const param : Pointer):Boolean;


implementation

function CalcSin(out pOut : Pointer; const param : Pointer):Boolean;
begin
pOut := AllocMem(SizeOf(Single));
Single(pOut^) := Sin(Single(param^));
Result := True;
end;

function CalcArcTan2(out pOut : Pointer; const param : Pointer):Boolean;
begin
pOut := AllocMem(SizeOf(Single));
Single(pOut^) := ArcTan2(
TVec2D(param^).x,
TVec2D(param^).y);
Result := True;
end;

function CalcVec2DNormalize(out pOut : Pointer; const param : Pointer):Boolean;
var
length_ : Single;
begin
pOut := AllocMem(SizeOf(TVec2D));
length_ := Sqrt(Sqr(TVec2D(param^).x) + Sqr(TVec2D(param^).y));
TVec2D(pOut^).x := TVec2D(param^).x / length_;
TVec2D(pOut^).y := TVec2D(param^).y / length_;
Result := True;
end;

function CalcVec3DNormalize(out pOut : Pointer; const param : Pointer):Boolean;
var
length_ : Single;
begin
pOut := AllocMem(SizeOf(TVec3D));
length_ := Sqrt(Sqr(TVec3D(param^).x) + Sqr(TVec3D(param^).y) + Sqr(TVec3D(param^).z));
TVec3D(pOut^).x := TVec3D(param^).x / length_;
TVec3D(pOut^).y := TVec3D(param^).y / length_;
TVec3D(pOut^).z := TVec3D(param^).z / length_;
Result := True;
end;

function CalcVec2DLength(out pOut : Pointer; const param : Pointer):Boolean;
begin
pOut := AllocMem(SizeOf(Single));
Single(pOut^) := Sqrt(Sqr(TVec2D(param^).x) + Sqr(TVec2D(param^).y));
Result := True;
end;

function CalcVec3DLength(out pOut : Pointer; const param : Pointer):Boolean;
begin
pOut := AllocMem(SizeOf(Single));
Single(pOut^) := Sqrt(Sqr(TVec3D(param^).x) + Sqr(TVec3D(param^).y) + Sqr(TVec3D(param^).z));
Result := True;
end;

function Calculate(out pOut: Pointer; const fn : TFUNCTION_NAME; const param : Pointer):Boolean;
begin
case fn of
FN_SIN: Result := CalcSin(pOut, param);
FN_ARCTAN2: Result := CalcArcTan2(pOut, param);
FN_VEC2D_NORMALIZE: Result := CalcVec2DNormalize(pOut, param);
FN_VEC3D_NORMALIZE: Result := CalcVec3DNormalize(pOut, param);
FN_VEC2D_LENGTH: Result := CalcVec2DLength(pOut, param);
FN_VEC3D_LENGTH: Result := CalcVec3DLength(pOut, param);
end;
end;

end.

طرز استفاده :

procedure TForm1.Button1Click(Sender: TObject);
var
ps : ^Single;
v : TVec2D;
pv : ^TVec2D;
begin
v.x := -0.5;
v.y := 0.5;
if Calculate(Pointer(ps), FN_VEC2D_LENGTH, @v) then
ShowMessage('Length [' + FloatToStr(v.x) + ' , ' + FloatToStr(v.y) + '] : ' + FloatToStr(ps^));
if Calculate(Pointer(pv), FN_VEC2D_NORMALIZE, @v) then
ShowMessage('Normal : [' + FloatToStr(pv.x) + ' , ' + FloatToStr(pv.y) + ']');
end;

یک تابع اصلی به اسم Calculate تعریف شده که با case of مشخص میکنه که کدوم تابع اجرا بشه و همه ی توابع با اشاره گر پارامتر های خوشونو میگیرن که ممکنه یک عدد اعشاری باشه و یا یک بردار سه بعدی باشه و خروجی ها هم میتونن متفاوت باشن که با pOut از تابع خارج میشن و مقدار True میتونه به عنوان درست اجرا شدن تابع در Result ریخته شه.