他の誰かが言ったように、バイナリ互換性のために、おそらくCAPIに制限するでしょう。
size
多くの場所のWindowsAPIは、構造体にメンバーを配置することでバイナリ互換性を維持しています。
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
};
size
そのような獣を作成するときは、それに応じてメンバーを設定する必要があります。
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
に追加のメンバーがある新しいバージョンのAPIに対してコードをコンパイルするとstruct
、そのサイズが変更され、そのsize
メンバーに記録されます。API関数は、そのようなものが渡されると、struct
おそらく最初にそのメンバーを読み取り、そのサイズに応じて、size
を処理するためのさまざまな方法に分岐します。struct
もちろん、これは進化が線形であり、新しいデータが常にの最後にのみ追加されることを前提としていstruct
ます。つまり、同じサイズのこのようなタイプのバージョンが異なることはありません。
ただし、このような獣を使用することは、ユーザーがコードにエラーを導入することを確実にするための優れた方法です。新しいAPIに対してコードを再コンパイルすると、sizeof(pluginInfo)
自動的に適応されますが、追加のメンバーは自動的に設定されません。struct
Cの方法を「初期化」することで、かなりの安全性が得られます。
PluginInfo pluginInfo;
std::memset( &pluginInfo, 0, sizeof(pluginInfo) );
pluginInfo.size = sizeof(pluginInfo);
ただし、技術的には、メモリをゼロ化しても各メンバーに妥当な値が設定されない可能性があるという事実は別として(たとえば、ゼロに設定されたすべてのビットが浮動小数点型の有効な値ではないアーキテクチャが存在する可能性があります)、これは厄介です。 3段階の構築が必要なため、エラーが発生しやすくなります。
解決策は、そのCAPIの周りに小さくインライン化されたC++ラッパーを設計することです。何かのようなもの:
class CPPPluginInfo : PluginInfo {
public:
CPPPluginInfo()
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
}
CPPPluginInfo(const char* author /* other data */)
: PluginInfo() // initializes all values to 0
{
size = sizeof(PluginInfo);
s_Author = author;
// set other data
}
};
クラスは、Cstruct
のメンバーが指す文字列をバッファに格納することもできるので、クラスのユーザーはそれについて心配する必要さえありません。
編集:これは私が思っていたほど明確ではないように思われるので、ここに例を示します。APIの新しいバージョンで、
まったく同じstruct
struct PluginInfo
{
std::size_t size; // should be sizeof(PluginInfo)
const char* s_Author;
const char* s_Process;
const char* s_ReleaseDate;
//And so on...
struct PluginVersion
{
const char* s_MajorVersion;
const char* s_MinorVersion;
//And so on...
};
PluginVersion o_Version;
int fancy_API_version2_member;
};
古いバージョンのAPIにリンクされたプラグインstruct
がこのように初期化されると
PluginInfo pluginInfo;
pluginInfo.size = sizeof(pluginInfo);
// set other members
これstruct
は古いバージョンになり、APIのバージョン2の新しい光沢のあるデータメンバーが欠落しています。へのポインタを受け入れる2番目のAPIの関数を呼び出すとPluginInfo
、古いPluginInfo
、短い1つのデータメンバーのアドレスが新しいAPIの関数に渡されます。ただし、バージョン2 API関数の場合、pluginInfo->size
はよりも小さいためsizeof(PluginInfo)
、それをキャッチし、ポインターを。を持たないオブジェクトを指しているものとして扱うことができますfancy_API_version2_member
。(おそらく、ホストアプリのAPIの内部は、PluginInfo
が付いた新しくて光沢のあるものでありfancy_API_version2_member
、PluginInfoVersion1
古いタイプの新しい名前です。したがって、新しいAPIが行う必要がPluginInfo*
あるのは、プラグインとして渡されたものをキャストすることPluginInfoVersion1*
だけです。そのほこりっぽい古いものを処理できるコードに分岐します。)
もう1つの方法は、新しいバージョンのAPIに対してコンパイルされたプラグインで、がPluginInfo
含まれ、fancy_API_version2_member
それについて何も知らない古いバージョンのホストアプリにプラグインされます。繰り返しになりますが、ホストアプリのAPI関数は、自身のAPI関数よりもpluginInfo->size
大きいかどうかをチェックすることでそれをキャッチできます。その場合、プラグインはおそらく、ホストアプリが認識しているよりも新しいバージョンのAPIに対してコンパイルされています。(または、プラグインの書き込みでメンバーを適切に初期化できませんでした。このやや脆弱なスキームの処理を簡素化する方法については、以下を参照してください。)
これに対処する方法は2つあります。最も簡単な方法は、プラグインのロードを拒否することです。または、可能であれば、ホストアプリはとにかくこれで動作し、最後のバイナリのものを単に無視することができますsizeof
PluginInfo
size
PluginInfo
渡されたオブジェクトで、解釈方法がわかりません。ただし、新しいAPIがどのようになるかを正確に知らずに、古いAPIを実装するときに
これを決定する必要があるため、後者は注意が必要です。