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

طرق كتابة "دالة" بعدد متغير من البارمترات .

بدأه Abdullah.Alshammeri في 7 سبتمبر 2010 · 6 رد · 1,964 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ،

عدت للسي بلس بلس بعد سنة من هجرانها واستخدام Java ،وكأي شخص جرّب طريقة أفضل في تمرير عدد لا محدود من البارمترات إلى الدوال و التي تقدمها الجافا ، سيجد أن الأمور بحاجة إلى إعادة نظر .

الطريقة التقليدية في لغة " سي " هي استخدام va_list و أخواتها ، بمعنى أننا نتحدث عن macro في لغة " سي " . فمثلاً لو أردنا كتابة دالة تستقبل عدد غير محدود من البارمترات وتقوم بجمع تلك البارمترات/ المعاملات ، ومن ثم إعادة الناتج .

باستخدام Macro

#include <stdarg.h>
#include <iostream>
using namespace std;

template<typename T> T sum(int length, ...)
{
    va_list vList;
    va_start(vList, length);
    T total = 0;
    for(int i = 1; i <= length; i++) {
        int a = va_arg(vList, T);
        total = total + a ;
    }
    va_end(vList);
    return total;
}


int main()
{
    const int NUM_OF_ARGUMENTS  = 3;
    cout << "The summation of : 1,2,3 " << "is:" << sum<int>(NUM_OF_ARGUMENTS,100,300,200)<< endl;
    return 0;
}

الملاحظات :

  • استخدمنا Template لدعم أي نوع من المتغيرات
  • استخدمنا va_list وأخواتها لدعم عدد لامحدود من من البارمترات
  • الكود السابق بشع ، يعتمد على أسلوب لغة سي القديم ، ويقولون أنه غير آمن
  • لاحظ أن أول بارمتر في الدالة sum هو عدد البارمترات .. وهي طريقة سيئة بالمقارنة مع ماتقدمه اللغات الأخرى ، وحتى إن وجدنا طريقة للاستغناء عن أول بارمتر ، فسيبقى شكل الدالة بشع

قارن الكود السابق بطريقة جافا :

static int sum (int ... numbers)
{
   int total = 0;
   for (int i = 0; i < numbers.length; i++)
        total += numbers ;
   return total;
}

كود نظيف .. رائع ، آمن ، مفهوم وقل ما شئت ، ويمكن أن تدعم أي نوع وليس فقط int باستخدام Object أو Generic في لغة جافا . يمكن أن نحاكي طريقة " جافا " في التعامل مع البارمترات الغير محدودة ، باستخدام مصفوفة ديناميكية ، سأستخدم std::vector و يمكن أن تستخدم طرق أفضل و أسرع ، مثل تلك الموجودة في boost .

استخدام std::vector

#include <stdarg.h>
#include <iostream>
#include <vector>
using namespace std;


template<typename T> T sum(const std::vector<T> &numbers)
{
    T total = 0;
    for(int i = 0; i <= numbers.size(); i++) {
        total = total + numbers ;
    }
    return total;
}

int main()
{
    std::vector<int> arguments;
    arguments.push_back(100);
    arguments.push_back(200);
    arguments.push_back(300);
    cout << "The summation of : 1,2,3 " << "is:" << sum<int>(arguments)<< endl;
    return 0;
}

الكود السابق أكثر جمالاً والأهم أنه أكثر مقروئية ، ويمكنك أن تزينه جمالاً بالقراءة عن Boost.Assign

ولكن بعد قياس سرعة كل طريقة على جهاز واحد ذو مواصفات جيدة جداً وباستخدام Release طبعاً وجدت التالي :

  • نجد أن std::vector : تكلفنا 47 جزء من الثانية .
  • بينما Macro : تكلفنا صفر جزء من الثانية :D .

سأضع كود التجربة لاحقاً

واضح جداً أن فرق السرعة لصالح Macro ولكن يمكن استخدام طرق أسرع من std::vector ، ولنجرب شيء تافه نقوم به .. لنجرب أن نصنع " Class " على الماشي ، ليمثل المصفوفة الديناميكة التي نريد ولنستخدمها كبديل لـ std::vector .

استخدام مصفوفة ديناميكية بسيطة DynamicArray

لاحظوا :

#include <stdarg.h>
#include <iostream>
#include <vector>
#include <windows.h>
#include <stdio.h>
#include <assert.h>

using namespace std;

template <typename T>
class DynamicArray
{
	T *a;
	int size; // max size
	int length; // current length
public:
	DynamicArray<T>(int size)
		:size(size),length(0)
	{
		// suppose that there is no exception to throw !
		a = new T[size];
	}
	bool add(const T &value){
		if(length > size) return false;
		a[length++] = value;
		return true;
	}
	const T& get(int index) const{
		assert(index < length );
		return a[index];
	}
	int getLength() const{
		return this->length;
	}
};

template<typename T> T sumDynamicArray(const DynamicArray<T> &numbers)
{
	T total = 0;
	for(int i=0;i<numbers.getLength();i++){
		total = total + numbers.get(i);
	}
	return total;
}


int main()
{
		DynamicArray<int> arr(10);
		arr.add(100);
		arr.add(200);
		arr.add(300);
		cout << sumDynamicArray<int>(arr) << endl ;
	return 0;
}

الآن ، قمت بإعادة حساب تكلفة كل طريقة ، والنتيجة مذهلة !

  • طريقة std::vector : تكلفنا 47 جزء من الثانية .
  • طريقة macro : تكلفنا صفر جزء من الثانية .
  • طريقة DynamicArray : تكلفنا 15 جزء من الثانية ( كأسوأ تقدير ) .

نلاحظ عزيز القارئ ، أننا و صلنا لمرحلة متقدمة جداً من الوصول لمستوى قريب من طريقة Macro وهذا ما دفعني للطمع واستخدام Boost.array ، وكانت تلك القشة التي قصمت ظهر va_list ،

استخدام boost::array

template<typename T,std::size_t E> T sumBoostArray(const boost::array<T,E> &numbers)
{
	T total = 0;
	for(int i=0;i<numbers.size();i++){
		total = total + numbers;
	}
	return total;
}

int main()
{

	boost::array<int,3> arr = { { 100, 200, 300} };
	std::cout << sumBoostArray(arr) << std::endl;
	return 0;
}

الآن ، قمت بإعادة حساب تكلفة كل طريقة ، والنتيجة مذهلة !

  • طريقة std::vector : تكلفنا 47 جزء من الثانية .
  • طريقة macro : تكلفنا صفر جزء من الثانية .
  • طريقة DynamicArray : تكلفنا 15 جزء من الثانية ( كأسوأ تقدير ) .
  • طريقة boost::array : تكلفنا صفر جزء من الثانية !

عيوب الطرق السابقة

الطرق السابقة يعيبها شيء واحد ، وهو أنه لا يمكنك التحكم بنوع كل بارمتر لوحده ، فلا تستطيع مثلاً أن تكتب :

void printListOfArgs(... args){
	for (arg : args){
		cout << arg <<endl;
	}
}
int main()
{

	int i = 10;
	float f = 20.6f;
	char c = 'c';
	std::string s = "string";
	Object obj("Name","age","Addresss");

	printListOfArgs(i,f,c,s,obj);
	return 0;  
}

هناك خيوط قد تقود لحل هذا الإشكال ، لعل boost:tuple والذي سيكون ضمن c++0x على ما اذكر ، ولكن لا أعرف إن كان يمكن استخدامه ليحل المشكلة التي نتحدث عنها في هذا المقال ! حاولت تسخيره لحل الاشكال لكن يبدو أنه عسيراً بعض الشيء .

هل تملك فكرة ؟

من الحلول أيضاً ، Variadic Templates ، وهي من ضمن مواصفات C++0x ، وشرحها Stroustrup في موقعه :

http://www2.research.att.com/~bs/C++0xFAQ.html#variadic-templates

أيضاً / هل تملك فكرة عن هذه الطريقة ؟

الخلاصة

أجد أن استخدام boost::array والتي أظن أنها أصبحت ضمن المقاييس في C++0x ، هي البديل المناسب لـ Macro وذلك من أجل حل مشكلة " الدالة ذات البارمترات الغير محدودة " ، يمكن أن ألخص مزايا هذه الطريقة بالنقاط التالية :

  • استخدام boost::array سريع بما فيه الكفاية لدرجة أنه قد لايوجد فرق إطلاقاً بينه و بين Macro va_list و أخواتها
  • استخدام boost::array يعني كود نظيف ، واضح ، آمن ، ويقوم بالمهمة على أفضل وأكمل وجه
  • باستخدام boost::array ، لن تضيف عبء على المبرمج الذي يريد استخدام ميزة الدوال ذات البامرترات الغير محدودة ، فلن تحتاج إلا إلى أساسيات ++C بعكس va_list والتي تعتبر أسلوب شاذ عن المألوف وتتطلب وقت لاستيعابها و فهمها ، بل أن حتى فهمها بشكل منطقي يبدو أنه مطلب عسير
  • إذا لم تتمكن من استخدام boost::array في مشروعك ، فيمكنك بناء مصفوفة ديناميكية خاصة بك ، وقد تصل لمستوى قريب جداً من boost::array ببضعة أسطر

كود المقارنات

var_arguments.cpp

مكتوب بأسلوب بسيط ، فليس الهدف التأكد 100% من صحة المقارنة ،

تم تطبيق المقارنة على جهاز محمول يحمل هذه المواصفات :

Intel Core 2 Due 2.10 GHz

Ram 3 Giga

Windows XP SP3

Visual C++ 2008 and GCC

أتمنى أن تكون يا مبرمج " سي بلس بلس "، أزلت فكرة استخدام macro لحل مشكلة البارمترات الغير محدودة !

تم تعديل هذه المشاركة بواسطة الشمري في 7 سبتمبر 2010 في 19:48 — السبب: أخطاء إملائية

8

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#2

كنت أفكر بمثل هذا الأمر من منظور C++0x و أحد الحلول يكمن فى استخدام الـ variadic templates (أنا مش عارف هما امتى هينتهوا من مواصفات الإصدار ده :excl:، نفسى اطبق الإمكانيات اللى فيها مع مترجم انتل).

تم تعديل هذه المشاركة بواسطة محمد علاء الدين عبد العزيز في 7 سبتمبر 2010 في 22:47

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

#3

و ماذا عن إستخدام المصفوفات العاديه؟

#4

أظن المواصفات اعتمدت بالفعل Variadic Template من ثلاث سنوات تقريباً .. لكن VC 2010 لم يدعمها حتى الآن كما أعرف ،

حتى Tuple كتبوها بطريقة محدودة ، المفترض أن تدعم عدد لا نهائي من العناصر ،‘ لكن الواقع محدود بعدد معين في VC ( لا أعرف بالظبط كم .. لكن أتوقع أنها 8 عناصر أو 10 .. لا اذكر فقد شاهدت فيديو على قناة MSDN من شهور ) ، يعني " إسكات للمطوّرين " .. إلى أن ينتهوا من Variadic Template،

لكن السؤال : ماهو الفرق بين Variadic Templates و Variadic macros ؟ : ) .

اقتباس
مترجم انتل

؟!

اقتباس
و ماذا عن إستخدام المصفوفات العاديه؟

لم أجرب ، لكن مشكلتها أنك لا تعرف طولها بشكل مباشر ، وبالتالي ستحتاج لمتغير إضافي يحمل الطول .. ورح ندخل بموضوع " الأمن وانتهاك الذاكرة ومشاكل مصفوفات ++C " .

1

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#5

ال Boost::Array تحوى مشكله اخرى وهو ان الحجم compile time constant وهنا نصل لان ال va_list rules :D وليست بشعه ولاشى على فكره

ونسيت ايضا ميزتها الرهيبه الاخرى Syntactic Support

-----------

وقعت فى نسخه عامه من هذه المشكله قبلا وحليتها حل ( له مشاكله هو الاخر :P) لكنه يتلافى العيب الخاص بالانواع الى حد ما :D

الفكره كانت استخدام Vector من ال Boost::Variant ,, ممكن ايضا ال Boost::Any

ميزه ال Any انه اى نوع فعلا لكنه ابطء ومعرفه النوع عذاب

ال Variant أنيق وسريع مشكلته الوحيده ان الانواع التى يمكنك ان تدعمها لازم تكون محدده Compile time (فى حالتى لم تكن مشكله )

1
#6
اقتباس
لكن السؤال : ماهو الفرق بين Variadic Templates و Variadic macros ؟ : ) .

الـ Variadic macros تستخدم مع الماكرو فقط و يتم عمل expand لمحتواها من قبل الـ preprocessor اما الـ Variadic Templates فيتم عمل expand لها من قبل مترجم اللغه نفسه.

اقتباس
مترجم انتل ؟!

انا استخدم مترجم انتل و هو لا يحتوى على دعم كامل لإمكانيات الـ C++0x و ذلك بسبب ان قواعدها لازلت قيد التطوير و ها نحن بعد أن كنا نتكلم عن C++0x اصبحنا نتكلم عن C++1x و لازال الإصدار draft!. هذا الإصدار من اللغه يزداد تعقيدا مع مرور الوقت. أخشى أن الإصدار الجديد لن يكون C++ و لكن لغه جديده تشبه لها و يكون عهد C++ السابق انتهى بإصدار 2003.

اقتباس
و ماذا عن إستخدام المصفوفات العاديه؟

الـ variable argument هى static array و لكنها غير معلومة الحجم لذا تستخدم va_arg للحصول على المعامل الذى تريد و كل ما يفعله فى الحقيقه تحويل مؤشر هذه المنطقه من char* إلى النوع الذى ترغب به، va_list هى إسم بديل لـ char*.

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

تم تعديل هذه المشاركة بواسطة محمد علاء الدين عبد العزيز في 8 سبتمبر 2010 في 01:53

1

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

#7

شكرا ً لك أخي الشمري

موضوع أكثر من رائع

سبق لي ان سألت عنها و بالفعل كان الجواب باستخدام المايكرو و لم أكن بعلم عن الأساليب الأُخرى

فشكرا ً جزيلا ً لك

+1 لآنو فعلا ً بيستاهل

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