Reputation:
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.
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.
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.
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).
Sorry if this has an obvious solution I just don't find a way out of this without duplicating my code.
Upvotes: 4
Views: 183
Reputation: 8607
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:
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.
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.
Upvotes: 0
Reputation: 93456
If you create the rbTree
with a bstTree
as the first member thus:
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:
#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.
Upvotes: 2
Reputation: 4179
There is no good way of doing it in C (in contrast to C++ where templates exist for exactly this purpose).
Ugly way #1. By using macro:
#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()
).
// header.h
FN_NAME(VAR_TYPE t, char *k)
{
// Searching in tree
}
// 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
Upvotes: 0