如果我们有多个数据结构,例如:
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
iterator.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”并更新联合,并切换语句。
请记住,这是一个非常小的例子(它可能是一种小框架)。
我试图理解的是天气与否这种方法可以在更大的背景下有任何好处(试图想象我的功能更复杂,程序算法“巨大”调用所有这些类似的算法“,它是否更方便重写特殊版本的“算法”或写几个算法调用的小函数?
我希望这次能说明我的观点。
更新:
从网络资源中获取灵感,以及其他一些答案,您如何看待以下内容?:
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;
}
它基本上为虚拟功能实现了一个小的虚拟表。这对我的目的应该足够好吗?我基本上只是创建不同的结构,它们是函数表,我不需要修改任何其他内容,我错了吗?
答案 0 :(得分:2)
所有这些都取决于数据的性质。实现抽象层可能有意义也可能没有意义。
如果制作一个抽象层是有意义的,我无法用有限的数据信息告诉它,它应该以更优雅的方式完成。
例如,对于每个结构,您可以定义一个知道如何访问该结构的函数。
然后你可以这样做:
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);
或者你可以根据标准化的函数格式设计一些东西,让每个struct包含一个函数指针,但是你必须改变结构。