一意のハンドルを作成する場合は、とを使用して作成できmalloc()
ますstruct
。
typedef intptr_t HANDLE_TYPE;
HANDLE_TYPE init_buffer_traverse(double * src、size_t src_len);
int copy_buffer(HANDLE_TYPE h_traverse、double * dest、size_t dest_len);
void close_handle_buffer_traverse(HANDLE_TYPE h);
typedef struct
{{
double*ソース;
size_t source_length;
size_t位置;
}トラバーサル;
#define INVALID_HANDLE 0
/ *
*新しいトラバーサルハンドルを返します。失敗した場合は0(INVALID_HANDLE)を返します。
*
*トラバーサル状態を含むようにメモリを割り当てます。
*トラバーサル状態をソースバッファの先頭にリセットします。
* /
HANDLE_TYPE init_buffer_traverse(double * src、size_t src_len)
{{
TRAVERSAL * trav = malloc(sizeof(TRAVERSAL));
if(NULL == trav)
INVALID_HANDLEを返します。
trav-> source = src;
trav-> source_len = src_len;
trav-> position = 0;
return(HANDLE_TYPE)trav;
}
/ *
*トラバーサルハンドルに関連付けられているシステムリソース(メモリ)を返します。
* /
void close_handle_buffer_traverse(HANDLE_TYPE h)
{{
TRAVERSAL * trav = NULL;
if(INVALID_HANDLE!= h)
free((TRAVERSAL *)h);
}
int copy_buffer(HANDLE_TYPE h、
float * dest、int dest_length)
{{
TRAVERSAL * trav = NULL;
if(INVALID_HANDLE == h)
-1を返します。
trav =(TRAVERSAL *)h;
int copy_length = trav-> source_length --trav-> position;
if(dest_length <copy_length)
copy_length = dest_length;
for(int i = 0;i*強調テキスト*<copy_length;i ++)
dest [i] = trav-> source [trav-> position + i];
//次にcopy_buffer()が呼び出されたときに続行する場所を覚えておいてください
trav-> position + = copy_length;
copy_lengthを返します。
}
この種のスタイルは、C++が登場する前に一部のCコーダーが使用していたものです。このスタイルには、「クラス」のすべてのデータ要素を含むデータ構造が含まれます。クラスのほとんどのAPIは、最初の引数として、これらの構造体の1つへのポインターを取ります。このポインタはポインタに似ていthis
ます。この例では、このパラメーターの名前はtrav
です。
APIの例外は、ハンドルタイプを割り当てるメソッドです。これらはコンストラクターに似ており、戻り値としてハンドルタイプがあります。私たちの場合、namedinit_buffer_traverse
はと呼ばれることもありますconstruct_traversal_handle
。
「不透明なハンドル」値を実装するためのこの方法以外にも多くの方法があります。実際、一部のコーダーは、ハンドルの本質を曖昧にするために(たとえば、XORを介して)ビットを操作します。(このあいまいさは、そのような必要がある場合のセキュリティを提供しません。)
与えられた例では、宛先バッファポインタと長さがハンドル構造体に保持されることが最も意味があるかどうかはわかりません(sndlibを見ていませんでした)。もしそうなら、それは「トラバーサル」ハンドルではなく「コピーバッファ」ハンドルになり、この答えからすべての用語を変更したいと思うでしょう。
これらのハンドルは、現在のプロセスの存続期間中のみ有効であるため、ハンドルサーバーの再起動後も存続する必要があるハンドルには適していません。そのためには、ISAMデータベースと列IDをハンドルとして使用します。データベースのアプローチは、メモリ内/ポインタのアプローチよりもはるかに低速ですが、永続的なハンドルの場合、とにかくメモリ内の値を使用することはできません。
一方、単一のプロセスライフタイム内で実行されるライブラリを実装しているように思われます。その場合、私が書いた答えは、あなたの要件に変更した後、使用できるはずです。
補遺
あなたは私が上で述べたC++との類似性のいくつかの説明を求めました。具体的には、(上記のCコードと)同等のC++コードは次のようになります。
class TRAVERSAL
{
double * source;
size_t source_length;
size_t position;
public TRAVERSAL(double *src, size_t src_len)
{
source = src;
source_length = src_len;
position = 0;
}
public int copy_buffer(double * dest, size_t dest_len)
{
int copy_length = source_length - position;
if (dest_length < copy_length)
copy_length = dest_length;
for (int i = 0; i < copy_length; i++)
dest[i] = source[position + i];
// remember where to continue next time the copy_buffer() is called
position += copy_length;
return copy_length;
}
}
明らかな違いがいくつかあります。C ++バージョンは、少し冗長に見えません。これのいくつかは幻想です。これに相当するのclose_handle_buffer_traverse
はdelete
C++オブジェクトです。もちろんdelete
、のクラス実装の一部ではなくTRAVERSAL
、delete
言語が付属しています。
C ++バージョンでは、「不透明な」ハンドルはありません。
Cバージョンはより明確であり、プログラムの実行に応じてハードウェアによって実行されている操作をより明確にする可能性があります。
HANDLE_TYPE
Cバージョンは、ポインター型ではなく「不透明なID」を作成するためにキャストを使用する方が適しています。C ++バージョンは、別のレイヤーを追加しながら同じことを実現するAPIに「ラップ」することができます。現在の例では、このクラスのユーザーは、のコピーを保持しますがTRAVERSAL *
、これは完全に「不透明」ではありません。
関数copy_buffer()
では、C ++バージョンはtrav
ポインターについて言及する必要はありません。代わりに、コンパイラーが提供するthis
ポインターを暗黙的に逆参照するためです。
sizeof(TRAVERSAL)
CとC++の両方の例で同じである必要があります-vtableがなく、C ++の実行時型識別がオフになっていると仮定すると、C++クラスには最初の例のC構造体と同じメモリレイアウトのみが含まれます。
C ++では「透明性」のペナルティが低くなるため、C++で「不透明ID」スタイルを使用することはあまり一般的ではありません。のデータメンバーはclass TRAVERSAL
であるprivate
ためTRAVERSAL *
、APIユーザーとのAPI契約を破るために誤って使用することはできません。
不透明なIDとクラスポインタの両方が悪意のあるAPIユーザーからの悪用に対して脆弱であることに注意してください。たとえば、不透明なIDまたはクラスポインタのいずれかを直接キャストして、IDの所有者がを介してメンバーを直接double **
変更できるようにすることができます。source
メモリー。もちろん、API呼び出し元はすでに信頼している必要があります。この場合、API呼び出しコードは同じアドレス空間にあるためです。ネットワークファイルサーバーの例では、メモリアドレスに基づく「不透明なID」が外部に公開されている場合、セキュリティに影響を与える可能性があります。
通常、APIユーザーの信頼性を損なうことはありませんが、C ++キーワードprivate
には「強制力」がなく、プログラマー間の合意を指定するだけであり、人間からの指示がない限り、コンパイラーもこれを尊重します。
最後に、C++クラスポインタは次のように不透明なIDに変換できます。
typedef intptr_t HANDLE_TYPE;
HANDLE_TYPE init_buffer_traverse(double *src, size_t src_len)
{
return (HANDLE_TYPE)(new TRAVERSAL(src, src_len));
}
int copy_buffer(HANDLE_TYPE h_traverse, double * dest, size_t dest_len)
{
return ((TRAVERSAL *)h_traverse)->copy_buffer(dest, dest_len);
}
void close_handle_buffer_traverse(HANDLE_TYPE h)
{
delete ((TRAVERSAL *)h);
}
そして今、私たちの「同等の」C++の簡潔さはさらに疑問視されるかもしれません。
C ++に関連する古いスタイルのCプログラミングについて私が書いたことは、C++がこのタスクに適していると言うことを意味するものではありませんでした。つまり、データのカプセル化と実装の詳細の非表示は、C++スタイルとほぼ同形のスタイルを介してCで実行できるということです。これは、Cでプログラミングしているのに、残念ながら最初にC++を習得したことがあるかどうかを知るのに役立ちます。
PS
これまでの実装で次のものが使用されていることに気づきました。
dest[i] = (float)source[position + i];
バイトをコピーするとき。dest
とは両方ともsource
(double *
つまり、両方ともdouble
値を指している)ので、ここでキャストする必要はありません。また、からにキャストすると、浮動小数点表現の精度が低下する可能性がありますdouble
。float
したがって、これは削除して次のように言い換えるのが最適です。
dest[i] = source[position + i];