wrye-bash / CBash

Home of CBash.DLL sources
GNU General Public License v2.0
9 stars 4 forks source link

On Wrapping CBash #27

Open GandaG opened 4 years ago

GandaG commented 4 years ago

I'd like to bring forth a proposal - this is obviously a long-term goal but from short discussions with @Infernio I believe it's something achievable (since part of it already has been) and can be considered. Wrapping CBash in Rust, RBash.

TL;DR - Memory-safe with no loss of performance wrapper. Python 3 64bit only. Long term goal. Example. Built wheel.

CBash is written in C++ and afaik I believe none of the current devs have the skill in it needed to tackle working on CBash. The biggest draw of C/C++ is the perfomance, mainly via the low-level access, which let's you bypass some slower, safer abstractions to achieve it. It also benefits from being an old language, pretty much everything has been done with C++ and you can leverage that. And if there is something you need that hasn't, you can just use C, which was created around the time the primordial dust was gathering into what would one day become Earth.

Rust, on the other hand, is the hipster newcomer. Version 1 in 2015(?), with a new edition in 2018. A memory-safe systems programming language with influence from functional programming. No more segmentation faults or memory leaks. It seems to be more and more talked about as a credible alternative to C/C++ but it could just be confirmation bias on my end. Pretty easy to learn, too. Biggest drawback is its age - the existing material for it is in no way comparable to the others above. BUT, much like C++, we can just use C behind the scenes and hope it doesn't segfault or memory leak there :P In terms of performance I've seen benchmarks that placed it above C++ and below C, and I know that benches are not real life situations but the point is that performance shouldn't be an issue.

Rust also allows us to create a python extension module from code completely in rust. In the example repo I'm using pyo3, although there is another crate who can also create python extensions. This can be discussed later if this proposal is accepted.

As far as specific goals for the wrapper, I'd like to divide them into 3 stages. These are all long term goals that will be done slowly, over time.

Over these stages, we'll never lose access to CBash and indeed we'll be using as RBash's own backend, so there's never any "big" breaking step. Keep in mind this is just a proposal and nothing is really set in stone.

EDIT: A handy flowchart to help with deciding :P

CBash

Hope this gets greenlit!

Utumno commented 4 years ago

That's a nice idea - some random thoughts: would be nice to have an automated way of creating records based say on the xEdit defs (I would throw this as an idea to the modding community). And/or we could consider maybe cython and get us rid of cbash entirely. Haven't looked at it but maybe worth a look. If performance (memory and speed) is comparable we could stay with a python/cython patcher. Finally, comparing the P and C patchers we might come up with a new patcher routine. In general, if we find a way of having the logic and the records (reworked so we can generate them automatically from common defs like the xEdit) in python (cython) we might be able to get best of both worlds - python ease of coding and c performance

GandaG commented 4 years ago

Disclaimer - I intensely dislike Cython:

Cython would be absolutely fine up to stage 2, it's its specialty. But we'll suffer from the same problem we currently have - the C++ -> Cython API has to be "dumbed down" to C. Neither CPython or Cython understand the C++ goodies so we have to pass everything as opaque structs and functions.

With Rust we lose nothing - all the Rust high-level abstractions and goodies that are used for the backend (currently the CBash wrapper and in stage 3 the actual code) can and are used to build the Python wrapper.

The Rust solution isn't perfect either, pyo3 has a severe lack of documentation and doesn't support everything we could do with Python, but it's honestly getting there. For example, with cython we could use enums while with pyo3 we have to (for now) use this monstruosity ("enum variants" are global variables in modules imported into the main module).

Just my two cents on this. If we're willing to consider "rewriting" CBash, we should do it correctly from the start, half-assed solutions will just land us here in another few years. Cython is a good wrapper, but I believe we'd be losing value if moving CBash into it. Not saying that Rust is the answer, just that I don't think Cython is.