-
Notifications
You must be signed in to change notification settings - Fork 595
OIIO 2.0 Porting Guide
OpenImageIO 2.0 makes several (minor) non-backwards compatible API changes that will require some minor changes to client applications.
I will describe this for ImageInput, but the situation with ImageOutput is exactly the same.
The ImageInput::create()
function returned a raw pointer to an ImageInput, which the caller was then responsible for eventually destroying.
Old (OIIO 1.x) code:
ImageInput *in = ImageInput::open(filename);
...
in->close(); // all done!
ImageInput::destroy (in); // correctly destroy it
It was equivalent to call ImageInput::create()
and then separately open()
the resulting ImageInput. The same guidelines apply to create() as to open().
Note also that a common mistake was to call delete in
rather than ImageInput::destroy(in)
. That usually worked, and was harmless on Linux or OSX, but could be problematic on Windows systems where sometimes code that lives in different DLLs have different heaps and you should avoid an allocation and a free happening in different DLLs.
In OIIO 2.0, the create()
function was changed to return a std::unique_ptr<ImageInput>
, which makes resource management a lot easier. The new way is:
auto in = ImageInput::open(filename);
...
in->close();
// in will automatically free when it goes out of scope!
// Optional: in.reset();
In OIIO 2.0, when in
leaves scope, it will automatically release its resources. But if you really want to ensure that it happens right now, you can call in.reset()
.
If you have code that needs to work with both OIIO 1.8 and 2.0, you can use this idiom:
auto in = ImageInput::open(filename);
...
in->close();
#if OIIO_VERSION < 10903
ImageInput::destroy(in);
#endif
The auto
will catch the result of open() (or create()) regardless of it's a raw pointer or a unique_ptr. You only need to destroy() the raw pointer, which is the case if OIIO_VERSION < 10903. But if you are confident that your code will only need to support OIIO 2.0 or newer, then you don't need the #if
or the destroy
at all.
You may or may not run into this, depending on whether you have apps that use the ustring
class, and how you use it.
The ustring
class previously had a operator int()
that returned nonzero if the ustring had characters, 0 if empty. So you could do this:
ustring u; // it hold something, or maybe nothing
if (u) {
// u is not empty
}
This was well intentioned, but by being an int, it led to other problems where if you assigned to a char*
, it could do the int cast and then interpret that int as a pointer, and it just gets worse from there. This possibility was theorized to create risk of unintentional bugs in code that used ustring, and as soon as we took away the operator int()
, which will generate a compile-time error if you try to use it that way, indeed we found places in both OSL and USD that used it incorrectly and could never have worked properly (presumably those lines were on untested code paths or made bugs that nobody correctly diagnosed and fixed).
Anyway, long story short, the fix is to change
ustring u, v;
if (u) { ... }
if (!v) { ... }
to
if (!u.empty()) { ... }
if (v.empty()) { ... }