可重复使用的循环函数

问题描述

问题: 我想创建一个可重用的函数,因为在我的代码中很多行使用相同的代码结构 使用例如 if { if { `Only here's the different` } }代码。当然模式和这个不一样,这里以这个为例。 我一直在使用 Laravel 等框架编写代码,有一个名为 SLOT 的指令 有什么办法可以在 for 循环中间注入代码吗?或者任何与 C 编程中的 SLOT 相同的东西

示例代码

void functionname() {
  for (int i=0; i < total_count; i++) {
    SELECT THE ITEM (i)
    if (a == b) return;
    if (c) {
      CODE INJECT HERE
    }
  }
}

前面忘记说了,上面编码中的a、b、c等是从ITEM(i)中得到的

解决方法

您应该使用回调。即您应该发送一个函数指针(即您要执行的函数的地址)并使用该指针在循环内执行该函数。

在下面的示例中,p 是一个指向函数的指针,该函数采用 const char * 作为参数并返回 int

int (*p)(const char *s) ;

注意:所有作为参数传递、用作回调的函数必须具有相同的原型(这就是为什么这些函数通常被声明为采用通用指针参数 void * 来接受您必须发送给函数的任何内容) .

因此,对于您的示例以及将 void * 作为参数并返回 void * 的函数,并且使用 param 定义要提供给函数的参数,这为我们提供了以下代码:

void functionname(void *(*func)(void *)) {
  for (int i=0; i < total_count; i++) {
    SELECT THE ITEM (i)
    if (a == b) return;
    if (c) {
      func(&param);
    }
  }
}

你可以用任何尊重原型的函数调用你的函数......例如:

void *my_custom_function(void *param) {
...
}
...
functionname(my_custom_function);
...
,

正如 KamilCik 在评论中所建议的,使用函数指针

void functionname(void *fx)(void)) {
  for (int i=0; i < total_count; i++) {
    SELECT THE ITEM (i)
    if (a == b) return;
    if (c) {
      //CODE INJECT HERE
      fx();
    }
  }
}

然后像这样使用

void foo(void) { puts("foo() called"); }
void bar(void) { puts("bar() called"); }

int main(void) {
    functionname(foo);
    functionname(bar);
}
,

举个具体的例子:

#include <stdio.h>

int a = 1;
int b = 2;
typedef void (*selector)(int,int *);
typedef void (*injector)(void);

void select1(int x,int *c) { printf("%s: %d\n",__func__,*c = x); }
void select2(int x,*c = x); }
void inject1(void) { printf("%s\n",__func__); }
void inject2(void) { printf("%s\n",__func__); }

void
functionname(size_t total_count,selector SELECT_THE_ITEM,injector CODE_INJECT_HERE )
{
        for (size_t i=0; i < total_count; i++) {
                int c;
                SELECT_THE_ITEM (i,&c);
                if (a == b) return;
                if (c) {
                        CODE_INJECT_HERE();
                }
        }
}

int
main(void)
{
        functionname(2,select1,inject1);
        functionname(3,select2,inject2);
}
,

函数指针对此非常有用。您可以typedef您想要支持的函数签名。示例:

/* A signature for condition checking functions,taking a "void*" argument
   and returning true or false */
typedef bool(*cond_check_t)(void*);

/* A signature for functions to execute if a condition is met. This takes a
   "void*" argument but you decide what you need */
typedef void(*exec_t)(void*);

您可以将这两个打包成一个 struct 以形成一对:

typedef struct {
    cond_check_t checker;
    exec_t executor;
} check_exec_t;

然后,另一个 struct 用于保留一堆这些条件和执行器对:

typedef struct {
    size_t size;
    size_t capacity;
    check_exec_t *conditionals;
} cond_pack_t;

然后创建用于添加检查器和执行器的支持函数以及处理这些打包检查器和执行器之一的函数。

cond_pack_t* cond_pack_create(size_t capacity) {
    cond_pack_t* cp = malloc(sizeof(*cp));
    if(cp) {
        cp->conditionals = malloc(sizeof(*cp->conditionals) * capacity);
        if(cp->conditionals) {
            cp->size = 0;
            cp->capacity = capacity;
        } else {
            free(cp);
            cp = NULL;
        }
    }
    return cp;
}

void cond_pack_destroy(cond_pack_t *cp) {
    free(cp->conditionals);
    free(cp);
}

bool cond_pack_add(cond_pack_t *cp,cond_check_t checker,exec_t executor) {
    if(cp->size == cp->capacity) return false;
    cp->conditionals[cp->size].checker = checker;
    cp->conditionals[cp->size].executor = executor;
    ++cp->size;
    return true;
}

void cond_pack_process(cond_pack_t *cp) {
    for(size_t i = 0; i < cp->size; ++i) {
        if(cp->conditionals[i].checker(NULL)) { /* execute checker */
            cp->conditionals[i].executor(NULL); /* execute executor */
        }
    }
}

这样,使用示例可能如下所示

//---
bool some_check(void *foo) {
    return true;
}
void some_executor(void *foo) {
    printf("some_executor\n");
}

bool some_other_check(void *foo) {
    return false;
}
void some_other_executor(void *foo) {
    printf("some_other_executor\n");
}

int main() {
    cond_pack_t *cp = cond_pack_create(10);
    if(cp) {
        cond_pack_add(cp,some_check,some_executor);
        cond_pack_add(cp,some_other_check,some_other_executor);
        
        cond_pack_process(cp); /* run all the checkers / executors */

        cond_pack_destroy(cp);
    }
}

Demo

,

您可以通过将“CODE INJECT HERE”定义为函数体并传递指向该函数的指针来执行您的要求:

void functionname(void (*inject)(void)) {
  for (int i=0; i < total_count; i++) {
    SELECT THE ITEM (i)
    if (a == b) return;
    if (c) {
        inject();
    }
  }
}

void do_something(void) {
    CODE INJECT HERE
}

void do_something_else(void) {
    OTHER CODE INJECT HERE
}

int main(void) {
    functionname(do_something));
    functionname(do_something_else));
}

但是请注意,这不是与宏提供的相同意义上的简单代码注入。特别是,do_something()do_something_else() 的执行不会看到 main()functionname() 的局部变量,而 do_* 函数只能从自身返回,而不是来自更上游的调用者。前者可以通过将参数传递给 do_* 函数(它们必须准备接受)在一定程度上得到缓解。


另一种替代方法是使用宏而不是函数来提供通用框架。它看起来像这样:

#define frame_it(x) do {                \
  for (int i=0; i < total_count; i++) { \
    SELECT THE ITEM (i)                 \
    if (a == b) return;                 \
    if (c) {                            \
        x                               \
    }                                   \
  }                                     \
} while (0)

int main(void) {
    frame_it(
        CODE INJECT HERE
    );
    frame_it(
        OTHER CODE INJECT HERE
    );
}

这将 CODE INJECT HERE 代码保留在使用它的函数中,如果实际上每段这样的代码只在一个地方使用,这可能是有利的。它还允许该代码和框架代码访问它们出现的函数的局部变量,并在需要时从该函数返回。

然而,宏编程因其容易出错且难以阅读和调试而赢得了当之无愧的声誉。您的特殊需求可能是这种方法可以很好地满足的需求,但不要轻易选择这个方向。