2013-12-05 4 views
4

Безопасно ли смешивать C++ 98 и C++ 11 в одном проекте? Под «смешиванием» я подразумеваю не только связывание объектных файлов, но и общие файлы заголовков, включенные в исходный код, скомпилированный с C++ 98 и C++ 11.Смешивание ароматов C++ в том же проекте

Фоном для вопроса является желание перевести хотя бы часть большой базы кода на C++ 11. Часть кода находится в C++ CUDA, скомпилированном для выполнения на GPU или CPU, и соответствующий компилятор в данный момент не поддерживает C++ 11. Тем не менее, большая часть кода предназначена только для CPU и может быть скомпилирована либо с использованием C++-флейвора. Некоторые файлы заголовков включены как в исходные файлы CPU + GPU, так и в CPU.

Если мы теперь скомпилируем исходные файлы только для процессора с компилятором C++ 11, можем ли мы быть уверены в нежелательных побочных эффектах?

+3

Однажды я спросил [аналогичный вопрос] (http://stackoverflow.com/q/10717106/596781). –

ответ

6

На практике, может быть.

Общеизвестно, что стандартная библиотека 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. internalstd::string существует полностью в DLL-коде. Код маршала перемещает данные с одной стороны на другую.

+1

Что касается 'std :: list': это будет libstdC++ (идет с gcc). –

+3

Этот ответ нуждается в иллюстрации ODR, скрывающейся за компилятором с летучей мышью в руке с надписью «НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ», ожидающим выскочить и избить дерьма у программиста. – Casey

Смежные вопросы