Как было сказано кем-то другим, для бинарной совместимости вы, скорее всего, ограничитесь C API.
API-интерфейс для Windows во многих местах поддерживает бинарную совместимость, поставив size
элемент в структуры:
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
С:
PluginInfo pluginInfo;
std::memset(&pluginInfo, 0, sizeof(pluginInfo));
pluginInfo.size = sizeof(pluginInfo);
Однако, даже если отбросить тот факт, что, технически, обнуление памяти может не поставить разумное значение в каждый элемент (например, там могут быть архитектурой, где все биты, установленные в ноль, не являются допустимым значением для типов с плавающей запятой), это раздражает и подвергает ошибкам, потому что для этого требуется трехступенчатая конструкция.
Выход из этого проекта заключался бы в разработке небольшой и встроенной C++-оболочки вокруг этого C API. Что-то вроде:
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
}
};
Класс может даже заботиться о хранении строки, на которую указывает членам С struct
«s в буфере, так что пользователи класса даже не придется беспокоиться об этом.
Edit: Так как кажется, что это не так очевидно, вырезать, как я думал, что это, вот пример.
Предположим, что тот же самый struct
будет в более поздней версии API получить дополнительный элемент:
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
будет старой версией, в которой отсутствует новый и блестящий элемент данных из версии 2 API. Если теперь он вызывает функцию второго API, принимающего указатель на PluginInfo
, он передает адрес старого PluginInfo
, одного члена данных, в новую функцию API. Однако для функции API версии 2 pluginInfo->size
будет меньше, чем sizeof(PluginInfo)
, поэтому он сможет поймать это и обработать указатель как указывающий на объект, который не имеет fancy_API_version2_member
. (Предположительно, внутренний API-интерфейс хост-приложения, PluginInfo
, является новым и блестящим с fancy_API_version2_member
, а PluginInfoVersion1
- это новое имя старого типа. Таким образом, все, что требуется новому API, - это отличить PluginInfo*
, которым он был вручен плагин в PluginInfoVersion1*
и ответвляются к коду, который может справиться с этой пыльной старой вещи.)
другой путь был бы плагин скомпилирован с новой версией API, где PluginInfo
содержит fancy_API_version2_member
, подключен к старше версию хост-приложения, которая ничего не знает об этом. Опять же, API-функции хост-приложения могут поймать это, проверив, pluginInfo->size
больше, чем sizeof
их собственные PluginInfo
. Если это так, плагин предположительно был скомпилирован против новой версии API, о которой знает хост-приложение. (Или запись плагина не смогла правильно инициализировать член size
. См. Ниже, как упростить работу с этой несколько хрупкой схемой.)
Есть два способа справиться с этим: простейшим является просто отказаться от загрузки плагина. Или, если возможно, приложение-хозяин может работать с этим так или иначе, просто игнорируя двоичный файл в конце объекта PluginInfo
, который он передал, который он не знает, как интерпретировать.
Однако последнее сложно, так как вам нужно решить этот , когда вы реализуете старый API, не зная точно, как будет выглядеть новый API.
посмотреть YAGNI .... –
Что заставляет вас думать, что то, за что вы не подготовлены, будет единым «пустотом»? –
@Mitch Wheat: Я уверен, что это YAGNI просто, что это «ты ** ** Нужно ли это» :) – nakiya