На практике, может быть.
Общеизвестно, что стандартная библиотека C++ 11 и C++ 03 не согласна с тем, что представляет собой объект пространства имен std
. Например, sizeof(std::vector<int>)
заметно изменился в разных версиях компилятора на земле MSVC. (он уменьшился по мере их оптимизации).
Другие примеры могут представлять собой разную кучу с каждой стороны ограждения компилятора.
Итак, вы должны тщательно «брандмауэр» между двумя деревьями-источниками.
Теперь некоторые компиляторы стремятся минимизировать такие бинарные изменения совместимости, даже ценой нарушения стандарта. Я считаю, что std::list
без счетчика размера может быть примером этого (что нарушает C++ 11, но я помню, что по крайней мере один поставщик предоставил не совместимый со стандартами std::list
для поддержки двоичной совместимости - я не помню, какой из них).
Для двух компиляторов (а компилятор в C++ 03 и C++ 11 - разные компиляторы) у вас появятся некоторые гарантии ABI. Вероятно, существует большая часть языка, для которого ABI согласится, и на этом уровне вы относительно безопасны.
Чтобы быть достаточно безопасным, вы захотите обработать другие файлы версий компилятора, как если бы они были сторонними DLL (библиотеки с задержкой загрузки), которые не ссылаются на одну и ту же стандартную библиотеку C++. Это означает, что любые ресурсы, переданные от одного к другому, должны быть упакованы с кодом уничтожения (т. Е. Возвращены в DLL, откуда он был уничтожен). Вам придется либо исследовать ABI двух стандартных библиотек, либо не использовать его в общих файлах заголовков, поэтому вы можете передавать такие вещи, как интеллектуальные указатели между DLL.
Даже более безопасный подход заключается в том, чтобы раздеться до интерфейса стиля C с другой базой кода и только обрабатывать дескрипторы (непрозрачные типы) между двумя базовыми кодами. Чтобы сделать это разумным, взломайте только заголовок-файл только mojo, который обертывает интерфейс стиля C в довольно C++-коде, просто не передавайте эти объекты C++ между базами кода.
Все это боль.
Например, предположим, что у вас есть функция std::string get_some_string(HANDLE)
, и вы не доверяете стабильности ABI.
Итак, у вас есть 3 слоя.
namespace internal {
// NOT exported from DLL
std::string get_some_string(HANDLE) { /* implementation in DLL */ }
}
namespace marshal {
// exported from DLL
// visible in external headers, not intended to be called directly
void get_some_string(HANDLE h, void* pdata, void(*callback)(void*, char const* data, std::size_t length)) {
// implementation in DLL
auto r = ::internal::get_some_string(h);
callback(pdata, r.data(), r.size());
}
}
namespace interface {
// exists in only public header file, not within DLL
inline std::string get_some_string(HANDLE h) {
std::string r;
::marshal::get_some_string(h, &r,
[](void* pr, const char* str, std::size_t length){
std::string& r = *static_cast<std::string*>(pr);
r.append(str, length);
}
);
return r;
}
}
Таким образом, код вне DLL делает auto s = ::interface::get_some_string(handle);
, и он похож на интерфейс C++.
Код внутри DLL реализует std::string ::internal::get_some_string(HANDLE);
.
marshal
«ы get_some_string
обеспечивает интерфейс в стиле Си между ними, что обеспечивает лучшую совместимость, чем бинарную опираясь на топологии и реализации std::string
, чтобы оставаться стабильными между DLL и кодом с помощью DLL.
std::string
полностью в пределах кода, не относящегося к категории DLL. internal
std::string
существует полностью в DLL-коде. Код маршала перемещает данные с одной стороны на другую.
Однажды я спросил [аналогичный вопрос] (http://stackoverflow.com/q/10717106/596781). –