【问题标题】:Iterators for different structs? [closed]不同结构的迭代器? [关闭]
【发布时间】:2016-06-09 09:23:35
【问题描述】:

如果我们有几个数据结构,比如:

typedef struct {
   int par1;
   //...
   int parn1;
} struct1;

typedef struct {
   int par1;
   //...
   int parn2;
} struct2;

(通常考虑字段类型可能不同)为此类结构的字段抽象迭代器是否有意义?

这是实际使用的东西吗?

我想到的是(或多或少的伪代码):

typedef struct {
   int par1;
   //...
   int parn1;
} struct1;

typedef struct {
   int par1;
   //...
   int parn2;
} struct2;

typedef struct {
   int* iterator_ptr;
   int curr_pos;
   int n_field;
} struct_iterator;

typedef union {
   struct1 s1;
   struct2 s2;
} generic_struct;

typedef enum {
   label_struct_1,
   label_struct_2,
   n_struct
} struct_label;

struct_iterator get_iterator(generic_struct* s,struct_label label) {
   struct_iterator it;
   if(label == label_struct_1) {
      it.iterator_ptr = s->par1;
      it.curr_pos = 0;
      it.n_fields = n1;
   } else if(label == label_struct_2) {
      it.iterator_ptr = s->par1;
      it.curr_pos = 0;
      it.n_fields = n2;
   } else {
      //something else, maybe other structures to handle
   }
   return it;
 }

目的基本上是为了代码重构,我有一个糟糕的 C 代码,我想让它更具可读性和更容易理解。我有像上面展示的那样的结构,以及在这些结构上运行的不同算法,高级算法实际上是相同的,但是与其编写带有要专门化的点的通用版本,不如直接编写专门的版本这样的算法。在这种算法的步骤中,有一个通过结构参数的迭代,因为这些结构的字段数量可能不同,但在某些情况下类型是相同的,我想到的是抽象概念,试图实现一个迭代器.

我知道这听起来可能没有必要,但是我想要实现的是代码重用的最简单方法。比如“如果你想扩展代码,只需用这些操作实现这个结构,你就不需要担心其他事情”。

我想出的例子是为了清楚起见,它不是实际的代码,我想了解这个想法是否有意义。

代码是用C写的,我说的大部分概念都是用C++实现的,但是我不得不用C。

实际有效的代码:

文件 data_structure_algorithm_1.h:

#ifndef HEADER_1_H
#define HEADER_1_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
   int data_1;
   int data_2;
   int data_3;
} struct_1;

void operation_1_struct_1(struct_1* s, int field) {
   if(field == 0) {
      s->data_1 = 1;
   } else if(field == 1) {
      s->data_2 = s->data_1*2 + 3;
   } else {
      s->data_3 = (s->data_1 + s->data_2)/2;
   }
}

void operation_2_struct_1(struct_1* s) {
   printf("s->data_1 = %d\n",s->data_1);
   printf("s->data_2 = %d\n",s->data_2);
   printf("s->data_3 = %d\n",s->data_3);
}

data_structure_algorithm_2.h:

#ifndef HEADER_2_H
#define HEADER_2_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
   int data_1;
   int data_2;
   int data_3;
   int data_4;
   int data_5;
   int data_6;
} struct_2; 

void operation_1_struct_2(struct_2* s, int field) {
   if(field == 0) {
      s->data_1 = 1;
   } else if(field == 1) {
      s->data_2 = s->data_1 - 3;
   } else if(field == 2) {
      s->data_3 = (s->data_1 - s->data_2)/2;
   } else if(field == 3) {
      s->data_4 = s->data_3 - s->data_2;
   } else {
      s->data_5 = 1;
      s->data_6 = 9;
   }
}

void operation_2_struct_2(struct_2* s) {
   printf("s->data_1 = %d\n",s->data_1);
   printf("s->data_2 = %d\n",s->data_2);
   printf("s->data_3 = %d\n",s->data_3);
   printf("s->data_4 = %d\n",s->data_4);
   printf("s->data_5 = %d\n",s->data_5);
   printf("s->data_6 = %d\n",s->data_6);
}

#endif

迭代器.h:

#ifndef HEADER_3_H
#define HEADER_3_H
#include "data_structure_algorithm_1.h"
#include "data_structure_algorithm_2.h" 

typedef enum {
   label_struct_1,
   label_struct_2,
   n_struct } label_struct;

typedef union {
   struct_1 s1;
   struct_2 s2;
} generic_struct;

void operation_1_struct(generic_struct *s, int index, label_struct label) {
   switch(label) {
      case label_struct_1: {
         operation_1_struct_1(&(s->s1),index);
         break;
      }
      case label_struct_2: {
         operation_1_struct_2(&(s->s2),index);
         break;
      }
   }
}

void operation_2_struct(generic_struct *s, label_struct label) {
   switch(label) {
      case label_struct_1: {
         operation_2_struct_1(&(s->s1));
         break;
      }
      case label_struct_2: {
         operation_2_struct_2(&(s->s2));
         break;
      }
   }
}

typedef struct {
   int *iterator_ptr;
   int curr_pos;
   int size;
} iterator;

iterator get_iterator_s1(struct_1* s1) {
   iterator it;
   it.iterator_ptr = &(s1->data_1);
   it.curr_pos = 0;
   it.size = 3;
   return it;
}

iterator get_iterator_s2(struct_2 *s2) {
   iterator it;
   it.iterator_ptr = &(s2->data_1);
   it.curr_pos = 0;
   it.size = 6;
   return it;
}

iterator get_iterator(generic_struct* s, label_struct label) {
   switch(label) {
      case label_struct_1: {
         return get_iterator_s1(&(s->s1));
         break;
      }
      case label_struct_2: {
         return get_iterator_s2(&(s->s2));
         break;
      }
   }
}

//I don't want to modify this, because this function could be huge
void algorithm(generic_struct* s, label_struct label) {
   iterator it;

   it = get_iterator(s,label);

   while(it.curr_pos != it.size) {
      operation_1_struct(s,it.curr_pos,label);
      it.curr_pos++;
   }

   operation_2_struct(s,label);
}

main.c:

   #include "iterator.h"
   #include <string.h>
   #include <assert.h>

   int main(int argc, char** argv) {

   generic_struct strct;
   label_struct ls;
   int choice;

   assert(argc <= 2);

   if(argc == 1) {
      while(1) {
         printf("Choose the test you'd like to perform:\n");
         printf("1. struct_1;\n");
         printf("2. struct_2;\n");
         printf("Choice: "); scanf("%d",&choice);
         if(choice < 1 || choice > 2) {
            printf("Choice not valid... please try again\n");
         } else {
            if(choice == 1) ls = label_struct_1;
            else ls = label_struct_2;
            break;
         }
      }
   }
   else {
      if(strcmp(argv[1],"struct_1") == 0) {
         ls = label_struct_1;
      } else if(strcmp(argv[1],"struct_2") == 0) {
         ls = label_struct_2;
      }
   }

   algorithm(&strct,ls);

   return 0;
}

我现在想让过程“算法”在具有类似行为的结构上工作,我只需添加一个类似的文件“data_structure_algorithm_3.h”并更新联合和 switch 语句。

请记住,这是一个非常小的示例(可能是一种小型框架)。

我试图理解的是天气与否这种方法在更大的背景下是否有任何好处(尝试想象我的功能更复杂,并且调用所有这些类似算法的过程算法“巨大”),是否更方便重写“算法”的特殊版本或编写算法调用的几个小函数?

我希望这次我的观点是正确的。

更新

从网络资源和 SO 中的其他一些答案中汲取灵感,您对以下内容有何看法?:

base.h:

#ifndef BASE_H_
#define BASE_H_

typedef struct base_struct base_struct;

typedef struct {
    int *ptr;
    int curr;
    int size;
} iterator;

typedef struct {
    void (*operation_1)(struct base_struct* b, int index);
    void (*operation_2)(struct base_struct* b);
    iterator (*get_iterator)(struct base_struct* b);
} vtable;

struct base_struct {
    vtable* vtbl;
};

void procedure_1(base_struct *b, int index) {
    b->vtbl->operation_1(b, index);
}

void procedure_2(base_struct *b) {
    b->vtbl->operation_2(b);
}

iterator get_iterator_proc(base_struct* b) {
    return b->vtbl->get_iterator(b);
}

void algorithm(base_struct* s) {
    iterator it;

    it = get_iterator_proc(s);

    while (it.curr != it.size) {
        procedure_1(s, it.curr);
        it.curr++;
    }

    procedure_2(s);
}


#endif /* BASE_H_ */

data_structure_1.h:

#ifndef HEADER_1_H
#define HEADER_1_H
#include "base.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    base_struct base;
    int data_1;
    int data_2;
    int data_3;
} struct_1;

void operation_1_struct_1(base_struct* b, int field) {
    struct_1* s = (struct_1*)b;
    if (field == 0) {
        s->data_1 = 1;
    } else if (field == 1) {
        s->data_2 = s->data_1 * 2 + 3;
    } else {
        s->data_3 = (s->data_1 + s->data_2) / 2;
    }
}

void operation_2_struct_1(base_struct* b) {
    struct_1* s = (struct_1*)b;
    printf("s->data_1 = %d\n", s->data_1);
    printf("s->data_2 = %d\n", s->data_2);
    printf("s->data_3 = %d\n", s->data_3);
}

iterator get_iterator_struct_1(base_struct* b) {
    iterator it;
    struct_1 *s = (struct_1*)b;
    it.ptr = &(s->data_1);
    it.curr = 0;
    it.size = 3;
    return it;
}

vtable vtbl_struct_1 = {&operation_1_struct_1,&operation_2_struct_1,&get_iterator_struct_1};

void init_struct_1(struct_1* s) {
    s->base.vtbl = &vtbl_struct_1;
}

#endif /* DATA_STRUCTURE_1_H_ */

data_structure_2.h

#ifndef HEADER_2_H
#define HEADER_2_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    base_struct base;
    int data_1;
    int data_2;
    int data_3;
    int data_4;
    int data_5;
    int data_6;
} struct_2;

void operation_1_struct_2(base_struct* b, int field) {
    struct_2 *s = (struct_2*)b;
    if (field == 0) {
        s->data_1 = 1;
    } else if (field == 1) {
        s->data_2 = s->data_1 - 3;
    } else if (field == 2) {
        s->data_3 = (s->data_1 - s->data_2) / 2;
    } else if (field == 3) {
        s->data_4 = s->data_3 - s->data_2;
    } else {
        s->data_5 = 1;
        s->data_6 = 9;
    }
}

void operation_2_struct_2(base_struct* b) {
    struct_2 *s = (struct_2*)b;
    printf("s->data_1 = %d\n", s->data_1);
    printf("s->data_2 = %d\n", s->data_2);
    printf("s->data_3 = %d\n", s->data_3);
    printf("s->data_4 = %d\n", s->data_4);
    printf("s->data_5 = %d\n", s->data_5);
    printf("s->data_6 = %d\n", s->data_6);
}

iterator get_iterator_struct_2(base_struct* b) {
    iterator it;
    struct_1 *s = (struct_1*)b;
    it.ptr = &(s->data_1);
    it.curr = 0;
    it.size = 6;
    return it;
}

vtable vtbl_struct_2 = {&operation_1_struct_2,&operation_2_struct_2,&get_iterator_struct_2};

void init_struct_2(struct_2* s) {
    s->base.vtbl = &vtbl_struct_2;
}

#endif /* DATA_STRUCTURE_2_H_ */

main.c

#include "data_structure_1.h"
#include "data_structure_2.h"
#include <string.h>
#include <assert.h>

int main(int argc, char** argv) {

    struct_1 s1;
    struct_2 s2;
    init_struct_1(&s1);
    init_struct_2(&s2);

    printf("test struct 1\n");
    algorithm((base_struct*)(&s1));

    printf("test struct 2\n");
    algorithm((base_struct*)(&s2));

    return 0;
}

它基本上实现了一个小的虚拟表,用于虚拟功能。这应该足以满足我的目的,对吧?我现在基本上只是用它们的函数表创建不同的结构,我不需要修改其他任何东西,我错了吗?

【问题讨论】:

  • 暂且不说这是一种可憎的做法,而且绝不会提高代码的清晰度,我不明白您建议如何以一种在C.
  • 与其说这是一个“可憎的事”,为什么不详细说明呢?而且我不明白,您在哪里看到了一种可能的抽象和解耦方式,您希望代码重复吗?我认为使用一点抽象可能会有一些好处......当然这是我的观点,如果我确定它的有效性,我就不会在这里问了。
  • 虽然我很清楚:我想看看你打算如何使用这个。发布一些示例代码来展示您对这种设计的意图。
  • 我不知道你的原始代码有多糟糕,但你的重构在我看来也很糟糕。
  • 根据经验,在考虑进行任何形式的元编程之前,您应该始终非常小心。它需要大量的技能经验,否则元编程会使程序变得更糟。例如,元编程是世界各地存在如此多损坏的 C++ 代码的主要原因之一。

标签: c algorithm design-patterns


【解决方案1】:

所有这些都取决于数据的性质。实现抽象层可能有意义,也可能没有意义。

如果创建一个抽象层是有意义的,我无法用有限的数据信息来判断,它可能应该以更优雅的方式完成。

例如,对于每个结构,您可以定义一个知道如何访问该结构的函数。

然后你可以这样做:

int get_struct1_iterator (struct1* s);
int get_struct2_iterator (struct2* s);
...

#define get_iterator(s)  _Generic((s), \
  struct1: get_struct1_iterator, \
  struct2: get_struct2_iterator)(&s)

int* i = get_iterator(my_struct);

或者,您可以根据标准化的函数格式设计一些东西,让每个结构都包含一个函数指针,但是您必须更改结构。

【讨论】:

  • 这个答案是一个起点,优雅问题是我关心的一个点。我不完全明白我的提议和你的提议有什么区别。您能否详细说明一下(与您的代码相比,对我的代码进行一些比较和评论)?
  • @user8469759 这个方法是完全类型通用的。与带有函数指针的版本一样,除了必须指定函数指针本身的类型。但是,您的方法基于构建一个包含所有已知类型的“数据库结构”,然后是一个知道所有已知访问方法的“超级函数”,并位于一个包含项目中所有内容的“超级文件”中。大量紧密耦合 = 非常糟糕、难以维护、难以移植。
  • 总的来说,您的方法不容易扩展,也不允许模块化编程。使用我的方法,该函数可以作为 ADT 的一部分提供,它不必暴露给世界其他地方。
  • 我上面的意思有一个实现。
  • @user8469759 是的,该代码存在所有提到的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-22
相关资源
最近更新 更多