Комментарии 27
Может, я невнимательно читал, но не увидел обоснования именно такого решения. Можно как-то прорезюмировать коротко? Я бы сервер скорее писал на C#, который предоставит весь функционал с помощью открытого и документированного протокола. А на Java потом только клиент. Что-то на подобие баз данных SQL. Немного громоздко, но по-моему менее громоздко чем портирование миллионов строк кода на плюсы и кодогенерация.
В случае с портированным кодом — проседания могут быть из-за разницы в механизмах работы с памятью. В C++ нет сжатия кучи, зато есть оверхед при копировании указателей (конструкторы-деструкторы, подсчёт ссылок) — из-за этого код, который часто создаёт много мелких объектов, будет работать медленнее после портирования. Насколько — полностью определяется соотношением рабочей нагрузки и операций по созданию объектов и работе с указателями. Счётные алгоритмы выглядят примерно одинаково на обоих языках, и с ними особой разницы мы не замечали — в основном всё упирается или в библиотеки, или в структуру языка.
Без реальных примеров преобразования кода эта статья смотрится несколько… пиарно, чтоль. Но спасибо, интересно таки. Ждем продолжения.
Сразу контр-аргумент по поводу «у нас никто не пишет на Haxe». Он очень похож на C#, у всех кто пришел в Haxe из C# или Java не было проблем с пониманием, как оно работает или проблем с адаптацией.
С ходу я вижу следующие риски при использовании Haxe, которых нет в текущей нашей парадигме:
- Сложности с сохранением структуры типов. Поскольку речь идёт о библиотеках, API после портирования C# > Haxe > C# должен оставаться тем же самым с точностью до всех библиотечных типов и интерфейсов. В противном случае это повлечёт необходимость изменения кода клиентов, чего, естественно, требуется избегать всеми возможными способами. Это относится не только к .Net, но и к API уже вышедших продуктов для других языков.
- Миграция с C# на Haxe должна быть выполнена идеально с первого раза (сейчас проблемы с портированным кодом никак не влияют на релизы для .Net), и до её завершения возможности начинать покрытие других языков нет.
- Утрата истории изменений кода, насчитывающего десятки миллионов строк и сотни человеколет разработки.
- Все подсистемы должны работать в точности одинаково на всех языках и в точности как в исходном коде .Net. Например, наши юзкейзы требуют, чтобы генерация XML или графики в портированном коде была полностью аналогична таковой в .Net.
- Непонятно, что будет при переезде с дотнетоспецифическими вещами вроде встраиваемых ресурсов, работы со сборками или кодом, завязанным на рефлексию.
Кроме того, я с ходу не нашёл ответа на вопрос о том, возможно ли портировать тесты на все поддерживаемые языки, что, естественно, тоже важно.
1) Повторить простые контракты можно. А вот со спецификой C# типа in/out, явной/неявной реализацией интерфейсов, перегрузкой операторов итд уже будет сложнее. С другими языками страшнее, особенно чем дальше они от managed языков с GC. Возможно придётся писать «фасады» под таргет платформу.
3) Повторите структуру файлов/папок старого проекта. На худой конец заставьте git думать что произошел ренейм из .cs в .hx.
4) Часть модулей придётся «переизобрести» в любом случае. Но вы уже это делали, когда эмулировали .NET. Добавлять чисто нативный код (C++, Java итд) после транспиляции никто не запрещает.
5) Рефлексия есть. Чисто платформными фишками Haxe позволяет пользоваться. Плюс это всего лишь генерация кода из кода, сборка остается за вами, и что вы туда дольете дело ваше. Работу с эмбеддед ресурсами можно сделать через абстракцию.
В общем, только человек знакомый со всем вашим проектом может правильно оценить риски, и не за один присест, нуансов много.
Просто взять из коробки Haxe и перенести туда 100% кода не получится. Но перевести 95% кода на Haxe и 5% на каждую платформу ИМХО вполне реально.
П.с. С++ несут больше всего рисков по портированию.
products.aspose.com/cells/family
Хотел высказать своё никому не нужное мнение.
Мне кажется, изначально нужно было сделать вашу либу "С++ first"а к остальным языкам наделать биндингов.
Это проверенный и многократно оправдавший себя подход.
Требование "уметь забиндиться на функцию из плюсовой либы" есть практически у любого языка программирования, это базовая вешь вообще.
Например, клинтская либа для Apache Kafka написана на С++ (librdkafka), а поверх этой плюсовой либы уже работает, например, C# обёртка (kafka-dotnet).
Таких примеров очень много.
Опять же, мне кажется при транспиляции вы будете всё время заперты в клетке — ни тебе yield, ни тебе span, вы так не будете успевать за развитием языка C# и будет всё сложнее нанимать C# програмистов с существенными ограничениями на использование фишек языка C#.
Короче, пока ещё не поздно предлагаю вам пересмотреть подход.
Новый код хотя бы начните на C++ или там Rust писать, уже толк будет.
К сожалению, изначально у нас весь код разрабатывается на C#, потому что это дешевле (и в плане оплаты труда программистов, и в плане количества проблем с управляемым кодом). Кроме того, вариант с оборачиванием библиотек C++ хорошо работает только в простых случаях (передача базовых типов, синхронность операций), а вот для работы с большими количествами объектов, передачи интерфейсов и делегатов придётся писать достаточно сложные обёртки.
Согласен, если туда-сюда надо гонять много объектов, то затраты по памяти на маршаллинг могут оказаться неприемлемыми.
Зря я начал что-то советовать не разобравшись до конца в проблеме.
А вот про "сложные обёртки" не соглашусь, транспилятор так-то тоже ни разу не простая получается обёртка.
Кажется в вашем случае проще было взять mono и выкинуть из него лишнее. А дальше сделать обертку на C++.
Одной из наших целей было получить возможно более простое решение для пользователя, который может просто подключить плюсовую библиотеку к плюсовому приложению и работать. Мост между CLR (не важно, mono или нативным) — это усложнение данной схемы, хотя, возможно, и оправданное.
Как мы автоматизировали портирование продуктов с C# на C++