【发布时间】: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