Какие проблемы переноса происходят с VC8 (VS2005) на VC9 (VS2008)?
Я унаследовал очень большой и сложный проект (фактически "решение", состоящее из 119 "проектов", большинство из которых являются DLL), который был собран и протестирован под VC8 (VS2005), и у меня есть задача перенести его на VC9 (VS2008).
Процесс портирования, который я использовал, был:
- Скопируйте файл VS8 .sln и переименуйте его в файл VC9 .sln.
- Скопируйте все файлы проекта VC8 и переименуйте их в файлы проекта VC9.
- Отредактируйте все файлы проекта VC9, s/vc8/vc9.
- Отредактируйте VC9 .sln, s/vc8/vc9/
- Загрузите VC9 .sln с VS2008 и позвольте IDE "преобразовать" все файлы проекта.
- Исправьте ошибки компилятора и компоновщика, пока я не получу хорошую сборку.
На этом последнем этапе я столкнулся со следующими проблемами.
1) Изменение способа вычисления оформленных имен, что приводит к усечению имен.
Это больше, чем просто предупреждение ( http://msdn.microsoft.com/en-us/library/074af4b6.aspx). Библиотеки, созданные с этим предупреждением, не будут связаны с другими модулями. Применение решения, указанного в MSDN, было нетривиальным, но выполнимым. Я решил эту проблему отдельно в разделе Как увеличить допустимую длину оформленного имени в VC9 (MSVC 2008)?
2) Изменение, которое не позволяет присвоить ноль итератору. Это в соответствии со спецификацией, и было довольно легко найти и исправить эти ранее допустимые ошибки кодирования. Вместо присвоения нулю итератору используйте значение end ().
3) область применения цикла for теперь соответствует стандарту ANSI. Еще одна легко решаемая проблема.
4) Больше места требуется для предварительно скомпилированных заголовков. В некоторых случаях требуется гораздо больше места. В итоге я использовал /Zm999, чтобы обеспечить максимальное пространство для PCH. Если использование памяти PCH снова увеличится, я предполагаю, что мне придется вообще отказаться от PCH и просто выдержать увеличение, которое уже занимает очень много времени.
5) Изменение требований для копирующих ctors и dtors по умолчанию. Похоже, что в шаблонных классах при определенных условиях, которые я до сих пор не выяснил, компилятор больше не генерирует ctor по умолчанию или dtor по умолчанию. Я подозреваю, что это ошибка в VC9, но может быть что-то еще, что я делаю неправильно. Если это так, я бы хотел знать, что это такое.
6) GUID в файлах sln и vcproj не были изменены. Это никак не влияет на сборку, которую я могу обнаружить, но, тем не менее, вызывает беспокойство.
Обратите внимание, что, несмотря на все эти проблемы, проект собрал, запустил и прошел обширное тестирование QA под VC8 . Я также перенес все изменения в проекты VC8, где они по-прежнему создаются и работают так же счастливо, как и раньше (с использованием VS2005/VC8). Итак, все мои изменения, необходимые для сборки VC9, по крайней мере кажутся обратно совместимыми, хотя регрессионное тестирование все еще продолжается.
Теперь о действительно сложной проблеме: я столкнулся с разницей в последовательности запуска между проектами VC8 и VC9. Программа использует распределитель мелких объектов, созданный по образцу Локи, в книге Андрея Александреску " Современный дизайн C++". Этот распределитель инициализируется с использованием глобальной переменной, определенной в основном программном модуле.
В VC8 эта глобальная переменная создается в самом начале запуска программы из кода в модуле crtexe.c. Под VC9 первый модуль, который выполняется, является crtdll.c, который указывает, что последовательность запуска была изменена. Библиотеки DLL, которые запускаются, по-видимому, сбивают с толку распределитель малых объектов, выделяя и освобождая память до того, как глобальный объект может инициализировать статистику, что приводит к некоторой ложной диагностике. На работу программы, похоже, не оказывают существенного влияния, но люди, отвечающие за обеспечение качества, не позволят ложной диагностике пройти их.
Есть ли способ форсировать создание глобального объекта перед загрузкой DLL?
С какими еще проблемами портирования я могу столкнуться?
5 ответов
Это сложная проблема, в основном потому, что вы унаследовали дизайн, который по своей природе опасен, потому что вы не должны полагаться на порядок инициализации глобальных переменных.
Это звучит как то, что вы могли бы попытаться обойти, заменив глобальную переменную одноэлементным, который другие функции получают, вызывая глобальную функцию или метод, который возвращает указатель на одноэлементный объект. Если объект существует во время вызова, функция возвращает указатель на него. В противном случае он выделяет новый и возвращает указатель на вновь выделенный объект.
Проблема, конечно, в том, что я не могу придумать одноэлементную реализацию, которая бы избежала проблемы, которую вы описываете. Может быть, это обсуждение будет полезно: http://www.oneunified.net/blog/Personal/SoftwareDevelopment/CPP/Singleton.article
Есть ли способ форсировать создание глобального объекта перед загрузкой DLL?
Как насчет опции DELAYLOAD? Так что библиотеки DLL не загружаются до их первого вызова?
Решение проблемы оказалось более простым, чем я думал. Проблема порядка инициализации была вызвана существованием нескольких глобальных переменных типов, производных от типов контейнеров std (основной недостаток дизайна, который предшествовал моей позиции в этой компании). Решением было заменить все такие глобалы на синглтоны. Их было около 100.
Как только это было сделано, порядок инициализации (и уничтожения) был под контролем программиста.
Это, безусловно, интересная проблема. У меня нет другого решения, кроме как, возможно, изменить дизайн так, чтобы не было зависимости от неопределенного поведения заказа или запуска link/dll. Рассматривали ли вы ссылку на старый линкер? (или каков бы ни был термин VS.NET)
Поскольку поведение вашей переменной и распределителя зависит от некоторого (неизвестного в то время) произвольного порядка запуска, я, вероятно, исправлю это, чтобы в будущем это не было проблемой. Я думаю, вы действительно спрашиваете, знает ли кто-нибудь, как сделать вуду в VC9, чтобы проблема исчезла. Мне тоже интересно это услышать.
Как насчет этого,
- Сделайте вашу основную программу тоже DLL, назовите ее main.dll, связав ее со всеми остальными, и экспортируйте основную функцию, скажем, mainEntry(). Удалить глобальную переменную.
- Создайте новый основной exe-файл, который имеет глобальную переменную и ее инициализацию, но не статически связан с какой-либо другой DLL-библиотекой приложения (за исключением содержимого распределителя).
- Этот новый main.exe затем динамически загружает main.dll с помощью LoadLibrary(), затем использует GetProcAddress для вызова mainEntry ().