Предисловие: wchar_t в API есть зло
При разработке C API под Win и Linux всегда следует помнить, что фактический размер wchar_t не гарантируется стандартом, даже в рамках одной платформы, и разными компиляторами может реализовываться по-разному. Поэтому нет ничего удивительного, что wchar_t для gcc под Linux - это 4 байта, а для штатного компилятора в VisualStudio - 2 байта (
https://en.wikipedia.org/wiki/Wide_character).
Для удобства вызова API из C++ кода я все процедуры, принимающие или возвращающие строки, объявлял с wchar_t* в сигнатурах. Если стартовать проект сейчас, то я бы подумал насчет char16_t в
C++11, но грамматический движок писался раньше, так что wchar_t на тот момент выглядел естественно. В общем, код на C++, которому нужна обработка текста, выделял у себя wchar_t* блок, затем дергал
процедуру в solarix_grammar_engine.(dll|so) и все работало прозрачно. Ну или второй вариант заключался в дополнительных процедурах, принимающих или отдающих utf-8 строки, что было более удобно для Linux.
К чему это привело в .NET Core
PInvoke под Linux? Чтобы вызвать процедуру в неуправляемом коде, на входе которой нужна строка, мы должны описать
правило маршалинга из System.String в соответствующий C-тип. Для преобразования в массив 16-битных символов, точнее говоря в
UTF-16 с возможными суррогатными парами, есть стандартное правило, которое отлично справляется с преобразованием в wchar_t* под Windows, поскольку по обеим сторонам присутствуют 2-байтные символы в utf16. А вот для преобразования из System.String в массив 4-байтных wchar_t готового правила маршалинга я не нашел, да и вряд ли оно будет простым, учитывая нюансы перекодирования UTF16 в UTF32 и обратно.
В итоге враппер под Linux использует варианты процедур с представлением строк в кодировке utf-8, а для Windows остается штатный вариант с дефолтным маршалингом.
Использование .NET Core под Ubuntu
Перед использованием матриала установите .NET Core на свою систему. Для Ubuntu это делается предельно просто, через скачивание дистрибутива и запуск инсталяции. У меня на Ubuntu 16.04 все прошло без дополнительных вопросов. Подробности можно найти
тут
https://www.microsoft.com/net/core.
Теперь сборка запчастей.
Сначала надо скомпилировать динамическую библиотеку solarix_grammar_engine.so (это plain C api) и установить ее в нужное место в системе, создав синонимы, примерно так:
cp solarix_grammar_engine.so /usr/lib64
rm -f /usr/lib64/libgren.so
rm -f /usr/lib64/libgren.so.1
rm -f /usr/lib64/libgren.so.1.0
ln -s /usr/lib64/solarix_grammar_engine.so /usr/lib64/libgren.so
ln -s /usr/lib64/solarix_grammar_engine.so /usr/lib64/libgren.so.1
ln -s /usr/lib64/solarix_grammar_engine.so /usr/lib64/libgren.so.1.0
Сборка .NET wrapper
Далее нужно подготовить проект для сборки .NET wrapper, который позволит вызывать динамическую библиотеку C API из C# через механизм PInvoke. Идем в папку
/home/eek/Solarix/GrammarEngine/src/demo/ai/solarix/engines/grammar_engine_fx. Там лежит исходный текст враппера
Grammar_Engine_fx.cs и готовый проект для сборки под Linux gren.ubuntu.csproj. Другой файл проекта нужен для win-версии сборки под старый .NET Framework. Чтобы он не путался под ногами и не вызывал жалобы у команды dotnet, лишние проекты из этой папки лучше удалить.
Просим NuGet подкачать и установить все необходимые компоненты .NET Core (по поводу команды dotnet и того, что она делает, читайте официальное руководство тут:
https://docs.microsoft.com/ru-ru/dotnet/core/tutorials/using-with-xplat-cli):
dotnet restore
Теперь можно проверить, что враппер скомпилируется:
dotnet build
Успешное построение выглядит так:
Сборка финальной программы
Ок, теперь перейдем в
папку с консольной программой на C#, которая будет использовать грамматический движок через .NET обертку:
cd ../../Grammar_Engine/SimpleGREN_FX
Там лежит файл
Program.cs и проект SimpleGREN_FX.csproj.
Вы можете пересоздать проект с нуля командой:
dotnet new console
Но при этом файл Program.cs будет затерт, так что либо используйте мой проект, либо восстановитте потом Program.cs.
Просим Nuget восстановить необходимые компоненты:
dotnet restore
И добавляем ссылку на .NET Wrapper:
dotnet add SimpleGREN_FX.csproj reference ../../engines/grammar_engine_fx/gren.ubuntu.csproj
Собираем программу и запускаем:
dotnet run
Вы увидите в консоли что-то типа этого: