The TypeDesc/ImageSpec strategy is exactly what I've done in oiio-rs, so can confirm it works well. I actually ended up implementing overrides for attribute_float, attribute_int etc, but I agree for the sake of simplicity it would be better (and more C-ish) to just do the void* version and let higher-level languages do whatever suits them best to polymorphize on top of that.
For null, again I wonder if we just leave the higher-level languages to deal with it? If the C layer gets passed a null pointer it's the higher-level layer that has the problem, not the intermediary. Yes it would be safer to check, but that's a redundant check for languages like Rust where you're guaranteed not to have a null pointer. Regarding exceptions, presumably it's possible to std::bad_alloc to still bubble up? Not sure what the best way of handling that is - having an exception thrown across the FFI boundary is bad news, but if it's std::bad_alloc then the process is almost certainly just going to terminate anyway... On Wed, 21 Oct 2020 at 08:22, Larry Gritz <[email protected]> wrote: > These are just guesses off the top of my head... > > > On Oct 19, 2020, at 8:35 PM, Scott Wilson <[email protected]> wrote: > > - In the case of TypeDesc, we could probably have a 1:1 interface between > C and C++. But something like ImageSpec would either have to replace the > std::string with *char or we may need to make ImageSpec completely opaque. > If we go for the latter, how do we want attrs such as width, height, etc to > be exposed? > > > TypeDesc should be 1:1 because it is POD and of fixed size by design. > ustring is as well. Those things should be 1:1 if at all possible. > > Anything that has C++ classes inside, or needs allocation, etc., should > probably just be an opaque pointer (everything is ImageSpec*, you never > have a live ImageSpec in C). We'd need to add methods: ImageSpec_width(), > ImageSpec_set_width(), etc. > > It doesn't make sense to have a *different* ImageSpec in C -- for example > by having an analogous structure with char* instead std::string -- because > how would yo pass it back to C++? Every C++ API call that takes an > ImageSpec& would need to be wrapped in such a way that it constructs a C++ > ImageSpec from the C ImageSpec, or vice versa, every time. No, in these > cases, you'll just have to pass the pointers. > > > - This may be a small thing, but when it comes to deleting objects, even > if they don't have pointers/own data, do we want to always include a > Type_delete method? For example, TypeDesc doesn't have/need a destructor. > If we say everything has a destructor, then would there be a problem with > that? > > > I think any time you have a C++ object that you are going to tend to pass > back and forth to C by pointer, you need an explicit C > allocator/constructor and deallocator/destroyer, because you don't know if > some time in the future the C++ implementation will have allocated > internals or have ctr/dtr that do important things, and if they should > acquire that in the future, you don't want to have to chase down every last > use on the C side to change the calls. > > > - How do we want to handle overrides? For example, if we have > OIIO::ImageSpec.attribute("my_attr", 1.0), would we want to convert that > into OIIO_ImageSpec_attribute_float("my_attr", 1.0)? What about with an > unsigned integer? I'm assuming we'd want to call it > OIIO_ImageSpec_attribute_uint(...). Also, in ImageInput, we have spec() > with a reference to the ImageSpec, and spec(subimage, miplevel). I'm > assuming the first will be OIIO_ImageInput_spec(), and the second will be > OIIO_ImageInput_spec_with_subimage_miplevel(...) or something like that. > > > I guess one question is whether we want to make a C API that is minimal, > enough to do the job for language bindings, or if we want to replicate > every last convenience function in C++, even the ones that are just trivial > overloads for different types. > > For example, in C++, the important attribute call is > > void ImageSpec::attribute (string_view name, TypeDesc type, const void > *value); > > which for C should be replaced with a const char* for the name. > > All the other overloads of attribute are just convenience functions to all > you to pass a value that is a direct type and not a pointer, and in those > cases remove the need to pass the TypeDesc. For example, > > attribute ("foo", my_float); > > is just a wrapper around > > attribute ("foo", TypeDesc::FLOAT, &my_float); > > So I think it's a judgment call about whether on the C side to just stick > with the one core function, or also add the convenience wrappers. > > > - Since we're probably going to be passing around ImageInput and ImageBuf > as pointers (since they're a unique_ptr in C++), do we want to always > assume that a pointer to an ImageInput/Output/Buf are not null, or add a > check in the code for that? > > > No opinion. Whatever is usual. I guess it's safer to always check. > > > - Does OIIO have exceptions? Either way, probably want to make sure any > exceptions passed from C++ don't cross over to the other side. > > > We don't! Whew! > > -- > Larry Gritz > [email protected] > > > > > _______________________________________________ > Oiio-dev mailing list > [email protected] > http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org >
_______________________________________________ Oiio-dev mailing list [email protected] http://lists.openimageio.org/listinfo.cgi/oiio-dev-openimageio.org
