الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

المؤشرات إلى الدوال

بدأه mhewedy في 24 أغسطس 2010 · 6 رد · 1,082 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

البارحة قرأت عن المؤشرات إلى الدوال (Pointer to functions) (أو الدوال المؤشريه :D ) و احببت أن أشارككم ما قرأت..

المؤشرات للدوال ميزه موجوده في لغه ال سي .. و هي ميزه مهمه لأن لها عدة إستخدامات ... سنتطرق لمثال لإستخدامها في نهاية الدُريس :D

تعريف مؤشر على دالة:

كما نعرف أنه لتعريف مؤشر على متغير فإننا نستخدم :

int* ptr_to_int;

أما لتعريف مؤشر على دالة فإننا نستخدم:

return_type (*ptr_to_func) (param_list);

مثلا:

int (*execute) (char* );

هذا بالنسبة للتعريف... اما بالنسبة لل initialization , فإننا نستخدم الشكل التالي:

execute = func_name;

حيث أن func_name هي دالة لها الشكل التالي:

int func_name (char*);

أي أن الدالة func_name يجب (أعتقد ذلك) أن يكون لها نفس ال signature للمؤشر على الدالة....

أما بالنسبة لإستخدام المؤشر على الدالة:

فإنه يستخدم لتنفيذ الدالة التي يشير عليها....

فإن :

execute("select * from employees;");

يقوم بتنفيذ الدالة func_name ...

التطبيقات

هناك أكثر من تطبيق للمؤشر على الدوال... مثلا المستخدم في الدالة qsort في stdlib.h

لكن هنا, سأتحدث عن تطبيق أخر للمؤشرات على الدوال.... و هو إستخدامها لعمل corss-cutting للكود ... او بما بيمثل شكل بيسط من أشكال ال Aspect-oriented programming (AOP):

(نستخدم في الجافا Dynamic proxies + reflection للحصول على مثل هذا العمل..... فعمل هذا في السي أيسر :D )

أترككم مع المثال:

#include <stdio.h>

void sql_insert(char*);
void sql_update(char*);
void sql_delete(char*);
void sql_select(char*);

void do_database_task(void (*sql_query) (char*), char*);

int main(void)
{
	do_database_task(sql_select , "SELECT * FROM employees");
	printf("\n");
	do_database_task(sql_delete , "DELETE FROM employees");

	return 0;
}

void sql_insert(char *query)
{
	printf("Executing insert query: %s\n", query);
}
void sql_update(char *query)
{
	printf("Executing update query: %s\n", query);
}
void sql_delete(char *query)
{
	printf("Executing delete query: %s\n", query);
}
void sql_select(char *query)
{
	printf("Executing select query: %s\n", query);
}

void do_database_task(void (*sql_query) (char*), char* query)
{
	printf("openning connection ....\n");
	sql_query(query);
	printf("closing connection ....\n");
}
3
#2

الفائدة المنتشرة للpointer to function هى الuser defined callback سواء لاحداث مثل استقبال signal او ما الى ذلك .

وفى بعض الاحيان تستخدم من اجل التخصيص (تعديل السلوك الافتراضى او وضعة) لدالة معرفة (او تعديل سلوك القيام بcompare بين قيمتين فى دالة sort ) وما الى ذلك .

ولها استخدامات اخرى مفيدة ايضاء وليست محدودة الا بافكار المبرمج وهناك ادوات اخرى فى السى لا يمكن اهمالها مثل الunion و void* وهى ادوات تعطى مرونة عالية.

name : mohamedyosry

#3
اقتباس
الفائدة المنتشرة للpointer to function هى الuser defined callback سواء لاحداث مثل استقبال signal او ما الى ذلك .

ممكن تعطينا مثال يا أخي؟

#4

فى الposix هناك مفهوم الsignal حيث تقوم احد الprocess باطلاق signal لعملية اخرى (لكن يجب ان يكون لها الصلاحيات) او تستقبلها بسبب تصرف من المستخدم (مثل crtl + c لارسال SIGINT )

نقوم بعمل handling (فانكشن تعمل عند استقبال الsignal ) .

فى الهيدر signal.h تعرف دالة

sighandler_t signal( int signum, sighandler_t handler );

وهى دالة تستخدمها لارسال فانكشن (قمت انت بكتابتها sighandler_t ) لتعمل عن استقبال الsignal .(تشبة الexception handling )

تلك المقالة ستوضح بشكل افضل

http://www.alexonlinux.com/signal-handling-in-linux

مثلا فى gtk+ تقوم بربط استقبال الsignal بcallback يحدث فور استقبال الاشارة

gulong g_signal_connect (gpointer object,
const gchar *signal_name,
GCallback handler,
gpointer data);

الobject هو الwidget التى سيستقبل عليها الاشارة

const gchar *signal_name, هو اسم الاشارة التى ينتظرها (مثل استقبال ضغطة زر معين)

GCallback handler وهو مشتق من pointer to function لتقوم بارسال ليكون الدالة التى تعمل عند استقبال الحدث .

data هى بيانات generic لترسلها للدالة الhandler وقد تكون NULL .

كما لاحظت الاسلوبين المستخدمين هما نوع من الtypedef او الاشتقاق للpointer to function

ارجو ان اكون وضحت الفكرة

name : mohamedyosry

#5

نعم قد وضحت الفكره.. جزاك الله خيرا :D

#6
اقتباس
نستخدم في الجافا Dynamic proxies + reflection للحصول على مثل هذا العمل..... فعمل هذا في السي أيسر :D

على العكس، هذا النوع من الإستدعاءات فى اللغات التى تتيح الـ Reflection يكون أيسر كثيرا مما هو موجود داخل اللغتين C++/C (هذا الأمر فى الأسمبلى أيسر كثيرا جدا منهم مجتمعين).

فمثلا فلنفترض أنك تريد من المستخدم ان يحدد لك داله انت لا تعرف بصمتها و لكنك تعرف انه سيحدد لك عدد معاملاتها و قيمة كل معامل، الان أنت تريد تنفيذ هذه الداله، السؤال هو كيف ستستطيع عمل هذا بإستخدام مؤشرات الدوال؟

فى الواقع لن يمكنك، أما داخل اللغات التى تتيح الـ Reflection يمكنك عمل ذلك بكل سهوله حيث ان الـ metadata موجوده ، بالإضافه أنه سيتم عمل type check على القيم التى سيتم تمريرها (أو بالأحرى سيتم تحويل القيمه من object إلى نوع المعامل إن كانت عملية التحويل متاحه) و فى النهايه بعد أن تقوم عمل assign لكل المعاملات ساقوم بإستدعاء الداله invoke او execute او أيا كان أسمها للحصول على النتيجه.

مع العلم أن مؤشرات الدوال تعطي قوه كبيره إلا أنه فى بعض الحالات تحجمك اكثر مما تطلقك.

معلومه إضافيه: مؤشرات الدوال تمكنك من الإستغناء عن switch تماما، هذا إن كنت تهتم للسرعه (و حتى فى هذه الحاله فهى تعتمد على الكود المكتوب و لكن بشكل عام هي أسرع).

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة محمد علاء الدين في 25 أغسطس 2010 في 02:57

مدونتي: C++ Tips and Tricks

#7

أتفق معاك أخي محمد:

اقتباس
و لكن بشكل عام هي أسرع
أي نعم كما أن ال reflection أمكانيتها أكبر (بوجود ال metadata) لكنها غالبا ماتؤثر على الأداء.

مواضيع مشابهة