Skip to content

Commit 5b166ef

Browse files
committed
Add two-tier OIIO_NODISCARD for ImageInput methods
Per @lgritz feedback, use a two-tier approach: - OIIO_NODISCARD (hard [[nodiscard]]) on create() and open(), where ignoring the return is always a bug. - OIIO_NODISCARD_ERROR (initially a no-op) on read_image() and read_scanlines(), where ignoring is sloppy but not catastrophic. This allows a gentle transition for downstream callers. Also fix unchecked return values at internal call sites: - imageioplugin.cpp: add null check after ImageInput::create() - jpegoutput.cpp: check open() return and propagate failure - py_imagebuf.cpp: check create()/open() return before writing Refs: #4781 Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
1 parent e399c8c commit 5b166ef

File tree

5 files changed

+66
-46
lines changed

5 files changed

+66
-46
lines changed

src/include/OpenImageIO/imageio.h

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,16 +1051,18 @@ class OIIO_API ImageInput {
10511051
/// A `unique_ptr` that will close and free the ImageInput when
10521052
/// it exits scope or is reset. The pointer will be empty if the
10531053
/// required writer was not able to be created.
1054-
static unique_ptr create (string_view filename, bool do_open=false,
1055-
const ImageSpec *config=nullptr,
1056-
Filesystem::IOProxy* ioproxy = nullptr,
1057-
string_view plugin_searchpath = "");
1054+
OIIO_NODISCARD static unique_ptr create (string_view filename,
1055+
bool do_open=false,
1056+
const ImageSpec *config=nullptr,
1057+
Filesystem::IOProxy* ioproxy = nullptr,
1058+
string_view plugin_searchpath = "");
10581059

10591060
/// Create an ImageInput using a UTF-16 encoded wstring filename.
1060-
static unique_ptr create (const std::wstring& filename, bool do_open=false,
1061-
const ImageSpec *config=nullptr,
1062-
Filesystem::IOProxy* ioproxy = nullptr,
1063-
string_view plugin_searchpath = "") {
1061+
OIIO_NODISCARD static unique_ptr create (const std::wstring& filename,
1062+
bool do_open=false,
1063+
const ImageSpec *config=nullptr,
1064+
Filesystem::IOProxy* ioproxy = nullptr,
1065+
string_view plugin_searchpath = "") {
10641066
return create(Strutil::utf16_to_utf8(filename), do_open, config,
10651067
ioproxy, plugin_searchpath);
10661068
}
@@ -1180,10 +1182,11 @@ class OIIO_API ImageInput {
11801182
///
11811183
/// @returns
11821184
/// `true` if the file was found and opened successfully.
1183-
virtual bool open (const std::string& name, ImageSpec &newspec) = 0;
1185+
OIIO_NODISCARD virtual bool open (const std::string& name,
1186+
ImageSpec &newspec) = 0;
11841187

11851188
/// Open the ImageInput using a UTF-16 encoded wstring filename.
1186-
bool open (const std::wstring& name, ImageSpec &newspec) {
1189+
OIIO_NODISCARD bool open (const std::wstring& name, ImageSpec &newspec) {
11871190
return open(Strutil::utf16_to_utf8(name), newspec);
11881191
}
11891192

@@ -1207,13 +1210,14 @@ class OIIO_API ImageInput {
12071210
///
12081211
/// @returns
12091212
/// `true` if the file was found and opened successfully.
1210-
virtual bool open (const std::string& name, ImageSpec &newspec,
1211-
const ImageSpec& config OIIO_MAYBE_UNUSED) {
1213+
OIIO_NODISCARD virtual bool open (const std::string& name,
1214+
ImageSpec &newspec,
1215+
const ImageSpec& config OIIO_MAYBE_UNUSED) {
12121216
return open(name,newspec);
12131217
}
12141218
/// Open the ImageInput using a UTF-16 encoded wstring filename.
1215-
bool open (const std::wstring& name, ImageSpec &newspec,
1216-
const ImageSpec& config OIIO_MAYBE_UNUSED) {
1219+
OIIO_NODISCARD bool open (const std::wstring& name, ImageSpec &newspec,
1220+
const ImageSpec& config OIIO_MAYBE_UNUSED) {
12171221
return open(name,newspec);
12181222
}
12191223

@@ -1411,15 +1415,17 @@ class OIIO_API ImageInput {
14111415
/// y, and z).
14121416
/// @returns `true` upon success, or `false` upon failure.
14131417
///
1414-
virtual bool read_image(int subimage, int miplevel, int chbegin, int chend,
1415-
TypeDesc format, const image_span<std::byte>& data);
1418+
OIIO_NODISCARD_ERROR virtual bool read_image(int subimage, int miplevel,
1419+
int chbegin, int chend,
1420+
TypeDesc format,
1421+
const image_span<std::byte>& data);
14161422

14171423
/// A version of `read_image()` taking an `image_span<T>`, where the type
14181424
/// of the underlying data is `T`. This is a convenience wrapper around
14191425
/// the `read_image()` that takes an `image_span<std::byte>`.
14201426
template<typename T>
1421-
bool read_image(int subimage, int miplevel, int chbegin, int chend,
1422-
const image_span<T>& data)
1427+
OIIO_NODISCARD_ERROR bool read_image(int subimage, int miplevel, int chbegin,
1428+
int chend, const image_span<T>& data)
14231429
{
14241430
static_assert(!std::is_const_v<T>,
14251431
"read_image() does not accept image_span<const T>");
@@ -1432,8 +1438,8 @@ class OIIO_API ImageInput {
14321438
/// contiguous strides in all dimensions. This is a convenience wrapper
14331439
/// around the `read_image()` that takes an `image_span<T>`.
14341440
template<typename T>
1435-
bool read_image(int subimage, int miplevel, int chbegin, int chend,
1436-
span<T> data)
1441+
OIIO_NODISCARD_ERROR bool read_image(int subimage, int miplevel, int chbegin,
1442+
int chend, span<T> data)
14371443
{
14381444
static_assert(!std::is_const_v<T>,
14391445
"read_image() does not accept span<const T>");
@@ -1480,17 +1486,19 @@ class OIIO_API ImageInput {
14801486
/// Added in OIIO 3.1, this is the "safe" preferred alternative to
14811487
/// the version of read_scanlines that takes raw pointers.
14821488
///
1483-
virtual bool read_scanlines(int subimage, int miplevel, int ybegin,
1484-
int yend, int chbegin, int chend,
1485-
TypeDesc format,
1486-
const image_span<std::byte>& data);
1489+
OIIO_NODISCARD_ERROR virtual bool read_scanlines(int subimage, int miplevel,
1490+
int ybegin, int yend,
1491+
int chbegin, int chend,
1492+
TypeDesc format,
1493+
const image_span<std::byte>& data);
14871494

14881495
/// A version of `read_scanlines()` taking an `image_span<T>`, where the
14891496
/// type of the underlying data is `T`. This is a convenience wrapper
14901497
/// around the `read_scanlines()` that takes an `image_span<std::byte>`.
14911498
template<typename T>
1492-
bool read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1493-
int chbegin, int chend, const image_span<T>& data)
1499+
OIIO_NODISCARD_ERROR bool read_scanlines(int subimage, int miplevel, int ybegin,
1500+
int yend, int chbegin, int chend,
1501+
const image_span<T>& data)
14941502
{
14951503
static_assert(!std::is_const_v<T>,
14961504
"read_scanlines() does not accept span<const T>");
@@ -1504,8 +1512,9 @@ class OIIO_API ImageInput {
15041512
/// contiguous strides in all dimensions. This is a convenience wrapper
15051513
/// around the `read_scanlines()` that takes an `image_span<T>`.
15061514
template<typename T>
1507-
bool read_scanlines(int subimage, int miplevel, int ybegin, int yend,
1508-
int chbegin, int chend, span<T> data)
1515+
OIIO_NODISCARD_ERROR bool read_scanlines(int subimage, int miplevel, int ybegin,
1516+
int yend, int chbegin, int chend,
1517+
span<T> data)
15091518
{
15101519
static_assert(!std::is_const_v<T>,
15111520
"read_scanlines() does not accept span<const T>");
@@ -1773,12 +1782,12 @@ class OIIO_API ImageInput {
17731782
///
17741783
/// @note This call was changed for OpenImageIO 2.0 to include the
17751784
/// explicit subimage and miplevel parameters.
1776-
virtual bool read_scanlines (int subimage, int miplevel,
1777-
int ybegin, int yend, int z,
1778-
int chbegin, int chend,
1779-
TypeDesc format, void *data,
1780-
stride_t xstride=AutoStride,
1781-
stride_t ystride=AutoStride);
1785+
OIIO_NODISCARD_ERROR virtual bool read_scanlines (int subimage, int miplevel,
1786+
int ybegin, int yend, int z,
1787+
int chbegin, int chend,
1788+
TypeDesc format, void *data,
1789+
stride_t xstride=AutoStride,
1790+
stride_t ystride=AutoStride);
17821791

17831792
/// Read the tile whose upper-left origin is (x,y,z) into `data[]`,
17841793
/// converting if necessary from the native data format of the file into
@@ -1904,14 +1913,14 @@ class OIIO_API ImageInput {
19041913
/// @param progress_callback/progress_callback_data
19051914
/// Optional progress callback.
19061915
/// @returns `true` upon success, or `false` upon failure.
1907-
virtual bool read_image (int subimage, int miplevel,
1908-
int chbegin, int chend,
1909-
TypeDesc format, void *data,
1910-
stride_t xstride=AutoStride,
1911-
stride_t ystride=AutoStride,
1912-
stride_t zstride=AutoStride,
1913-
ProgressCallback progress_callback=NULL,
1914-
void *progress_callback_data=NULL);
1916+
OIIO_NODISCARD_ERROR virtual bool read_image (int subimage, int miplevel,
1917+
int chbegin, int chend,
1918+
TypeDesc format, void *data,
1919+
stride_t xstride=AutoStride,
1920+
stride_t ystride=AutoStride,
1921+
stride_t zstride=AutoStride,
1922+
ProgressCallback progress_callback=NULL,
1923+
void *progress_callback_data=NULL);
19151924

19161925
/// @}
19171926

src/include/OpenImageIO/platform.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@
461461
# define OIIO_NODISCARD
462462
#endif
463463

464+
// OIIO_NODISCARD_ERROR is for functions returning error status (bool) where
465+
// ignoring the return is a bad practice but not always catastrophic. This is
466+
// initially a no-op to allow a gentle transition; it can later be defined as
467+
// [[nodiscard]] once downstream callers have been updated.
468+
#ifndef OIIO_NODISCARD_ERROR
469+
# define OIIO_NODISCARD_ERROR /* nothing for now */
470+
#endif
471+
464472

465473
// OIIO_NO_SANITIZE_ADDRESS can be used to mark a function that you don't
466474
// want address sanitizer to catch. Only use this if you know there are

src/jpeg.imageio/jpegoutput.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,15 +573,17 @@ JpgOutput::copy_image(ImageInput* in)
573573
ImageSpec in_spec;
574574
ImageSpec config_spec;
575575
config_spec.attribute("_jpeg:raw", 1);
576-
in->open(in_name, in_spec, config_spec);
576+
if (!in->open(in_name, in_spec, config_spec))
577+
return false;
577578

578579
// Re-open the output
579580
std::string out_name = m_filename;
580581
ImageSpec orig_out_spec = spec();
581582
close();
582583
m_copy_coeffs = (jvirt_barray_ptr*)jpg_in->coeffs();
583584
m_copy_decompressor = &jpg_in->m_cinfo;
584-
open(out_name, orig_out_spec);
585+
if (!open(out_name, orig_out_spec))
586+
return false;
585587

586588
// Strangeness -- the write_coefficients somehow sets things up
587589
// so that certain writes only happen in close(), which MUST

src/libOpenImageIO/imageioplugin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ pvt::catalog_all_plugins(std::string searchpath)
504504
// ImageInput::create will take a lock of imageio_mutex
505505
auto inp = ImageInput::create(f.first);
506506
lock.lock();
507-
if (inp->supports("procedural"))
507+
if (inp && inp->supports("procedural"))
508508
procedural_plugins.insert(f.first);
509509
}
510510
}

src/python/py_imagebuf.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ ImageBuf_repr_png(const ImageBuf& self)
270270

271271
std::unique_ptr<ImageOutput> out = ImageOutput::create("temp.png",
272272
&file_vec);
273-
out->open("temp.png", altered_spec);
273+
if (!out || !out->open("temp.png", altered_spec))
274+
return py::none();
274275
self.write(out.get());
275276
out->close();
276277

0 commit comments

Comments
 (0)