queue
Cを使用して構造を実装しようとしています。実装は非常に簡単です。キューはint
sのみを保持でき、他には何も保持できません。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*
、ええ\ o /)。あなたがそれを決して使わないことを願っています:p
もちろん、実際のテンプレートではなく、単なるテキスト置換マクロを使用しているため、いくつかの制限があります。
各タイプはコンパイル単位ごとに1回だけ定義できます。そうしないと、プログラムのコンパイルに失敗します。これは、たとえばライブラリを作成し、ヘッダーの一部にいくつかの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 */
ネストされたリストを作成する場合は、同じトリックに頼る必要があります。
さて、私の頭に浮かぶ唯一の可能性はマクロ(#define
s)です。多分次のようなものです:
queue.h:
#define TYPE int
#define TYPED_NAME(x) int_##x
#include "queue_impl.h"
#undef TYPE
#undef TYPED_NAME
#define TYPE float
#define TYPED_NAME(x) float_##x
#include "queue_impl.h"
#undef TYPE
#undef TYPED_NAME
...
queue_impl.h:
//no include guard, of course
typedef struct
{
TYPE *data;
...
} TYPED_NAME(queue);
void TYPED_NAME(queue_insert) (TYPED_NAME(queue) *queue, TYPE data)
{
...
}
それが機能する場合(私は100%確信していませんが、そのようなプリプロセッサの専門家ではありません)、関数とともに構造体int_queue
とを提供するはずですfloat_queue
void int_queue_insert(int_queue *queue, int data);
void float_queue_insert(float_queue *queue, float data);
もちろん、必要なすべてのタイプに対して「テンプレート」のインスタンス化を自分で行う必要がありますが、これはで5行のブロックを繰り返すことになりqueue.h
ます。実際の実装は一度だけ書く必要があります。もちろん、これをさらに洗練することもできますが、基本的な考え方は明確でなければなりません。
これにより、少なくとも完全にタイプセーフなキューテンプレートが提供されますが、インターフェイスを完全に一致させるという利便性はありません(Cはオーバーロードされた関数をサポートしていないため、関数はタイプ名を保持する必要があります)。
void *データを含むキューを実装し、このvoid *を任意の型、またはintのようなプリミティブ型へのポインターとして解釈します。
#defineを使用することは可能ですが、何か問題がある場合はデバッグを検討してください...
私は長い間これについて疑問に思っていましたが、今では誰もが理解できる明確な答えがあります。見よ!
私がデータ構造コースを受講していたとき、私はデータ構造、Cのアルゴリズムに関するStandishの本を読まなければなりませんでした。それは苦痛でした。ジェネリックスはなく、表記法が貧弱で、令状がないグローバルな状態の突然変異がたくさんありました。彼のコードスタイルを採用することは、将来のすべてのプロジェクトを台無しにすることを意味することを私は知っていましたが、より良い方法があることを知っていたので、見よ、より良い方法:
触る前はこんな感じでした(実はとにかく触って、人間が読めるようにフォーマットしてみました、どういたしまして)。それは多くのレベルで本当に醜くて間違っていますが、参考のためにリストします:
#include <stdio.h>
#define MaxIndex 100
int Find(int A[])
{
int j;
for (j = 0; j < MaxIndex; ++j) {
if (A[j] < 0) {
return j;
}
}
return -1;
}
int main(void)
{
// reminder: MaxIndex is 100.
int A[MaxIndex];
/**
* anonymous scope #1
* initialize our array to [0..99],
* then set 18th element to its negative value(-18)
* to make the search more interesting.
*/
{
// loop index, nothing interesting here.
int i;
// initialize our array to [0..99].
for (i = 0; i < MaxIndex; ++i) {
A[i] = i * i;
}
A[17]= -A[17];
}
/**
* anonymous scope #2
* find the index of the smallest number and print it.
*/
{
int result = Find(A);
printf(
"First negative integer in A found at index = %d.\n",
result
);
}
// wait for user input before closing.
getchar();
return 0;
}
このプログラムは、恐ろしく悪いスタイルで複数のことを行います。特に、単一のスコープ内でのみ使用されるグローバルマクロを設定しますが、それ以降はコードの汚染を継続します。非常に悪いです、そして大規模なグローバルスコープ汚染のWindowsAPIスケールを引き起こします。
さらに、このプログラムは、引数を含む構造体なしで、引数を配列として渡します。つまり、配列が関数Findに到達すると、配列は到着時に無効になります。配列のサイズがわからなくなったため、mainとFindがグローバルマクロに依存するようになりました。これは非常に悪いことです。
この問題を解決するためのブルートフォース攻撃には2つの方法がありますが、それでもコードは単純に保たれます。最初の方法は、配列を100個の整数の配列として定義するグローバル構造体を作成することです。このように構造体を渡すと、その中の配列の長さが保持されます。2番目の方法は、配列の長さをfindの引数として渡し、配列を作成する前に#define行のみを使用し、直後に#undefを使用することです。これは、スコープがsizeofを介して配列のサイズを認識しているためです。実行時のオーバーヘッドが0の(A)/ sizeof(A [0])の場合、コンパイラーは100を推定して、に貼り付けます。
この問題を3番目の方法で解決するために、ジェネリック配列を作成するのに適したヘッダーを作成しました。抽象データ型ですが、自動化されたデータ構造と呼びたいと思います。
SimpleArray.h
/**
* Make sure that all the options needed are given in order to create our array.
*/
#ifdef OPTION_UNINSTALL
#undef OPTION_ARRAY_TYPE
#undef OPTION_ARRAY_LENGTH
#undef OPTION_ARRAY_NAME
#else
#if (!defined OPTION_ARRAY_TYPE) || !defined OPTION_ARRAY_LENGTH || (!defined OPTION_ARRAY_NAME)
#error "type, length, and name must be known to create an Array."
#endif
/**
* Use the options to create a structure preserving structure for our array.
* that is, in contrast to pointers, raw arrays.
*/
struct {
OPTION_ARRAY_TYPE data[OPTION_ARRAY_LENGTH];
} OPTION_ARRAY_NAME;
/**
* if we are asked to also zero out the memory, we do it.
* if we are not granted access to string.h, brute force it.
*/
#ifdef OPTION_ZERO_MEMORY
#ifdef OPTION_GRANT_STRING
memset(&OPTION_ARRAY_NAME, 0, OPTION_ARRAY_LENGTH * sizeof(OPTION_ARRAY_TYPE));
#else
/* anonymous scope */
{
int i;
for (i = 0; i < OPTION_ARRAY_LENGTH; ++i) {
OPTION_ARRAY_NAME.data[i] = 0;
}
}
#endif
#undef OPTION_ZERO_MEMORY
#endif
#endif
このヘッダーは基本的に、Cプリプロセッサを使用することを余儀なくされた場合にすべてのCデータ構造ヘッダーがどのように見えるかを示します(PHP/テンプレートツールキット/ASP /独自の埋め込み可能なスクリプト言語とは対照的です)。
試してみましょう:
#include <stdio.h>
int Find(int A[], int A_length)
{
int j;
for (j = 0; j < A_length; ++j) {
if (A[j] < 0) {
return j;
}
}
return -1;
}
int main(void)
{
// std::array<int, 100> A;
#define OPTION_ARRAY_TYPE int
#define OPTION_ARRAY_LENGTH 100
#define OPTION_ARRAY_NAME A
#include "SimpleArray.h"
/**
* anonymous scope #1
* initialize our array to [0..99],
* then set 18th element to its negative value(-18)
* to make the search more interesting.
*/
{
// loop index, nothing interesting here.
int i;
// initialize our array to [0..99].
for (i = 0; i < (sizeof(A.data) / sizeof(A.data[0])); ++i) {
A.data[i] = i * i;
}
A.data[17]= -A.data[17];
}
/**
* anonymous scope #2
* find the index of the smallest number and print it.
*/
{
int result = Find(A.data, (sizeof(A.data) / sizeof(A.data[0])));
printf(
"First negative integer in A found at index = %d.\n",
result
);
}
// wait for user input before closing.
getchar();
// making sure all macros of SimpleArray do not affect any code
// after this function; macros are file-wide, so we want to be
// respectful to our other functions.
#define OPTION_UNINSTALL
#include "SimpleArray.h"
return 0;
}
見よ、私たちは純粋なCおよびCプリプロセッサで素朴なstd :: arrayを発明しました!マクロを使用しましたが、後片付けをするので悪ではありません。スコープの最後では、すべてのマクロが未定義です。
問題がある; わからない限り、配列のサイズはわかりません(sizeof(A.data) / sizeof(A.data[0]))
。これにはコンパイラのオーバーヘッドはありませんが、子供向けではありません。どちらもマクロではありませんが、ここではボックス内で作業しています。後で、PHPのようなより使いやすいプリプロセッサを使用して、子供に優しいものにすることができます。
これを解決するために、「無料の」配列データ構造のメソッドとして機能するユーティリティライブラリを作成できます。
SimpleArrayUtils.h
/**
* this is a smart collection that is created using options and is
* removed from scope when included with uninstall option.
*
* there are no guards because this header is meant to be strategically
* installed and uninstalled, rather than kept at all times.
*/
#ifdef OPTION_UNINSTALL
/* clean up */
#undef ARRAY_FOREACH_BEGIN
#undef ARRAY_FOREACH_END
#undef ARRAY_LENGTH
#else
/**
* array elements vary in number of bytes, encapsulate common use case
*/
#define ARRAY_LENGTH(A) \
((sizeof A.data) / (sizeof A.data[0]))
/**
* first half of a foreach loop, create an anonymous scope,
* declare an iterator, and start accessing the items.
*/
#if defined OPTION_ARRAY_TYPE
#define ARRAY_FOREACH_BEGIN(name, iter, arr)\
{\
unsigned int iter;\
for (iter = 0; iter < ARRAY_LENGTH(arr); ++iter) {\
OPTION_ARRAY_TYPE name = arr.data[iter];
#endif
/**
* second half of a foreach loop, close the loop and the anonymous scope
*/
#define ARRAY_FOREACH_END \
}\
}
#endif
これはかなり機能豊富なライブラリであり、基本的にはエクスポートします
ARRAY_LENGTH::データフィールド->intを持つもの
OPTION_ARRAY_SIZEがまだ定義されているか、再定義されている場合、ヘッダーはforeachループの実行方法も定義します。かわいいです。
さあ、夢中になりましょう。
SimpleArray.h
/**
* Make sure that all the options needed are given in order to create our array.
*/
#ifdef OPTION_UNINSTALL
#ifndef OPTION_ARRAY_TYPE
#undef OPTION_ARRAY_TYPE
#endif
#ifndef OPTION_ARRAY_TYPE
#undef OPTION_ARRAY_LENGTH
#endif
#ifndef OPTION_ARRAY_NAME
#undef OPTION_ARRAY_NAME
#endif
#ifndef OPTION_UNINSTALL
#undef OPTION_UNINSTALL
#endif
#else
#if (!defined OPTION_ARRAY_TYPE) || !defined OPTION_ARRAY_LENGTH || (!defined OPTION_ARRAY_NAME)
#error "type, length, and name must be known to create an Array."
#endif
/**
* Use the options to create a structure preserving structure for our array.
* that is, in contrast to pointers, raw arrays.
*/
struct {
OPTION_ARRAY_TYPE data[OPTION_ARRAY_LENGTH];
} OPTION_ARRAY_NAME;
/**
* if we are asked to also zero out the memory, we do it.
* if we are not granted access to string.h, brute force it.
*/
#ifdef OPTION_ZERO_MEMORY
#ifdef OPTION_GRANT_STRING
memset(&OPTION_ARRAY_NAME, 0, OPTION_ARRAY_LENGTH * sizeof(OPTION_ARRAY_TYPE));
#else
/* anonymous scope */
{
int i;
for (i = 0; i < OPTION_ARRAY_LENGTH; ++i) {
OPTION_ARRAY_NAME.data[i] = 0;
}
}
#endif
#undef OPTION_ZERO_MEMORY
#endif
#endif
SimpleArrayUtils.h
/**
* this is a smart collection that is created using options and is
* removed from scope when included with uninstall option.
*
* there are no guards because this header is meant to be strategically
* installed and uninstalled, rather than kept at all times.
*/
#ifdef OPTION_UNINSTALL
/* clean up, be mindful of undef warnings if the macro is not defined. */
#ifdef ARRAY_FOREACH_BEGIN
#undef ARRAY_FOREACH_BEGIN
#endif
#ifdef ARRAY_FOREACH_END
#undef ARRAY_FOREACH_END
#endif
#ifdef ARRAY_LENGTH
#undef ARRAY_LENGTH
#endif
#else
/**
* array elements vary in number of bytes, encapsulate common use case
*/
#define ARRAY_LENGTH(A) \
((sizeof A.data) / (sizeof A.data[0]))
/**
* first half of a foreach loop, create an anonymous scope,
* declare an iterator, and start accessing the items.
*/
#if defined OPTION_ARRAY_TYPE
#define ARRAY_FOREACH_BEGIN(name, iter, arr)\
{\
unsigned int iter;\
for (iter = 0; iter < ARRAY_LENGTH(arr); ++iter) {\
OPTION_ARRAY_TYPE name = arr.data[iter];
#endif
/**
* second half of a foreach loop, close the loop and the anonymous scope
*/
#define ARRAY_FOREACH_END \
}\
}
#endif
main.c
#include <stdio.h>
// std::array<int, 100> A;
#define OPTION_ARRAY_TYPE int
#define OPTION_ARRAY_LENGTH 100
#define OPTION_ARRAY_NAME A
#include "SimpleArray.h"
#define OPTION_UNINSTALL
#include "SimpleArray.h"
int Find(int A[], int A_length)
{
int j;
for (j = 0; j < A_length; ++j) {
if (A[j] < 0) {
return j;
}
}
return -1;
}
int main(void)
{
#define OPTION_ARRAY_NAME A
#define OPTION_ARRAY_LENGTH (sizeof(A.data) / sizeof(A.data[0]))
#define OPTION_ARRAY_TYPE int
#include "SimpleArray.h"
/**
* anonymous scope #1
* initialize our array to [0..99],
* then set 18th element to its negative value(-18)
* to make the search more interesting.
*/
{
#include "SimpleArrayUtils.h"
printf("size: %d.\n", ARRAY_LENGTH(A));
ARRAY_FOREACH_BEGIN(item, i, A)
A.data[i] = i * i;
ARRAY_FOREACH_END
A.data[17] = -A.data[17];
// uninstall all macros.
#define OPTION_UNINSTALL
#include "SimpleArrayUtils.h"
}
/**
* anonymous scope #2
* find the index of the smallest number and print it.
*/
{
#include "SimpleArrayUtils.h"
int result = Find(A.data, (sizeof(A.data) / sizeof(A.data[0])));
printf(
"First negative integer in A found at index = %d.\n",
result
);
// uninstall all macros.
#define OPTION_UNINSTALL
#include "SimpleArrayUtils.h"
}
// wait for user input before closing.
getchar();
// making sure all macros of SimpleArray do not affect any code
// after this function; macros are file-wide, so we want to be
// respectful to our other functions.
#define OPTION_UNINSTALL
#include "SimpleArray.h"
return 0;
}
ご覧のように; 私たちは今、自由な抽象化を表現する力を持っており(コンパイラーがそれらを代用します)、必要なもの(構造体)に対してのみ支払い、残りは捨てられ、グローバルスコープを汚染しません。
ここでPHPの力を強調するのは、HTMLドキュメントのコンテキスト外でPHPを見た人はほとんどいないからです。ただし、Cドキュメントやその他のテキストファイルで使用できます。テンプレートツールキットを使用して、好きなスクリプト言語をマクロに入れることができます。これらの言語は、名前空間、変数、および実際の関数を備えているため、Cプリプロセッサよりもはるかに優れています。これにより、コードを生成する実際のスクリプトをデバッグしているため、デバッグが容易になります。主に親しみやすさのために、デバッグするのが地獄であるCプリプロセッサではありません(正しい心の中で誰がCプリプロセッサで遊んで慣れるために何時間も費やしますか?ほとんどありません)。
PHPでこれを行う例を次に示します。
SimpleArray.php
<?php
class SimpleArray {
public $length;
public $name;
public $type;
function __construct($options) {
$this->length = $options['length'];
$this->name = $options['name'];
$this->type = $options['type'];
}
function getArray() {
echo ($this->name . '.data');
}
function __toString() {
return sprintf (
"struct {\n" .
" %s data[%d];\n" .
"} %s;\n"
,
$this->type,
$this->length,
$this->name
);
}
};
?>
main.php
#include <stdio.h>
<?php include('SimpleArray.php'); ?>
int Find(int *A, int A_length)
{
int i;
for (i = 0; i < A_length; ++i)
{
if (A[i] < 0) {
return i;
}
}
return -1;
}
int main(int argc, char **argv)
{
<?php
$arr = new SimpleArray(array(
'name' => 'A',
'length' => 100,
'type' => 'int'
));
echo $arr;
?>
printf("size of A: %d.\n", <?php echo($arr->length); ?>);
/* anonymous scope */
{
int i;
for (i = 0; i < <?php echo($arr->length)?>; ++i) {
<?php $arr->getArray(); ?>[i] = i * i;
}
<?php $arr->getArray(); ?>[17] = -<?php $arr->getArray()?>[17];
}
int result = Find(<?php $arr->getArray();?>, <?php echo $arr->length; ?>);
printf(
"First negative integer in A found at index = %d.\n",
result
);
getchar();
return 0;
}
走るphp main.php > main.c
それから
gcc main.c -o main
./main
これはObjectiveCによく似ています。これは、コンパイル時の「マクロ」を実際のランタイムにリンクする傾向があることを除けば、基本的にObjective Cが行うことです(Cの実行中に実行時にphpが使用可能であるかのように)。 Cがphpと通信できるようにし、phpがCと通信できるようにします。ただし、phpは、多くの角括弧が付いた小さなおしゃべりな言語です)。主な違いは、Objective Cには、ここで行ったように「静的」構造を作成する方法がないことです。そのオブジェクトは実際には実行時であるため、アクセスにはるかにコストがかかりますが、はるかに柔軟性があり、構造を保持します。一方、C構造体は、ヘッダーがスコープを離れるとすぐにバイトに折りたたまれます(オブジェクトは元のオブジェクトに反映できます)。内部タグ付き共用体を使用した状態)..。
これは、(プリプロセッサを介して)インスタンス化し、同じCファイルで複数のタイプを使用できるバージョンです(注意、トークンの連結を使用します):
#include <stdio.h>
#define DEFINE_LL_NODE(CONCRETE_TYPE) \
struct node_of_ ## CONCRETE_TYPE \
{ \
CONCRETE_TYPE data; \
struct node_of_ ## CONCRETE_TYPE *next; \
};
#define DECLARE_LL_NODE(CONCRETE_TYPE,VARIABLE_NAME) \
struct node_of_ ## CONCRETE_TYPE VARIABLE_NAME;
/* Declarations for each type. */
DEFINE_LL_NODE(int)
DEFINE_LL_NODE(char)
int main (void)
{
/* Declaration of instances of each type. */
DECLARE_LL_NODE (int, foo)
DECLARE_LL_NODE (char, bar)
/* And you can then use these instances. */
foo.data = 1;
foo.next = NULL;
bar.data = 'c';
bar.next = NULL;
}
で前処理するとcpp
、次のようになります。
struct node_of_int { int data; struct node_of_int *next; };
struct node_of_char { char data; struct node_of_char *next; };
int main (void)
{
struct node_of_int foo;
struct node_of_char bar;
foo.data = 1;
foo.next = ((void *)0);
bar.data = 'c';
bar.next = ((void *)0);
}
プリプロセッサマクロを使用したCでも、高品質のテンプレートを実際に機能させることはできません。なぜなら、これらのマクロは1回しか展開されないため、せいぜい再入力可能なデータ構造を取得できますが、処理されると、プログラム全体でその型になります。
つまりvoid *
、Cの型チェックを弱める型ソリューションを検討する必要があります。弱められた型チェックを修正するために、非void*型を表す「構築時に1回割り当てる」文字列である「type」フィールドを構造体に埋め込むことを検討してください。次に、構造体の保守に関連する関数内の型チェックの欠如を改善できる可能性があります。つまり、そのようなことがあなたにとってさらに重要である場合です。
あなたが本当にこれをしたいのなら、それは簡単な方法で解決することができますtypedef
:
typedef int data_t;
struct queue
{
data_t* data;
}
data_t
プレーンの代わりに、すべての場所で使用できるようになりましたint
。ただし、一度に複数のタイプを使用することはできないことに注意してください(少なくとも、C ++テンプレートのこの特定の動作をプレーンCでシミュレートする方法はわかりません)。
#define q(t) \
typedef struct _q_##t {t v; struct q_##t *next} q_##t;
q(char);
q(int);
int main(void)
{
q_char qc;
q_int qi;
qc.v = 'c';
qc.next = (void *) 0;
qi.v = 42;
qi.next = (void *) 0;
return 0;
}
しかし、それがあなたが探しているものかどうかはわかりません...
別の回答でコード生成マクロの1つを使用してから、いくつかのC11オーバーロードマクロで終了します。これにより、呼び出しサイトにタイプ情報が多すぎる必要がなくなります。
私が回答で見たものに基づいたコメントのカップル。
実際、Cでこれに取り組む方法は、#defineマクロと関数ポインターを試すことです。重要なのは、C ++テンプレートは、最初のバージョンではほとんどそれでした。コードをコピーし、シンボルを生成するための単なる方法であり、テキストで元のテンプレートを「パラメータ化」するようなものでした。ここでの想像力の使い方には空が限界です。型チェックなどに関しては、多くの点で自分自身で行っているので注意してください。コンパイラからの助けはあまり期待しないでください。
ここで「C++を使用しない理由」と時々言うのは逆効果です。問題は、機能がない場合にその機能をどのようにシミュレートするかということでした。私の経験から言うと、私はかつてC++でもテンプレートをシミュレートしていました。なぜだと思いますか?1990年の初めだったので、C ++と、C ++のテンプレートのアイデアがありましたが、そのような実装はほとんどありませんでした。それが理由です。そうですね、その前もCでやりました。C ++を使用すると、少なくとも関数ポインターを使用してクラスメソッドをシミュレートする必要がなくなったため、多少簡単になりました。これは、母国語でサポートされているためです。それ以外の場合は、当時、Cの場合と同様に、#defineがa-laパラメトリックプログラミングの唯一の友達でした。