You have to do some refactoring on the C side first to make the C code usable from the C side. You can't just port some big C project directly to Nim in one go. What I would recommend is make the entry point in Nim as a first step. Like take the `main` function in C, rename it to `cMain` or whatever, and call that from Nim. Make sure that works.
After that you move functionality to Nim slowly. You should also make sure that you can call Nim from C so that when you find a relatively self-contained module you can port that to Nim and expose it to C. The call stack while porting will look like this: * `Nim entry point -> the actual C code doing the work` * `Nim entry point -> the actual C code doing most of the work -> self contained Nim modules exposed to C` * ... (you do whatever you can to reduce `the actual C code doing most of the work` part) > the modules are too big... This is not really a problem, you just have to know how to navigate the code. > and depend of globals This is not fun, but at first it's a good idea to just create a catch-all "context" struct and put all the globals in that. Then you pass that struct to each function. Depending on how you want to go about this, you can break that struct into smaller structs in C or just replicate the same thing in Nim and pass that to C functions. > void functions What's wrong with void functions? If you're talking about functions that don't return values but take in pointer for result argument, that's not a problem, just create a specific type that's only returned from that function and used by its callers. Don't fear more types, they're your friends at this points. Do as much as you can in types and reduce the code / pointers / whatever. > hash tables, structs with function pointers These are also not fun. What I recommend is move all hash table types, structs with function pointers etc. to their own module and make sure thay're only accessed through functions that you control. Nobody accesses them directly. Nobody. So at first in Nim if you need to use those they're opaque types, you use them through the functions exposed from C. Same thing goes for the rest of the C codebase. You need to access a field of the struct? Create a `access_struct_field(struct*, result*)` function, and make sure only that is used everywhere. You need to create the struct? Make a `create_struct()` function that returns an opaque pointer. Nobody accesses the struct directly, only through the functions you provide. You can even make it a seperate library with only needed functions exposed so that you can be sure. Then when you're sure everyone accesses that stuff through functions you control, just swap those functions with Nim version! Easy peasy, lemon squeezy. I mean, not really, but you got the gist (by the way, I'm serious. NOBODY accesses those weird types directly until the whole codebase is in one language again). Oh, and you can't use cool Nim features such as destructors while you have C code in the codebase, so you'll have to create `deinit` procs for you Nim types expose them. Call deinit in your destructor in Nim, and call the exposed deinit function in C. Anyways, you'll have to be comfortable with C, I think that's pretty much clear by this point. Oh, and I'm not sure if I said this before but do not let weird types like type-specific-hash-tables and structs-with-function-pointers be accessed directly! What I wrote above is not specific to Nim, I believe Nim has cool C interoperability features such as inlining C directly, but the points I made above goes for any porting project that will communicate over the C ABI.