[解決済み] C言語によるテンプレートのシミュレーション(待ち行列データ型用)
質問
を実装しようとしています。
queue
私の実装は非常にシンプルで、キューに格納できるのは
int
であり、それ以外には何もありません。をシミュレートできないかと考えていました。
C++
のテンプレートは
C
(おそらくプリプロセッサーで
#define
を使用することで、私の
queue
は任意のデータ型を保持することができます。
注
を使いたくないのです。
void*
. ちょっと危険だし、奇妙なランタイムエラーを起こしやすいと思うのです。
どのように解決するのですか?
そのようなテンプレートを作成するためには、微妙な、あるいは醜いトリックを使うことができます。私ならこうします。
テンプレート化されたリストの作成
リストを定義するマクロ
まず、マクロを作成します。
define_list(type)
- この関数は、与えられた型のリストに対するすべての関数を作成します。そして、リストのすべての関数へのポインタを含むグローバル構造体を作成し、リストの各インスタンスにそのグローバル構造体へのポインタを持たせます(この点が
仮想メソッド表
). こんな感じです。
#define define_list(type) \
\
struct _list_##type; \
\
typedef struct \
{ \
int (*is_empty)(const struct _list_##type*); \
size_t (*size)(const struct _list_##type*); \
const type (*front)(const struct _list_##type*); \
void (*push_front)(struct _list_##type*, type); \
} _list_functions_##type; \
\
typedef struct _list_elem_##type \
{ \
type _data; \
struct _list_elem_##type* _next; \
} list_elem_##type; \
\
typedef struct _list_##type \
{ \
size_t _size; \
list_elem_##type* _first; \
list_elem_##type* _last; \
_list_functions_##type* _functions; \
} List_##type; \
\
List_##type* new_list_##type(); \
bool list_is_empty_##type(const List_##type* list); \
size_t list_size_##type(const List_##type* list); \
const type list_front_##type(const List_##type* list); \
void list_push_front_##type(List_##type* list, type elem); \
\
bool list_is_empty_##type(const List_##type* list) \
{ \
return list->_size == 0; \
} \
\
size_t list_size_##type(const List_##type* list) \
{ \
return list->_size; \
} \
\
const type list_front_##type(const List_##type* list) \
{ \
return list->_first->_data; \
} \
\
void list_push_front_##type(List_##type* list, type elem) \
{ \
... \
} \
\
_list_functions_##type _list_funcs_##type = { \
&list_is_empty_##type, \
&list_size_##type, \
&list_front_##type, \
&list_push_front_##type, \
}; \
\
List_##type* new_list_##type() \
{ \
List_##type* res = (List_##type*) malloc(sizeof(List_##type)); \
res->_size = 0; \
res->_first = NULL; \
res->_functions = &_list_funcs_##type; \
return res; \
}
#define List(type) \
List_##type
#define new_list(type) \
new_list_##type()
汎用インターフェース
ここでは、格納された関数ポインタを介してリストの関数を単純に呼び出すマクロをいくつか紹介します。
#define is_empty(collection) \
collection->_functions->is_empty(collection)
#define size(collection) \
collection->_functions->size(collection)
#define front(collection) \
collection->_functions->front(collection)
#define push_front(collection, elem) \
collection->_functions->push_front(collection, elem)
同じ構造を使ってリスト以外のコレクションを設計する場合、良いポインタを格納するどのコレクションに対しても最後の関数を使用することができることに注意してください。
使用例
そして最後に、新しいリスト・テンプレートの使い方を示す小さな例を挙げます。
/* Define the data structures you need */
define_list(int)
define_list(float)
int main()
{
List(int)* a = new_list(int);
List(float)* b = new_list(float);
push_front(a, 5);
push_front(b, 5.2);
}
Cのテンプレートを本当に使いたいのなら、その程度のトリックは使えますが、それはかなり醜いです(C++を使えばいいのです、もっとシンプルになります)。唯一のオーバーヘッドは、データ構造のインスタンスごとにポインタが1つ増えることと、関数を呼び出すときに間接的に1つ増えることです(キャストは行われません。
void*
のポインターがあるんだよ。) 使わないことを祈る:p
制限事項
もちろん、本物のテンプレートではなく、単なるテキスト置換マクロを使用しているため、いくつかの制限があります。
一度定義する
各タイプはコンパイル単位に一度だけ定義することができます。これは大きな欠点となります。例えば、ライブラリを書いていて、ヘッダの中に
define_
という命令があります。
マルチワード型
を作成したい場合は
List
そのテンプレート・タイプが複数の単語で構成されている (
signed char
,
unsigned long
,
const bar
,
struct foo
...) または、テンプレート・タイプがポインタである (
char*
,
void*
...)を使用する場合は
typedef
という型があります。
define_list(int) /* OK */
define_list(char*) /* Error: pointer */
define_list(unsigned long) /* Error: several words */
typedef char* char_ptr;
typedef unsigned long ulong;
define_list(char_ptr) /* OK */
define_list(ulong) /* OK */
ネストされたリストを作成する場合も、同じトリックを使う必要があります。
関連
-
[解決済み】変数の警告が設定されているが使用されていない
-
[解決済み】「無効なイニシャライザー」と表示されるのですが、何が間違っているのでしょうか?
-
[解決済み】 switch case: error: case label does not reduce to an integer constant
-
[解決済み】MB/sとMiB/sを計算する方法は?
-
[解決済み】Errno: 11, Resource Temporarily Unavailable(リソースが一時的に利用できない
-
[解決済み】c - 警告:関数 'printf'の暗黙の宣言
-
[解決済み] テンプレートにおける'typename'と'class'の違い?
-
[解決済み] なぜテンプレートはヘッダーファイルでしか実装できないのですか?
-
[解決済み】高放射能環境下で使用するアプリケーションのコンパイルについて
-
[解決済み】NULL終端文字列の根拠は何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】stdinとSTDIN_FILENOの違いは何ですか?
-
[解決済み】Cコンパイルエラー。"変数サイズのオブジェクトが初期化されていない可能性がある"
-
[解決済み】「ポインタから異なるサイズの整数へのキャスト」エラーが発生するのはなぜですか?
-
[解決済み】"Expected expression before ' { ' token"(トークンの前に期待される式)。
-
[解決済み] clang: error: linker command failed with exit code 1が表示されるのはなぜですか?
-
[解決済み] struct has no member named
-
[解決済み】コンパイラの警告 - 真理値として使用される代入の周囲に括弧を付けることを推奨する
-
[解決済み】argv[]をint型として取得するには?
-
[解決済み】「複数の定義」「最初に定義されたのはここです」エラーについて
-
[解決済み】C言語のフォーマット文字列でデータ引数が使用されない [重複]。