На счёт «где можно узнать больше» в отношении разработки .Net-приложений на C++ — с ходу не смогу привести ссылок. Всё на собственном опыте.
В сценарии, когда все два миллиона строк кода должны работать в контексте клиентского приложения — межпроцессная граница проводится там же, где она имеется у вас уже сейчас. Т. е. Recognition server — сам по себе (в своих процессах), его клиенты — сами по себе (в клиентских процессах). Если я, конечно, правильно понял суть вопроса.
Это если оставить за кадром, что вам пришлось перепроектировать интерфейс к библиотеке и писать n тысяч строк нового кода. А вот другое решение. Вы берёте ваш уже имеющийся код, с которым непосредственно взаимодействует клиент, делаете из него .Net-сборку, оставляя legacy-интерфейс (ведь у вас таковой уже был?) и добавляете «в нагрузку» .Net interop и managed-интерфейс, который позволяет работать с этой библиотекой из любого .Net-приложения. Обеспечиваете работоспособность именно этого кода на x86 и x64-платформах. Что в итоге? Вы получаете сборку, которая работает в контексте клиентских приложений (как .Net- так и «обычных»), а также с которой можно взаимодействовать посредством COM. Минимум затрат на маршалинг, никаких суррогатных процессов, широкой спектр клиентов (список которых будет только расти). Все довольны и счастливы.
Как следствие (и как подитог) трудоёмкость переноса кода на x64-конфигурацию, указанный в статье, несколько, гм… завышен. Как минимум, на порядок. Более того, я почти уверен, что проектирование и разработка суррогатного COM+-компонента сопоставима по трудоёмкости с переводом всей библиотеки, используемой непосредственно клиентом, на x64. Ещё раз отмечу — я достаточно хорошо представляю себе, о чём идёт речь, поскольку похожие задачи решать приходилось.
Иными словами, объём кода, реально работающий в контексте клиентского приложения — это порядка 100 тыс. строк. Из них 70 тыс. — это общие библиотеки. Собственно, именно этот объём, на самом деле, и требует «портирования» на x64. А никак не все 2 миллиона. Межпроцессный транспорт, очевидно, у вас уже есть (если Recognition Server может работать в другом процессе). А это куда как проще (утверждение основано на решении аналогичных задач). При наличии, понятное дело, хороших тестов на код общих библиотек. А то, что у вас получилось сейчас — это, гм… Я это себе представил следующим образом: x64-клиент взаимодействует с x86-прокси процессом (по COM+), который взаимодействует с x86-процессами, в которых крутится Recognition Server.
Ок. Fire and motion. Армия А вкладывает деньги в развитие сбруи, стремян, ручного оружия, обучению всадников новым техникам ведения боя и т. п. Армия Б решила оценить перспективность технологии бронированных машин и самоходных артиллерийских установок с минимально-необходимой поддержкой пехоты, вложило в это деньги и получило новое вооружение. Через некоторое время обе встречаются на поле боя и начинают fire and motion, fire and motion. Какая из этих двух армий имеет больше шансов на успех?
Я понимаю и поддерживаю принцип «работает — не трожь!», но! Решение проблемы использования библиотеки из x64-приложений не снимает необходимости решения проблемы нативной работы этой библиотеки в x64-конфигурации. Ничто в мире не вечно. DOS ушла. Win16 ушла. Уйдёт и Win32. Это случится не в ближайший год — определённо. Но то, что такое произойдёт в обозримом будущем — сие есть факт. Сейчас взаимодействие x86 и x64 кода достаточно дёшево. Послезавтра может существенно подорожать. Учитывая объём кода — к такому повороту событий надо быть как минимум готовым.
Статья хорошая и известная. Вопрос в другом — каков объём продаж FR, скомпилированного под Win16? Подозреваю, что нулевой (ввиду присутствия отсутствия этой операционки на более-менее современных платформах), да и версия эта уже не поддерживается. Я к тому, что да — действительно. Нет нужды слепо гнаться за всеми новинками на технологическом рынке. Но при этом надо здраво оценивать риск перейти в разряд «динозавров», которые никому не нужны ввиду сильного устаревания платформ, которые для этих «динозавров» требуются. Как никак, но x64 — это мощный тренд, который спустя некоторое время прочно обоснуется на рынке аппаратных платформ (да, на самом деле, уже обосновался — 32bit железа, наверное, уже не найти), а не очередная «новомодная технология от ведущего производителя». И отмирание 32bit-операционок — это просто вопрос времени.
Соглашусь. Несмотря на свою похожесть C++ и C# — это, всё таки, разные языки. И далеко не всегда то, что хорошо работает, будучи написанное на C++, будет также хорошо работать переложенное «1-в-1» на C#.
Сборки, полученные путём компиляции C++-кода студией в режиме «CLR Support» для разных конфигураций могут загружаться только в контекст приложения с такой же конфигурацией. Т. е. x32 managed-сборку (скомпилированную из C++-кода) нельзя загрузить с x64 managed application и наоборот. Возможно, конечно, что я что-то не знаю, но спотыкался об это не один раз.
Это всё замечательно. А что вы собираетесь делать, когда (если) столкнётесь с, например, проблемой утечек ресурсов на стороне клиента или сервера, которые в такой конфигурации более, чем возможны? С проблемой, что серверное приложение (в силу тех или иных обстоятельств) становится недоступным? Когда расходы на маршалинг данных (а то и двойной маршалинг в случае с .Net-клиентами) превысят все разумные значения? Я прекрасно понимаю, что проблемы решаются по мере их возникновения и написать COM-фасад к библиотеке гораздо дешевле, чем прошерстить 2 миллиона строк кода на предмет x64-ошибок. Но не получится ли так, что решать проблемы, которые возникнут в будущем, будет гораздо дороже, чем «портировать» с x32 на x64 сейчас? :)
Строго говоря, предложенное решение — не единственный способ «написать один раз и использовать для спектра языков». Можно, например, скомпилировать библиотеку в .Net-сборку (не переписывая на C#), попутно «прикрутив» фасад из managed-классов, написанных на C++/CLI с поддержкой COM Interop. В сухом остатке — у вас основной интерфейс ориентирован на более современную технологию, в опциях — возможность взаимодействовать посредством COM, при этом кодовая база — одна.
Более того, если ошибка в функционале/архитектуре — то она проявит себя и под x32 и под x64-конфигурации в одинаковой степени. Если x64-specific, то да, отлаживать надо именно те аспекты, которые связаны именно с x64-сборокой. А в целом рискну утверждать, что если в x64-сборке код работает, то и в x32-сборке тоже будет работать. Главное, чтобы комплект тестов на обе платформы был одинаков.
А «закладываться» на межпроцессное взаимодействие посредством COM/COM+ — тут могут быть свои грабли. Как минимум связанные с временем жизни серверного процесса.
1 — если пофиксить, то пример не будет собираться ни одним из отрелизиных в настоящий момент компиляторов.
2 — пофикшу.
3 — собрать в полном объёме (вместе с лямбдами) можно с помощью gcc 4.5 и gcc 4.6 (который сейчас только разрабатывается). Без лямбд — начиная с gcc 4.3. Собственно, NULL в коде появился из за того, что nullptr в gcc 4.5 не поддерживается (а вот в 4.6 уже есть).
И знаете, чисто субъективно вариантный мне нравится больше. Как-то в нем более строгое связывание получается. Модель данных — это чистая модель, незамутненная логикой обработки себя. Логика отрисовки (преобразование в html или еще что) выделена как отдельная сущность, как-то в стороне от модели.
Зато имеет существенный недостаток — высокая степень связности. Если в интерфейсной реализации поставщик данных зависит только от интерфейса потребителя (который может быть весьма «узким»), то в вариантном — требуется доступ к полному определению. Причём всех потребителей. В простых случаях на это можно пойти. В общем случае — не самое удачное архитектурное решение.
Идея интересная, но действительно не совсем понятно — почему в данном случае нельзя было применить обычный runtime-полиморфизм на виртуальных функциях? Приведённое решение хорошо подходит для варианта, когда набор типов, для которых требуется обеспечить полиморфное поведение, недоступен для модификации (third-party-классы, нативные типы). Чтобы не городить огород из собственных классов-обёрток variant + static_visitor — вполне подходят. А вот в приведённом примере…
Это если не заспамят и не зафлудят. В интернете же (не многим сложнее) создать нужное мнение по тому или иному вопросу. Типичные методы — флеш-мобы, атаки ботов, флуд, троллинг, и т. д. и т. п. Всё очень сильно зависит от постоянной аудитории ресурса, а также от настроений наиболее активной части этой аудитории. Это если не брать во внимание, скажем так, информационных агентов, которые намеренно (и за деньги) создают нужное мнение по тому или иному вопросу.
В сценарии, когда все два миллиона строк кода должны работать в контексте клиентского приложения — межпроцессная граница проводится там же, где она имеется у вас уже сейчас. Т. е. Recognition server — сам по себе (в своих процессах), его клиенты — сами по себе (в клиентских процессах). Если я, конечно, правильно понял суть вопроса.
А «закладываться» на межпроцессное взаимодействие посредством COM/COM+ — тут могут быть свои грабли. Как минимум связанные с временем жизни серверного процесса.
1 — если пофиксить, то пример не будет собираться ни одним из отрелизиных в настоящий момент компиляторов.
2 — пофикшу.
3 — собрать в полном объёме (вместе с лямбдами) можно с помощью gcc 4.5 и gcc 4.6 (который сейчас только разрабатывается). Без лямбд — начиная с gcc 4.3. Собственно, NULL в коде появился из за того, что nullptr в gcc 4.5 не поддерживается (а вот в 4.6 уже есть).
Зато имеет существенный недостаток — высокая степень связности. Если в интерфейсной реализации поставщик данных зависит только от интерфейса потребителя (который может быть весьма «узким»), то в вариантном — требуется доступ к полному определению. Причём всех потребителей. В простых случаях на это можно пойти. В общем случае — не самое удачное архитектурное решение.