Comments 22
Halt пытался реализовать идиоматичные байндинги, но его запал слегка угас.
Не то что угас, я по-прежнему не знаю, как это сделать и возможно ли это в принципе. Все подробности я описал на форуме.
Здóрово написано!
К сожалению да, авторы BWAPI написали его на C++ и это доставило массу головной боли всем, кто не на MSVC++ и Windows. Хотя их можно понять, когда они начинали (а это было аж в 2009м), решиться писать на C было сложно, поскольку сам проект был связан с реверс инжинирингом бинарников Starcraft, а он написан на C++. Вдобавок, они считали, что их разработка будет продуктивнее, если они будут делать на C++.
Но теперь, когда так много различных платформ, с которыми можно интегрироваться (Rust, Go, Node, JVM, Python, .Net) выбор C++ кажется архитектурным просчётом.
Но повторюсь, ребята проделали огромную работу, и они большие молодцы, пусть уж лучше законченная библиотека на C++, чем гипотетическая на C.
Но теперь, когда так много различных платформ, с которыми можно интегрироваться (Rust, Go, Node, JVM, Python, .Net) выбор C++ кажется архитектурным просчётом.
А вы считаете что лучше было бы на С?
С точки зрения интеграции с другими языками — однозначно.
есть и другие аспекты. В этом конкретном случае явно проще написать на плюсах, сделать биндинги для си и через них биндить другие языки
Есть swig, который поддерживает все вами перечисленное, за исключением Rust.

Ну а C можно использовать для Perl, Ruby, R, D, Smalltalk, lua, Ada, Fortran, Go, Julia, Erlang, Elixir… и этот список включает почти все языки, в отличие от swig.

Отличная статья! Я бы даже сказал: о наболевшем, а именно о мучениях с ABI C++.


Однако, я не смог понять, как Вам удалось справиться с соглашениями о вызовах. Если речь идёт о 32-битном коде, то просто объявления статических функций и заполнения ими таблицы виртуальных методов недостаточно, поскольку виртуальные методы используют соглашение о вызовах thiscall, тогда как функции обычно используют stdcall.


С 64-битным кодом всё несколько проще. Насколько я понял, там спасает ключ компилятора -mabi=ms. Тем не менее, мне очень интересно, что за магия происходит в wrap_handler и как Rust понимает, что нужно использовать соглашения о вызовах MSVC вместо System V без -mabi=ms.

Дело в том, что я избавился от виртуальных функций членов в публичном API с помощью opaque pointers. BWAPI-C скрывает C++ ABI внутри себя и правильно выбирает соглашение о вызовах благодаря нативному компилятору, а наружу торчит обычный C.


Например, для Windows BWAPI-C собирается cl.exe & link.exe, на выходе получаем 2 библиотеки — BWAPIC.lib и BWAPIC.dll (в CMake за это отвечает флаг CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS). GNU toolchain умеет работать с .lib, а динамическая линковка с .dll уже происходит во время запуска игры. Таким образом у нас нет головной боли с соглашением о вызовах.


Ну а с 64-битным кодом всё еще проще, его просто нет. Игра писалась 20 лет назад =)

> Ну а с 64-битным кодом всё еще проще, его просто нет. Игра писалась 20 лет назад =)

Чуть подкорректирую: оригинальный Старкрафт, он да, 32-битный, но OpenBW может в x64.
Логику ботов вполне можно писать под OpenBW, а потом компилировать в Win32 таргет, запустить под оригинальным Старкрафтом, убрать шероховатости, если есть.
Это неплохой вариант, если кому-то некомфортно под виндой (например, мне :-))

Чтобы понять, что происходит в wrap_handler надо понять, что происходит в сишной обертке createAIModuleWrapper: https://github.com/RnDome/bwapi-c/blob/master/src/AIModule.cpp. Я обернул C структуру в C++ класс, вернув сырой указатель. Чтобы создать Rust модуль, надо его обернуть в C, чтобы потом обернуть в C++, читайте https://github.com/RnDome/bwapi-rs/blob/master/src/aimodule.rs .


Если будут вопросы, я отвечу подробнее.

Спасибо, теперь понятно. До этого мне показалось, что Вы эмулируете полноценный объект С++ с помощью структуры AIModule.

Это было бы слишком непереносимым решением. А я не люблю играть с огнем =)

UFO landed and left these words here
Когда первый раз увидел это офигел, до сих пор самый сильный ИИ который я видел в играх. Но даже средний киберспортсмен в рамках 1 игры успевает понять как обыграть его.
А это вообще легально насколько возможно — вторгаться в адресное пространство другой программы? Я как-то читал, что все программы исполняются в своей «песочнице», и менеджер памяти контроллирует, чтобы другой процесс не лез куда его не просят.
Динамическая библиотека загружается в адресное пространство процесса, соответственно она имеет к нему доступ. То что вы пишете относится к виртуальной памяти и изоляции процессов.
Было бы круто перевести статью на английский — я рассказал коллегам про нее, многие rustаманы заинтересовались. Выдал им ссылки из конца, авось разберутся, но мне кажется, статья была бы популярна в англоязычном Хабре.
Only those users with full accounts are able to leave comments. Log in, please.