Введение.

В своей предыдущей статье я упомянул Hebron - утилиту для портирования кода с C на C# или на Rust.

В этой статье хотелось бы подробно расписать - как я с ней работаю.

Написанное можно воспринимать как своего рода мануал на тот случай, если кто-нибудь тоже захочет что-нибудь портануть.

Итак, портирование осуществляется в 4 шага:

Первый шаг: работа Hebron.

Hebron - это обычная Class Library и для работы с ней необходимо написать приложение на .NET.

К примеру,  вот так выглядит соответствующее приложение, портирующее StbImageSharp: ссылка на Github.

Непосредственно, обращение к Hebron осуществляется в этой строке:

var result = RoslynCodeConverter.Convert(parameters);

parameters содержат путь к исходному файле, о том с какими #defines его препроцессить, какие функции пропускать при портировании и т.д.

result - объект класса RoslynConversionResult. Он содержит набор Dictionary для каждого типа экспортируемого объекта(перечисления, делегаты, структуры, функции и т.д.): ключом является имя объекта, а значением сгенерированный код на C#.

После обращение к Hebron, приложение пишет код по разным файлам так, что каждому формату картинок соответствует свой файл: код связанный с загрузкой Png пишется в StbImage.Generated.Png.cs, Jpg - в StbImage.Generated.Jpg.cs и т.д.

Однако, к сожалению, сгенерированный код сходу не компилируется из-за синтаксических ошибок. Поэтому требуется второй шаг.

Второй шаг: ручные правки.

Для начала я форматирую в Visual Studio все сгенерированные файлы(Ctrl-A, Ctrl-K, Ctrl-F), чтобы они стали более читаемыми. Затем приступаю к правке синтаксических ошибок.

Это долгий и сложный процесс, где приходится много превозмогать. Поскольку часто попадаются такие ошибки перед которыми хочется просто развести руками.

Часто приходится вносить правки в оригинальный исходный файл: вникать в логику оригинального кода и переписывать её таким образом, чтобы Hebron таки смог переварить изменённый код.

Впрочем, о том какие различия между C и C# вызывают больше всего боли при портировании я планирую запилить отдельную большую статью с примерами кода.

Третий шаг: фикс багов.

Когда новая библиотека начинает компилироваться, то я обычно пишу какое-нибудь простенькое тестовое приложение, чтобы протестировать новый проект.

Если он работает не так, как оригинал, то приходится разбираться в чём заключается проблема. Т.е. запускать две студии: в одной дебажить новую библиотеку, а в другой - оригинал. И дебаггерские шаг за шагом находить расхождения в логике.

Порой на этой требуется не меньше времени и превозмогания, чем на фикс синтаксических ошибок.

К примеру, однажды я потратил много времени на поиск бага в StbVorbisSharp.

Проблема оказалась в файле StbVorbisSharp.Generated.cs, в строке:

int book = g->subclass_books[pclass * 8 + (cval & csub)];

cval & csub не были окружены скобочками. Из-за чего это выражение считалось не так, как надо.

Наконец, когда новая библиотека начинает более ни менее работать, я приступаю к последнему шагу.

Четвёртый шаг: чистка кода.

Здесь нужно сказать, что Hebron не скупится на скобочки(из описания предыдущего шага можно понять почему) или приведения к типам. Из-за чего сгенерированный код вызывает ужас у студии и она предлагает мне сделать множество автоисправлений. Выглядит это примерно так:

Чтобы решить эту проблему, я обычно запускаю Rider и осуществляю Code Cleanup(Ctrl-E, Ctrl-C). Я пользуюсь бесплатной лицензией, которую JetBrains предоставляет для проектов с открытым исходным кодом.

После очистки кода новая библиотека готова.

Кстати, пользуясь случаем, хочу поблагодарить JetBrains как за Rider, так и за лицензию. Спасибо!

Надеюсь, кто-нибудь из официальных представителей на хабре это прочитает.

Прочие порты.

Напоследок я бы хотел рассказать о моих портах, не входящих в StbSharp.

FontStashSharp. На самом деле, этот проект уже нельзя считать портом, поскольку фактически весь оригинальный код был переписан. Неизменным остался, пожалуй, лишь код упаковки прямоугольников на атласе в классе FontAtlas.

NvgSharp. Проект используется в отечественном игровом движке коммерческого уровня под названием Citrus.

DdsKtxSharp. Не слишком известный проект, умеющий загружать картинки в форматах DDS и KTX. Важно отметить, что декодировать он их не умеет. Предполагается, что декодированием будет заниматься видеоускоритель.

SamSharp. Самый неизвестный проект, синтезирующий человеческую речь по какому-то древнему алгоритму, используемому ещё в Commodore C64. Важно отметить, что у оригинального кода не было свободной лицензии, поэтому её нет и у порта.

Пример работы:

Эпилог.

Ну и совсем напоследок скажу, что готов принимать заявки на портирование. Для подачи такой заявки достаточно создать соответствующий кейс в Hebron.

freetype и sqlite можно не предлагать. Поскольку к ним я уже приступал. И сломался на втором шаге. Собственно, вот SqliteSharp. FreeTypeSharp же история не сохранила.

Вот теперь, действительно, всё :)

Комментарии (3)


  1. hbn3
    10.09.2022 00:03

    Конкуренты у Hebron есть?

    Если да — то не плохо было бы добавить секцию почему решились писать свою реализацию или можно матрицу сравнения фич.


    1. rds1983 Автор
      10.09.2022 00:25

      Знаю только о существовании https://github.com/immunant/c2rust


  1. AlexPoz
    11.09.2022 11:20
    -1

    Как я портирова питон на шарпы(питон с неадекватным шифрованием): на шарпах (яве пыхе жабаскрипте и плюсах) не удалось повторить алгоритм шифрования, вот такая эта гадюка. А требование было шифровать как у них. Решение: поднимаешь ещё один кубик, собираешь там все какахи и забываешь что там лежит)) Если не говорить о проблеме, то ее как будто бы и нет)

    Вообще мне религия не позволяет держать зоопарк и тем более на скриптовых языках, но как известно, мусульманину можно свинину, которая не касалась земли. Так и мне можно скриптик, который не касался змеи)