سؤال

أقول لقد مؤشر على وظيفة _stack_push(stack* stk, void* el).كنت تريد أن تكون قادرة على الاتصال curry(_stack_push, my_stack) والحصول على وظيفة أن يأخذ فقط void* el.لم أستطع التفكير في طريقة للقيام بذلك ، حيث ج لا تسمح runtime تعريف الدالة ، ولكن أعرف أن هناك الآن أذكى الناس من لي هنا :).أي أفكار ؟

هل كانت مفيدة؟

المحلول

ولقد وجدت ورقة لوران دامي أن يناقش التمشيط في C / C ++ / الهدف-C:

إعادة استخدام أكثر وظيفية في C / C ++ / الهدف جيم مع وظائف بالكاري

ومن الاهتمام لكيفية تنفيذه في C:

<اقتباس فقرة>   

وتنفيذنا الحالي يستخدم C الموجودة يبني لإضافة آلية الضمادة. وكان هذا أسهل بكثير للقيام من تعديل المترجم، ويكفي لإثبات مصلحة الضمادة. هذا النهج لديه اثنين من العيوب، ولكن. أولا، يمكن ظائف بالكاري لا يكون المسحوب الكتابة، وبالتالي تتطلب الاستخدام الحذر لتفادي الأخطاء. الثانية، وظيفة الكاري لا يمكن أن نعرف حجم حججها، والتهم لهم كما لو كانوا جميعا من حجم عدد صحيح.

لا يحتوي على الورقة تنفيذ curry()، ولكن يمكنك أن تتخيل كيف يتم تنفيذ ذلك باستخدام مؤشرات و ظائف variadic .

نصائح أخرى

ودول مجلس التعاون الخليجي يوفر امتدادا للتعريف الوظائف المتداخلة. على الرغم من أن هذا ليس معيار ايزو C، وهذا قد يكون من بعض الاهتمام، لأنه تمكن من الإجابة على السؤال مريح جدا. باختصار، يمكن وظيفة متداخلة الوصول إلى المتغيرات والمؤشرات المحلية وظيفة الوالدين لهم يمكن إرجاع بواسطة الدالة الأم كذلك.

وهنا يبعد مسافة قصيرة، لا تحتاج إلى شرح سبيل المثال:

#include <stdio.h>

typedef int (*two_var_func) (int, int);
typedef int (*one_var_func) (int);

int add_int (int a, int b) {
    return a+b;
}

one_var_func partial (two_var_func f, int a) {
    int g (int b) {
        return f (a, b);
    }
    return g;
}

int main (void) {
    int a = 1;
    int b = 2;
    printf ("%d\n", add_int (a, b));
    printf ("%d\n", partial (add_int, a) (b));
}

وغير ذلك هناك قيد على هذا البناء. اذا واصلتم مؤشر إلى وظيفة الناتجة عن ذلك، كما هو الحال في

one_var_func u = partial (add_int, a);

وقد ينتج u(0) استدعاء دالة في سلوك غير متوقع، كما a المتغير الذي يقرأ u دمرت بعد partial إنهاؤها.

هذا القسم من وثائق دول مجلس التعاون الخليجي.

وهنا أول تخميني من على قمة رأسي (قد لا يكون أفضل حل).

وظيفة الكاري يمكن تخصيص بعض الذاكرة من كومة، ووضع قيم المعلمات إلى أن الذاكرة المخصصة كومة. هو خدعة ثم لوظيفة عاد لمعرفة أنه من المفترض أن قراءة معالمها من أن الذاكرة المخصصة كومة. إذا كان هناك حالة واحدة فقط من وظيفة عاد، ثم مؤشر إلى تلك المعايير يمكن تخزينها في المفرد / العالمي. إلا إذا كان هناك أكثر من مثيل واحد من وظيفة عاد، ثم أعتقد أن الكاري يحتاج إلى إنشاء كل مثيل وظيفة عاد <م> في الذاكرة المخصصة كومة (عن طريق كتابة أكواد العمليات مثل "الحصول على هذا المؤشر إلى معلمات "،" دفع المعلمات "، و" تتذرع بأن وظيفة أخرى "في الذاكرة المخصصة كومة). في هذه الحالة تحتاج إلى الحذر سواء الذاكرة المخصصة هي القابلة للتنفيذ، وربما (لا أعرف) حتى يكون خائفا من برامج مكافحة الفيروسات.

هنا هو نهج إلى تقوم التمشيط في C.في حين أن هذا التطبيق نموذج باستخدام C++ iostream الناتج عن الراحة هو كل ج أسلوب الترميز.

مفتاح هذا النهج هو أن يكون struct الذي يحتوي على مجموعة من unsigned char و هذه مجموعة يستخدم لبناء حجة قائمة على وظيفة.وظيفة تسمى المحدد باعتبارها واحدة من الحجج التي دفعت إلى الصفيف.مما أدى مجموعة ثم تعطى الوكيل الذي ينفذ فعلا إغلاق وظيفة والحجج.

في هذا المثال أقدم زوجين من نوع محدد وظائف المساعد في دفع الحجج في إغلاق فضلا عن العامة pushMem() وظيفة لدفع struct أو غيرها من الذاكرة المنطقة.

هذا النهج يتطلب تخصيص مساحة الذاكرة التي تستخدم بعد ذلك في إغلاق البيانات.سيكون من الأفضل استخدام كومة الذاكرة هذه المنطقة حيث أن إدارة الذاكرة لا تصبح مشكلة.هناك أيضا مسألة كيفية كبيرة لجعل إغلاق ذاكرة التخزين المنطقة حتى لا يكون هناك مساحة كافية اللازمة الحجج ولكن ليست كبيرة حيث أن المساحة الزائدة في الذاكرة أو على المكدس هو تناول المساحة غير المستخدمة.

أنا جربت مع استخدام مختلف قليلا تعريف إغلاق البنية التي تحتوي على حقل إضافي من أجل المستخدمة حاليا حجم المصفوفة لتخزين إغلاق البيانات.هذا مختلف إغلاق البنية ثم يستخدم مع تعديل وظائف المساعد الذي يزيل الحاجة إلى المستخدم من وظائف المساعد للمحافظة على الخاصة بهم unsigned char * مؤشر عند إضافة الحجج إلى إغلاق البنية.

الملاحظات والمحاذير

المثال التالي البرنامج تم تجميعها واختبارها مع Visual Studio 2013.الناتج من هذه العينة المقدمة أدناه.أنا غير متأكد حول استخدام دول مجلس التعاون الخليجي أو ضجيج مع هذا المثال ولا أنا متأكد أن القضايا التي يمكن أن ينظر إليها مع 64 بت مترجم وأنا تحت انطباع بأن الاختبار كان مع 32 بت التطبيق.كما أن هذا النهج من شأنه أن يبدو فقط إلى العمل مع الوظائف التي تستخدم معيار ج الإعلان في وظيفة الدعوة مقابض ظهرت الحجج من المكدس بعد المستدعى يعود (__cdecl و لا __stdcall في نظام التشغيل Windows API).

وبما أننا في بناء الحجة قائمة في وقت التشغيل ومن ثم استدعاء وكيل وظيفة هذا النهج لا تسمح مترجم لأداء الاختيار على الحجج.هذا يمكن أن يؤدي إلى الغامض الفشل بسبب متطابقة أنواع المعلمة التي المترجم غير قادر على العلم.

على سبيل المثال تطبيق

// currytest.cpp : Defines the entry point for the console application.
//
// while this is C++ usng the standard C++ I/O it is written in
// a C style so as to demonstrate use of currying with C.
//
// this example shows implementing a closure with C function pointers
// along with arguments of various kinds. the closure is then used
// to provide a saved state which is used with other functions.

#include "stdafx.h"
#include <iostream>

// notation is used in the following defines
//   - tname is used to represent type name for a type
//   - cname is used to represent the closure type name that was defined
//   - fname is used to represent the function name

#define CLOSURE_MEM(tname,size) \
    typedef struct { \
        union { \
            void *p; \
            unsigned char args[size + sizeof(void *)]; \
        }; \
    } tname;

#define CLOSURE_ARGS(x,cname) *(cname *)(((x).args) + sizeof(void *))
#define CLOSURE_FTYPE(tname,m) ((tname((*)(...)))(m).p)

// define a call function that calls specified function, fname,
// that returns a value of type tname using the specified closure
// type of cname.
#define CLOSURE_FUNC(fname, tname, cname) \
    tname fname (cname m) \
    { \
        return ((tname((*)(...)))m.p)(CLOSURE_ARGS(m,cname)); \
    }

// helper functions that are used to build the closure.
unsigned char * pushPtr(unsigned char *pDest, void *ptr) {
    *(void * *)pDest = ptr;
    return pDest + sizeof(void *);
}

unsigned char * pushInt(unsigned char *pDest, int i) {
    *(int *)pDest = i;
    return pDest + sizeof(int);
}

unsigned char * pushFloat(unsigned char *pDest, float f) {
    *(float *)pDest = f;
    return pDest + sizeof(float);
}

unsigned char * pushMem(unsigned char *pDest, void *p, size_t nBytes) {
    memcpy(pDest, p, nBytes);
    return pDest + nBytes;
}


// test functions that show they are called and have arguments.
int func1(int i, int j) {
    std::cout << " func1 " << i << " " << j;
    return i + 2;
}

int func2(int i) {
    std::cout << " func2 " << i;
    return i + 3;
}

float func3(float f) {
    std::cout << " func3 " << f;
    return f + 2.0;
}

float func4(float f) {
    std::cout << " func4 " << f;
    return f + 3.0;
}

typedef struct {
    int i;
    char *xc;
} XStruct;

int func21(XStruct m) {
    std::cout << " fun21 " << m.i << " " << m.xc << ";";
    return m.i + 10;
}

int func22(XStruct *m) {
    std::cout << " fun22 " << m->i << " " << m->xc << ";";
    return m->i + 10;
}

void func33(int i, int j) {
    std::cout << " func33 " << i << " " << j;
}

// define my closure memory type along with the function(s) using it.

CLOSURE_MEM(XClosure2, 256)           // closure memory
CLOSURE_FUNC(doit, int, XClosure2)    // closure execution for return int
CLOSURE_FUNC(doitf, float, XClosure2) // closure execution for return float
CLOSURE_FUNC(doitv, void, XClosure2)  // closure execution for void

// a function that accepts a closure, adds additional arguments and
// then calls the function that is saved as part of the closure.
int doitargs(XClosure2 *m, unsigned char *x, int a1, int a2) {
    x = pushInt(x, a1);
    x = pushInt(x, a2);
    return CLOSURE_FTYPE(int, *m)(CLOSURE_ARGS(*m, XClosure2));
}

int _tmain(int argc, _TCHAR* argv[])
{
    int k = func2(func1(3, 23));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    XClosure2 myClosure;
    unsigned char *x;

    x = myClosure.args;
    x = pushPtr(x, func1);
    x = pushInt(x, 4);
    x = pushInt(x, 20);
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func1);
    x = pushInt(x, 4);
    pushInt(x, 24);               // call with second arg 24
    k = func2(doit(myClosure));   // first call with closure
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;
    pushInt(x, 14);              // call with second arg now 14 not 24
    k = func2(doit(myClosure));  // second call with closure, different value
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    k = func2(doitargs(&myClosure, x, 16, 0));  // second call with closure, different value
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    // further explorations of other argument types

    XStruct xs;

    xs.i = 8;
    xs.xc = "take 1";
    x = myClosure.args;
    x = pushPtr(x, func21);
    x = pushMem(x, &xs, sizeof(xs));
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    xs.i = 11;
    xs.xc = "take 2";
    x = myClosure.args;
    x = pushPtr(x, func22);
    x = pushPtr(x, &xs);
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func3);
    x = pushFloat(x, 4.0);

    float dof = func4(doitf(myClosure));
    std::cout << " main (" << __LINE__ << ") " << dof << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func33);
    x = pushInt(x, 6);
    x = pushInt(x, 26);
    doitv(myClosure);
    std::cout << " main (" << __LINE__ << ") " << std::endl;

    return 0;
}

اختبار الإخراج

الناتج من هذه العينة البرنامج.الرقم بين قوسين هو رقم السطر في الرئيسية حيث استدعاء دالة.

 func1 3 23 func2 5 main (118) 8
 func1 4 20 func2 6 main (128) 9
 func1 4 24 func2 6 main (135) 9
 func1 4 14 func2 6 main (138) 9
 func1 4 16 func2 6 main (141) 9
 fun21 8 take 1; func2 18 main (153) 21
 fun22 11 take 2; func2 21 main (161) 24
 func3 4 func4 6 main (168) 9
 func33 6 26 main (175)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top