ImagingDataCommons / libdicom

C library for reading DICOM files
https://libdicom.readthedocs.io
MIT License
16 stars 7 forks source link

Add error object #34

Closed jcupitt closed 1 year ago

jcupitt commented 1 year ago

This (huge!) PR adds GError-style error handling to libdicom, plus docs, plus tests.

Resolves https://github.com/ImagingDataCommons/libdicom/issues/17

Here's the proposed new style:

    #include <stdlib.h>
    #include <dicom.h>

    int main() {
        const char *file_path = "does not exist";
        DcmError *error = NULL;

        DcmFile *file = dcm_file_create(&error, file_path, 'r');
        if (file == NULL) {
            printf("error detected: %s\n", dcm_error_code_str(dcm_error_code(error)));
            printf("summary: %s\n", dcm_error_summary(error));
            printf("message: %s\n", dcm_error_message(error));
            dcm_error_clear(&error);
            return 1;
        }

        dcm_file_destroy(file);

        return 0;
    }

Summary:

In the library, code that raises errors looks like this:

DcmFile *dcm_file_create(DcmError **error, 
                         const char *file_path, const char mode)
{   
    if (mode != 'r' && mode != 'w') {
        dcm_error_set(error, DCM_ERROR_CODE_INVALID,
                      "Open of file failed",
                      "Wrong file mode specified");
        return NULL;
    }

    ...

    if (file->fp == NULL) {
        dcm_error_set(error, DCM_ERROR_CODE_IO,
                      "Open of file failed",
                      "Could not open file for reading: %s", file_path);
        free(file);
        return NULL;
    }

    ...
}

Summary:

I also fixed a few small bugs and leaks I found as I went.

I removed optimisation from the default debug build since it was making debugging tricky. I also removed asserts from production builds -- this is more in line with practice in other libraries (glib, for example, will not abort() if you get a parameter wrong).

In my opinion, detailed review is not very useful at this stage, since libdicom is likely to be changing pretty quickly over the next couple of weeks. If the outline above sounds reasonable, I think we should just merge it.

Virtual IO next!

bgilbert commented 1 year ago

General approach SGTM. I'd suggest not falling back to logging a message if the error pointer is NULL, since that could encourage callers to generate log messages that their callers won't be able to intercept.

jcupitt commented 1 year ago

Hi Benjamin, thanks for having a look.

My idea was to make it possible to flag errors in code paths that don't have a DcmError** parameter (there are a few of these inside libdicom). Maybe it'd be better to force us to add the extra parameters instead.

How about logging these messages as DEBUG?

bgilbert commented 1 year ago

I think it makes sense to do both together. DEBUG logging would be useful for debugging missing error checks, but we should also be sure to add DcmError ** parameters to any function that might need one.

jcupitt commented 1 year ago

Yes, exactly. I changed it to log NULL errors to DEBUG, changed the docs to say you should always pass an error object, and changed dcm-dump to use DcmError.

jcupitt commented 1 year ago

Sorry to ping you, but could someone approve this? I can't merge otherwise :(

jcupitt commented 1 year ago

Thanks @hackermd, merged! I have two more queued up :(