Now I know what you are thinking - the thing that I described in the title sounds just like overloading. I know that is not a thing in C and I'm not going for it anyways. I have these 2 functions - ABSOLUTELY the same in their body, but the arguments are 2 different structs. Basically it's a binary search tree struct and a red black tree struct. As you may know the structs have only one difference - the red black tree struct contains one more field and that's the color one. Also the functions search, min, max, predecessor, successor.. those are going to have the EXACT same body but alas they take 2 different types of structs. And of course the insert and delete methods will be different.
现在我知道你在想什么了——我在标题中描述的东西听起来就像超载。我知道这在C中不存在,我也不会去做。我有这两个函数,它们的主体完全相同,但参数是两个不同的结构。基本上它是一个二叉搜索树结构和一个红黑树结构。正如你所知道的,这些结构体只有一个区别——红黑树结构体包含了另外一个场,那就是颜色。还有函数搜索,最小,最大值,前任,继承者。这些结构体是完全相同的但是它们有两种不同的结构体。当然,插入和删除方法是不同的。
So I was thinking how can I avoid breaking the number one rule in programming and not repeat myself? I thought about a lot of solutions but none work when I try to find a way to implement it. I thought about just using one function for both but I can't do that since the structs are different. I thought about using macros but honestly I have no idea how to make those work and I am not even sure that they can avoid the problem that I have 2 different structs. I thought about making one generic struct and have the rb struct contain it and a color variable but this straight up changes the code with a few characters everywhere since I have to go one level deeper into the struct to get the values and I no longer have duplicate code.
所以我在想,我怎么才能避免打破编程的第一规则,而不重复我自己呢?我考虑了很多解决方案,但是当我试图找到一种方法来实现它时,没有一个工作是可行的。我想两个都用一个函数,但由于结构不同,我不能这么做。我考虑过使用宏,但老实说,我不知道如何让它们工作,我甚至不确定它们能避免我有两个不同结构的问题。我想做一个通用的结构和rb结构包含它颜色变量但这直接改变到处都有几个字符的代码,因为我不得不去一层更深的结构体的价值观和我不再有重复的代码。
Just an example of what the problem looks like:
这只是问题的一个例子:
bst_search(bstTree t, char *k)
{
// Searching in tree
}
rb_search(rbTree t, char *k)
{
// SAME code for searching in tree
}
If I was coding in java I would probably solve this using an abstract superclass but C doesn't have fancy stuff like that.
如果我用java编程,我可能会用一个抽象的超类来解决这个问题,但是C没有像那样的东西。
Some extra info: both implementations have their own header and class files and I'd like to keep it that way. Right now I have duplicate code all over those 2 classes and the only things differing are the names of the functions and the structs (except for the insert and delete functions ofc).
一些额外的信息:两个实现都有自己的头和类文件,我想保持这种方式。现在我在这两个类上都有重复的代码,唯一不同的是函数和结构的名称(除了插入和删除函数c)。
Sorry if this has an obvious solution I just don't find a way out of this without duplicating my code.
抱歉,如果这有一个明显的解决方案,我只是找不到一种方法,不重复我的代码。
3 个解决方案
#1
2
If you create the rbTree
with a bstTree
as the first member thus:
如果以bstTree作为第一个成员创建rbTree,则:
typedef struct
{
bstTree common ;
int color ;
} rbTree
Then you can safely cast an rbTree
to a bstTree
, so rb_search()
can be implemented as a simple macro thus:
然后,您可以安全地将rbTree转换为bstTree,因此可以将rb_search()作为一个简单的宏实现:
#define rb_search(t, k) bst_search( (bstTree*)(t). k )
One problem is that now for any code that is unique to rbTree
you have to access most of the members via teh common
member. That however is not entirely necessary; if you do not define rbTree
with a bstTree
member, but simply ensure that both are defined identically with the common members first and in the same order and type, you will be able to cast one to the other and access the members so long as the same packing and alignment options are applied to all modules that use the structures - doing that however is far less safe and less easily maintained. A somewhat ugly but safer way to do this is to place the common members in an include file and #include
the members in each struct definition.
一个问题是,对于任何rbTree特有的代码,都必须通过公共成员访问大多数成员。然而,这并不是完全必要的;如果不定义rbTree bstTree成员,而只是确保定义相同与普通成员首先在相同的顺序和类型,您将能够投到另一个和访问成员只要相同的包装和对齐选项应用到所有模块使用的结构——这样做不过是更安全的,更不容易维护。一种比较难看但更安全的方法是将公共成员放在include文件中,并将#include成员包含在每个struct定义中。
#2
0
There is no good way of doing it in C (in contrast to C++ where templates exist for exactly this purpose).
在C语言中没有很好的方法来实现它(与c++相反,在c++中模板的存在就是为了达到这个目的)。
Ugly way #1. By using macro:
丑陋的方式# 1。通过使用宏:
#define MAKE_SEARCH_FUNCTION(FN_NAME, VAR_TYPE) \
FN_NAME(VAR_TYPE t, char *k) \
{ \
/* Searching in tree */ \
}
struct bstTree {
};
MAKE_SEARCH_FUNCTION(bst_search, struct bstTree*)
struct rbTree {
};
MAKE_SEARCH_FUNCTION(rb_search, struct rbTree*)
Ugly way #2. By moving body into separate include file. A bit more work, but makes sense if function is very large or if you need to generate whole families of functions at once (e.g. bst_search()
/bst_add()
/ bst_remove()
).
丑陋的方式# 2。将主体移动到独立的包含文件中。再多做一点工作,但是如果函数很大,或者需要同时生成整个函数族(例如bst_search()/bst_add()/ bst_remove())),那么这样做是有意义的。
// header.h
/ / header.h
FN_NAME(VAR_TYPE t, char *k)
{
// Searching in tree
}
// source.c
/ / source.c
struct bstTree {
};
#define VAR_TYPE struct bstTree*
#define FN_NAME bst_search
#include "header.h"
#undef VAR_TYPE
#undef FN_NAME
struct rbTree {
};
#define VAR_TYPE struct rbTree*
#define FN_NAME rb_search
#include "header.h"
#undef VAR_TYPE
#undef FN_NAME
#3
0
A macro that acted on a generic input tree would make it, but I find that solution somehow dirty.
一个在泛型输入树上执行的宏将会成功,但是我发现这个解决方案有点脏。
Another approach is to have a generic struct with all the members of both trees together, without nested structs. For example:
另一种方法是,在没有嵌套结构的情况下,将两棵树的所有成员都配置为通用结构。例如:
struct genericTree {
// common members for both trees
...
// members for rb trees
...
// members for bst
...
}
Then you have a single function:
然后你有一个单一的函数:
search(genericTree* t, char* k)
To keep the semantics, use typedefs:
为了保持语义,使用typedefs:
typedef genericTree bstTree;
typedef genericTree rbTree;
So you can still have functions that get a bstTree or a rbTree when they expect only one if those types.
所以你仍然可以得到一个bstTree或者rbTree的函数,当它们只期望这些类型为1时。
The drawback of this approach is that you take more memory for a single tree because you keep members of the other. You may ease it with some unions, probably.
这种方法的缺点是,您为单个树占用更多内存,因为您保留了另一个树的成员。你可能会和一些工会合作。
#1
2
If you create the rbTree
with a bstTree
as the first member thus:
如果以bstTree作为第一个成员创建rbTree,则:
typedef struct
{
bstTree common ;
int color ;
} rbTree
Then you can safely cast an rbTree
to a bstTree
, so rb_search()
can be implemented as a simple macro thus:
然后,您可以安全地将rbTree转换为bstTree,因此可以将rb_search()作为一个简单的宏实现:
#define rb_search(t, k) bst_search( (bstTree*)(t). k )
One problem is that now for any code that is unique to rbTree
you have to access most of the members via teh common
member. That however is not entirely necessary; if you do not define rbTree
with a bstTree
member, but simply ensure that both are defined identically with the common members first and in the same order and type, you will be able to cast one to the other and access the members so long as the same packing and alignment options are applied to all modules that use the structures - doing that however is far less safe and less easily maintained. A somewhat ugly but safer way to do this is to place the common members in an include file and #include
the members in each struct definition.
一个问题是,对于任何rbTree特有的代码,都必须通过公共成员访问大多数成员。然而,这并不是完全必要的;如果不定义rbTree bstTree成员,而只是确保定义相同与普通成员首先在相同的顺序和类型,您将能够投到另一个和访问成员只要相同的包装和对齐选项应用到所有模块使用的结构——这样做不过是更安全的,更不容易维护。一种比较难看但更安全的方法是将公共成员放在include文件中,并将#include成员包含在每个struct定义中。
#2
0
There is no good way of doing it in C (in contrast to C++ where templates exist for exactly this purpose).
在C语言中没有很好的方法来实现它(与c++相反,在c++中模板的存在就是为了达到这个目的)。
Ugly way #1. By using macro:
丑陋的方式# 1。通过使用宏:
#define MAKE_SEARCH_FUNCTION(FN_NAME, VAR_TYPE) \
FN_NAME(VAR_TYPE t, char *k) \
{ \
/* Searching in tree */ \
}
struct bstTree {
};
MAKE_SEARCH_FUNCTION(bst_search, struct bstTree*)
struct rbTree {
};
MAKE_SEARCH_FUNCTION(rb_search, struct rbTree*)
Ugly way #2. By moving body into separate include file. A bit more work, but makes sense if function is very large or if you need to generate whole families of functions at once (e.g. bst_search()
/bst_add()
/ bst_remove()
).
丑陋的方式# 2。将主体移动到独立的包含文件中。再多做一点工作,但是如果函数很大,或者需要同时生成整个函数族(例如bst_search()/bst_add()/ bst_remove())),那么这样做是有意义的。
// header.h
/ / header.h
FN_NAME(VAR_TYPE t, char *k)
{
// Searching in tree
}
// source.c
/ / source.c
struct bstTree {
};
#define VAR_TYPE struct bstTree*
#define FN_NAME bst_search
#include "header.h"
#undef VAR_TYPE
#undef FN_NAME
struct rbTree {
};
#define VAR_TYPE struct rbTree*
#define FN_NAME rb_search
#include "header.h"
#undef VAR_TYPE
#undef FN_NAME
#3
0
A macro that acted on a generic input tree would make it, but I find that solution somehow dirty.
一个在泛型输入树上执行的宏将会成功,但是我发现这个解决方案有点脏。
Another approach is to have a generic struct with all the members of both trees together, without nested structs. For example:
另一种方法是,在没有嵌套结构的情况下,将两棵树的所有成员都配置为通用结构。例如:
struct genericTree {
// common members for both trees
...
// members for rb trees
...
// members for bst
...
}
Then you have a single function:
然后你有一个单一的函数:
search(genericTree* t, char* k)
To keep the semantics, use typedefs:
为了保持语义,使用typedefs:
typedef genericTree bstTree;
typedef genericTree rbTree;
So you can still have functions that get a bstTree or a rbTree when they expect only one if those types.
所以你仍然可以得到一个bstTree或者rbTree的函数,当它们只期望这些类型为1时。
The drawback of this approach is that you take more memory for a single tree because you keep members of the other. You may ease it with some unions, probably.
这种方法的缺点是,您为单个树占用更多内存,因为您保留了另一个树的成员。你可能会和一些工会合作。