Ответ 1
Интересный вопрос, и я взглянул на статью в приведенном ответе (Более функциональное повторное использование в C/С++/ Objective-C с функциями Curried).
Итак, следующий путь к тому, куда вы, возможно, захотите пойти. Я не думаю, что это действительно функция Curried, поскольку я не совсем понимаю, о чем говорит газета, а не как функциональный программист. Однако, выполняя небольшую работу, я нахожу интересное приложение некоторой из этой концепции. Я не уверен, что это то, что вы хотите, с другой стороны, это вроде как мой ум, что вы можете сделать это на C.
Казалось, что есть две проблемы.
Прежде всего, это возможность обрабатывать произвольные вызовы функций с произвольными списками аргументов. Подход, который я принял, состоял в использовании стандартных аргументов аргументов переменной C Library (va_list с функциями va_start(), va_arg() и va_end()), а затем сохранить указатель функции вместе с предоставленными аргументами в область данных, чтобы они затем может быть выполнена позднее. Я заимствовал и изменил, как функция printf()
использует строку формата, чтобы узнать, сколько аргументов и их типов предоставлены.
Следующее - это хранение функции и списка ее аргументов. Я просто использовал структуру с произвольным размером, чтобы попробовать эту концепцию. Это потребует немного больше мысли.
В этой конкретной версии используется массив, который обрабатывается как стек. Существует функция, которую вы используете для подталкивания некоторой произвольной функции с ее аргументами в массив стека, и есть функция, которая выведет верхнюю часть самой функции и ее аргументы из массива стека и выполнит ее.
Однако на самом деле у вас могут быть только произвольные структурные объекты в какой-то коллекции, например хэш-карта, и это может быть очень круто.
Я просто заимствовал пример обработчика сигнала из статьи, чтобы показать, что концепция будет работать с таким типом приложения.
Итак, вот исходный код, и я надеюсь, что это поможет вам придумать решение.
Вам нужно будет добавить другие случаи в коммутатор, чтобы иметь возможность обрабатывать другие типы аргументов. Я просто сделал несколько для доказательства концепции.
Также это не делает функцию, вызывающую функцию, хотя это казалось бы на поверхности довольно простым расширением. Как я уже сказал, я не полностью понимаю эту ситуацию.
#include <stdarg.h>
#include <string.h>
// a struct which describes the function and its argument list.
typedef struct {
void (*f1)(...);
// we have to have a struct here because when we call the function,
// we will just pass the struct so that the argument list gets pushed
// on the stack.
struct {
unsigned char myArgListArray[48]; // area for the argument list. this is just an arbitray size.
} myArgList;
} AnArgListEntry;
// these are used for simulating a stack. when functions are processed
// we will just push them onto the stack and later on we will pop them
// off so as to run them.
static unsigned int myFunctionStackIndex = 0;
static AnArgListEntry myFunctionStack[1000];
// this function pushes a function and its arguments onto the stack.
void pushFunction (void (*f1)(...), char *pcDescrip, ...)
{
char *pStart = pcDescrip;
AnArgListEntry MyArgList;
unsigned char *pmyArgList;
va_list argp;
int i;
char c;
char *s;
void *p;
va_start(argp, pcDescrip);
pmyArgList = (unsigned char *)&MyArgList.myArgList;
MyArgList.f1 = f1;
for ( ; *pStart; pStart++) {
switch (*pStart) {
case 'i':
// integer argument
i = va_arg(argp, int);
memcpy (pmyArgList, &i, sizeof(int));
pmyArgList += sizeof(int);
break;
case 'c':
// character argument
c = va_arg(argp, char);
memcpy (pmyArgList, &c, sizeof(char));
pmyArgList += sizeof(char);
break;
case 's':
// string argument
s = va_arg(argp, char *);
memcpy (pmyArgList, &s, sizeof(char *));
pmyArgList += sizeof(char *);
break;
case 'p':
// void pointer (any arbitray pointer) argument
p = va_arg(argp, void *);
memcpy (pmyArgList, &p, sizeof(void *));
pmyArgList += sizeof(void *);
break;
default:
break;
}
}
va_end(argp);
myFunctionStack[myFunctionStackIndex] = MyArgList;
myFunctionStackIndex++;
}
// this function will pop the function and its argument list off the top
// of the stack and execute it.
void doFuncAndPop () {
if (myFunctionStackIndex > 0) {
myFunctionStackIndex--;
myFunctionStack[myFunctionStackIndex].f1 (myFunctionStack[myFunctionStackIndex].myArgList);
}
}
// the following are just a couple of arbitray test functions.
// these can be used to test that the functionality works.
void myFunc (int i, char * p)
{
printf (" i = %d, char = %s\n", i, p);
}
void otherFunc (int i, char * p, char *p2)
{
printf (" i = %d, char = %s, char =%s\n", i, p, p2);
}
void mySignal (int sig, void (*f)(void))
{
f();
}
int main(int argc, char * argv[])
{
int i = 3;
char *p = "string";
char *p2 = "string 2";
// push two different functions on to our stack to save them
// for execution later.
pushFunction ((void (*)(...))myFunc, "is", i, p);
pushFunction ((void (*)(...))otherFunc, "iss", i, p, p2);
// pop the function that is on the top of the stack and execute it.
doFuncAndPop();
// call a function that wants a function so that it will execute
// the current function with its argument lists that is on top of the stack.
mySignal (1, doFuncAndPop);
return 0;
}
Дополнительный бит удовольствия, который вы можете иметь с этим, заключается в использовании функции pushFunction()
в функции, которая вызывается doFuncAndPop()
, чтобы иметь другую функцию, которую вы можете поместить в стек с ее аргументами.
Например, если вы измените функцию otherFunc()
в источнике выше, чтобы выглядеть следующим образом:
void otherFunc (int i, char * p, char *p2)
{
printf (" i = %d, char = %s, char =%s\n", i, p, p2);
pushFunction ((void (*)(...))myFunc, "is", i+2, p);
}
если вы затем добавите еще один вызов doFuncAndPop()
, вы увидите, что выполняется первый otherFunc()
, тогда выполняется вызов myFunc()
, который был запущен в otherFunc()
, а затем, наконец, вызов myFunc()
, который был нажат в main ()
.
ИЗМЕНИТЬ 2:
Если мы добавим следующую функцию, это выполнит все функции, которые были помещены в стек. Это позволит нам в основном создать небольшую программу, нажав функции и аргументы на наш стек, а затем выполнив серию вызовов функций. Эта функция также позволит нам нажимать функцию без каких-либо аргументов, а затем нажимать некоторые аргументы. Когда функция popping отключается от нашего стека, если блок аргументов не имеет допустимого указателя функции, тогда мы должны поместить этот список аргументов в блок аргументов в верхней части стека и затем выполнить его. Аналогичное изменение может быть сделано и для функции doFuncAndPop()
. И если мы используем операцию pushFunction() в выполняемой функции, мы можем сделать некоторые интересные вещи.
На самом деле это может быть основой для Threaded Interpreter.
// execute all of the functions that have been pushed onto the stack.
void executeFuncStack () {
if (myFunctionStackIndex > 0) {
myFunctionStackIndex--;
// if this item on the stack has a function pointer then execute it
if (myFunctionStack[myFunctionStackIndex].f1) {
myFunctionStack[myFunctionStackIndex].f1 (myFunctionStack[myFunctionStackIndex].myArgList);
} else if (myFunctionStackIndex > 0) {
// if there is not a function pointer then assume that this is an argument list
// for a function that has been pushed on the stack so lets execute the previous
// pushed function with this argument list.
int myPrevIndex = myFunctionStackIndex - 1;
myFunctionStack[myPrevIndex].myArgList = myFunctionStack[myFunctionStackIndex].myArgList;
}
executeFuncStack();
}
}
ИЗМЕНИТЬ 3:
Затем мы вносим изменения в pushFunc()
для обработки двойного со следующим дополнительным переключателем:
case 'd':
{
double d;
// double argument
d = va_arg(argp, double);
memcpy (pmyArgList, &d, sizeof(double));
pmyArgList += sizeof(double);
}
break;
Таким образом, с помощью этой новой функции мы можем сделать что-то вроде следующего. Прежде всего создайте две наши функции, похожие на исходный вопрос. Мы будем использовать pushFunction() внутри одной функции для ввода аргументов, которые затем используются следующей функцией в стеке.
double f1 (double myDouble)
{
printf ("f1 myDouble = %f\n", myDouble);
return 0.0;
}
double g2 (double myDouble) {
printf ("g2 myDouble = %f\n", myDouble);
myDouble += 10.0;
pushFunction (0, "d", myDouble);
return myDouble;
}
В новой версии мы используем наши новые функции со следующими операциями:
double xDouble = 4.5;
pushFunction ((void (*)(...))f1, 0);
pushFunction ((void (*)(...))g2, "d", xDouble);
executeFuncStack();
Эти операторы будут выполнять сначала функцию g2()
со значением 4.5, а затем функция g2()
будет выталкивать свое возвращаемое значение в наш стек, который будет использоваться функцией f1()
, которая была сначала нажата на наш стек.