タイプのユーザーがスタック上などでタイプしてインスタンス化できるようにしながら、不透明なタイプを作成したい
struct Foo obj;
/*...*/
Foo_init(&obj);
Foo_do_something(&obj, param);
/*...*/
慣習的な不透明ポインターのアプローチでは、この例の最初の行は許可されません。回避策として、公開されているが不透明な「データ」配列を公開ヘッダー ファイル内に固定サイズで配置しています。これはいくつかの例ではうまくいくようですが、次の 2 つの点については少し確信が持てません。
Foo_get_bar と Foo_set_bar で行われているように、データ配列のアドレスをキャストしても安全ですか? これは私のテストでは問題なく動作しますが、疑わしいようです。
FOO_DATA_SIZE が固定されたままである場合、ユーザー コードで ABI の互換性を期待するのは合理的ですか?
main.c (ユーザーコードの例)
#include <stdio.h>
#include <limits.h>
#include "foo.h"
int main() {
struct Foo foo;
Foo_init(&foo);
Foo_set_bar(&foo, INT_MAX);
int bar = Foo_get_bar(&foo);
printf("Got bar: %d\n", bar);
}
foo.h (公開ヘッダー)
#pragma once
#include <inttypes.h>
#define FOO_DATA_SIZE (64)
struct Foo {
uint8_t data[FOO_DATA_SIZE];
};
void Foo_init(struct Foo *f);
void Foo_set_bar(struct Foo *f, int barval);
int Foo_get_bar(struct Foo *f);
foo.c (実装)
#include "foo.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
typedef int64_t bar_t;
struct Foo_private {
bar_t bar;
};
_Static_assert(sizeof(struct Foo_private) <= FOO_DATA_SIZE,
"FOO_DATA_SIZE is insufficient for struct Foo_private");
void Foo_init(struct Foo *foo) {
struct Foo_private foodata;
foodata.bar = (bar_t)0;
memcpy(foo->data, &foodata, sizeof(struct Foo_private));
}
void Foo_set_bar(struct Foo *foo, int barval) {
struct Foo_private *foodata = (void*)&(foo->data);
foodata->bar = (bar_t)barval;
int stored = (int)foodata->bar;
if (stored != barval) {
fprintf(stderr, "Foo_set_bar(%"PRId64"): warning: bar rounded to %"PRId64"\n",
(int64_t)barval, (int64_t)stored);
}
}
int Foo_get_bar(struct Foo *foo) {
struct Foo_private *foodata = (void*)&(foo->data);
bar_t bar = foodata->bar;
return (int)bar;
}