2016-04-30 4 views
4

私はあなたの考えを知っています。タイトルに書かれていることはオーバーロードのように聞こえます。私はそれがCのものではないことを知っています。そして、私はそれをとにかく行っていません。私はこれらの2つの機能を持っています - 絶対に同じものですが、引数は2つの異なる構造体です。基本的にはバイナリ検索ツリー構造体と赤色の黒いツリー構造体です。構造体には1つの違いしかありません - 赤い黒いツリーの構造体にはもう1つのフィールドがあり、それはカラーの構造体です。関数search、min、max、predecessor、successor ..これらは完全に同じボディを持ちますが、残念なことに2つの異なるタイプの構造体を取ります。もちろん、挿入と削除の方法は異なります。このような状況で自分を繰り返さない方法は?同じだが異なる引数を持つC関数

私はプログラミングでナンバーワンのルールを破って自分自身を繰り返さないようにするにはどうすればいいか考えていましたか?私は多くのソリューションについて考えていましたが、実装する方法を見つけようとするとうまくいきません。私は両方のために1つの関数を使用することについて考えましたが、構造体が異なるので、それを行うことはできません。私はマクロを使用することを考えましたが、正直なところ、これらの作業をどのように行うかわからず、2つの異なる構造体を持つ問題を回避できるかどうかもわかりません。私は1つの汎用構造体を作ることを考え、rb構造体にそれと色の変数が含まれていることを考えましたが、これはコードをいくつかの文字で変更しています。コードを複製する。

問題がどのように見えるかの単なる例:私はJavaでコーディングされた場合、私はおそらく抽象スーパークラスを使用してこの問題を解決するだろうが、Cはそのような派手なものを持っていない

bst_search(bstTree t, char *k) 
{ 
    // Searching in tree 
} 

rb_search(rbTree t, char *k) 
{ 
    // SAME code for searching in tree 
} 

いくつかの追加情報:どちらの実装も独自のヘッダーファイルとクラスファイルを持っていますので、そのまま使用したいと思います。今、私はそれらの2つのクラス全体で重複したコードを持っていますが、関数と構造体の名前(挿入と削除の機能を除く)は異なります。

申し訳ありませんこれは明白な解決策がある場合、私はちょうど私のコードを複製せずにこれの方法を見つけることはありません。

+0

汎用構造体を使用して残りのコードをすべて変更したくない場合は、2つの呼び出し関数(それぞれの型ごとに1つずつ)でそれぞれ行います。関連するメンバーを共通の構造体にコピーし、検索機能を呼び出します。 –

+0

ナンバーワンのルールは確かにDRYではありません。私はKISSを最初に置くだろう。しかし、もちろん、最初のルールはあなたが求めるすべてのプログラマーのためのものでなければならない別の答えを得る... – cmaster

答えて

2

あなたは、このように最初のメンバーとしてbstTreerbTreeを作成する場合:

typedef struct 
{ 
    bstTree common ; 
    int color ; 
} rbTree 
その後

あなたは安全にbstTreerbTreeをキャストすることができますので、rb_search()は、このように簡単なマクロとして実装することができます。

#define rb_search(t, k) bst_search((bstTree*)(t). k) 

1つの問題は、rbTreeに固有のすべてのコードで、あなたはほとんどのメンバーにteh common mでアクセスする必要がありますエバー。しかしそれは完全には必要ではない。 rbTreebstTreeのメンバーで定義していない場合、両方のメンバーが共通のメンバーと同じ順序で同じタイプで定義されていることを確認するだけで、一方を他方にキャストしてメンバーにアクセスできるようになります構造体を使用するすべてのモジュールに同じパッキングオプションとアライメントオプションが適用されますが、安全性が低く、メンテナンスが容易ではありません。これを行うには多少醜いしかしより安全な方法は、共通のメンバをインクルードファイルに配置し、#includeの各構造体定義のメンバを配置することです。

+0

ベスト答え。これは、Windows APIが古い構造の新しいバージョンを処理する方法です。 –

+0

私はいつもこのマクロを定義します: '#define rb_search(t、k)bst_search(&(t) - > common、k)'それは呼び出しの型チェックを保持し、 'rb_search(hashTablePointer、" foo ")'で置き換えます。 – cmaster

+0

私はこのバージョンが好きですが、実装を開始したときには、一般的なbstTreeには親、右と左の3つのポインタがあり、すべてbstTreeポインタです。したがって、他のbstTreesへのポインタを含むbstTreeを含むrbTreeがある場合、rbTreeノード間の接続はありません。どのような木の目的を打つ。コードの重複を伴​​わない回避策はありますか?私はちょうど先に進んで、異なるツリーごとに同じコードを行うべきですか? – user3212138

0

Cで行うのは良い方法ではありません(この目的のためにテンプレートが存在するC++とは対照的です)。

醜い方法#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*) 

醜い方法#2。本文を別のインクルードファイルに移動します。もう少し機能はありますが、関数が非常に大きい場合や、すべての関数ファミリを一度に生成する必要がある場合(たとえば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 
0

一般的な入力ツリーに作用するマクロはそれを作るだろうが、私はそのソリューションは何とか汚い見つけます。

もう1つのアプローチは、ネストされた構造体を使用せずに、両方のツリーのすべてのメンバーをまとめた汎用構造体を持つことです。たとえば:

typedef genericTree bstTree; 
typedef genericTree rbTree; 

だから、あなたはまだbstTreeを取得する機能を持っているか、することができます

search(genericTree* t, char* k) 

セマンティクス、使用のtypedefを保つために:

struct genericTree { 
    // common members for both trees 
... 
    // members for rb trees 
... 
    // members for bst 
... 
} 

その後、あなたは、単一の機能を持っていますrbTreeは、それらの型が1つだけの場合にのみ必要です。

このアプローチの欠点は、他のメンバーを保持するので、1つのツリーに対してより多くのメモリを取ることです。おそらく、おそらくいくつかの組合でそれを緩和するかもしれません。

関連する問題