If you were going to use an interface like a C++ stream, I think you should *provide* one rather than taking one as an argument. It lets you decide to do it in a thread or even a separate process with the buffer in shared memory or even just an OS pipe.
Conversation
Replying to
I mean giving them an input stream rather than taking an output stream. Taking output stream or writing to FILE provided by a caller will be way less efficient because essentially everything will still want buffering but you'll be doing indirect calls and maybe locking per byte.
1
1
It has to be buffered somewhere for efficiency. Seems like the only way to get good performance is doing the buffering yourself if you're going to be getting the output byte by byte.
1
1
Taking an output stream / FILE is just a fancy way of taking a function pointer with a write(ptr, size) method with interoperability issues and a lot of baggage. Look at git.musl-libc.org/cgit/musl/tree and git.musl-libc.org/cgit/musl/tree for example. It's not pretty and this is a clean impl.
1
1
Replying to
yeah this totally makes sense. I think I'm going to have the FFI provide a write(ptr,size) function which will be called at will by the simulation (at the beginning, simply after every simulation step) and treat the C++ interface as internal/unstable for now
1
Replying to
That's the internal iterator approach and then the other approach is giving them a stream object where they can call read to get a data structure with pointer / size, which is more powerful and lets it be mapped to something like Rust iterators, etc. Doesn't really matter much.
1
1
I would personally give them a stream object just because it's more powerful and passing in a function pointer complicates it. For example, you might want to provide a boolean return value so they can tell you to stop iterating, but maybe they only want to stop temporarily...
1
1
Also ends up being annoying to wrap those kinds of APIs from languages with exceptions since you have to catch exceptions, tell it to stop iterating and then have a system for storing and throwing the exception, etc. Just nicer to avoid using function pointers at all IMO.
2
1
It does make your life harder since you have to store all the state in the stream object, but it's a nicer API for them. The usual trade-off of internal vs. external iterators. I obviously think external is the way to go since I fought pretty hard to get Rust to switch. :P
1
2
I suppose C++ coroutines make this much easier to do but I haven't tried / looked at them at all and you're using C++11.

