【问题标题】:Reuseable function for looping [closed]循环的可重用函数[关闭]
【发布时间】:2021-07-02 11:37:45
【问题描述】:

问题: 我想创建一个可重用的函数,因为在我的代码中有很多行使用相同的代码结构 使用例如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)得到的

【问题讨论】:

  • 你听说过函数指针吗?
  • 是的,以前用过指针,但对指针不太熟练。但我不认为指针是我要找的。或者您是否有我可以阅读的有用资源示例(与此问题主题相关的函数指针)。 GitHub / 其他 StackOF 可以做到。谢谢

标签: c inject reusability


【解决方案1】:

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

在下面的示例中,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);
...

【讨论】:

    【解决方案2】:

    根据 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);
    }
    

    【讨论】:

      【解决方案3】:

      举个具体的例子:

      #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, int *c) { printf("%s: %d\n", __func__, *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);
      }
      

      【讨论】:

        【解决方案4】:

        您可以通过将“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 代码保留在使用它的函数中,如果实际上每段这样的代码只在一个地方使用,这可能是有利的。它还允许该代码和框架代码访问它们出现的函数的局部变量,并在需要时从该函数返回。

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

        【讨论】:

        • 虽然我(几乎)总是喜欢用“几乎可以肯定”之类的词对冲,但“最应得的”是过于慷慨了!
        【解决方案5】:

        函数指针非常适合这个。你可以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

        【讨论】:

          猜你喜欢
          • 2020-10-01
          • 1970-01-01
          • 2017-08-15
          • 2014-03-14
          • 2017-11-10
          • 1970-01-01
          • 1970-01-01
          • 2021-03-13
          • 1970-01-01
          相关资源
          最近更新 更多