Error handling in libjpeg
Asked Answered
T

3

21

I am using the C++ JPEG library (libjpeg) and I have realized that when some functions fail exit() is called and the application is closed. How can I override this behavior and prevent the application from closing on libjpeg errors?

Tommietommy answered 8/11, 2013 at 11:16 Comment(0)
B
25

This is the default behavior of libjpeg. In order to handle errors with libjpeg, you'll have to define an error handling routine like this:

struct jpegErrorManager {
    /* "public" fields */
    struct jpeg_error_mgr pub;
    /* for return to caller */
    jmp_buf setjmp_buffer;
};
char jpegLastErrorMsg[JMSG_LENGTH_MAX];
void jpegErrorExit (j_common_ptr cinfo)
{
    /* cinfo->err actually points to a jpegErrorManager struct */
    jpegErrorManager* myerr = (jpegErrorManager*) cinfo->err;
    /* note : *(cinfo->err) is now equivalent to myerr->pub */

    /* output_message is a method to print an error message */
    /*(* (cinfo->err->output_message) ) (cinfo);*/      
    
    /* Create the message */
    ( *(cinfo->err->format_message) ) (cinfo, jpegLastErrorMsg);

    /* Jump to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
  
}

And then register it using jpeg_std_error.

FILE* fileHandler;
/* ... */
struct jpeg_decompress_struct cinfo;
jpegErrorManager jerr;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpegErrorExit;
/* Establish the setjmp return context for my_error_exit to use. */
if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error. */
    cerr << jpegLastErrorMsg << endl;
    jpeg_destroy_decompress(&cinfo);
    fclose(fileHandler);
    return 1;
}

You can find a complete example here.

Benefit answered 8/11, 2013 at 11:20 Comment(5)
Thank you so much, let me try it and I'll tell you :)Tommietommy
Wow, I didn't not about that jump mechanisms. Thank you so much, it worked!Tommietommy
You can try C++ lambda's with exceptions to create the exception handler inline -- see my answer https://mcmap.net/q/598231/-error-handling-in-libjpegFemi
How do I trigger a JPEG error to make sure this function works? Preferably in the compressor.Irascible
@philix Load a Motion Panorama from a Samsung Galaxy. They're f*cked. :DMetic
N
13

As the question was targetting C++, an alternative approach with exceptions:

Error handler:

void jpegErrorExit ( j_common_ptr cinfo )
{
    char jpegLastErrorMsg[JMSG_LENGTH_MAX];
    /* Create the message */
    ( *( cinfo->err->format_message ) ) ( cinfo, jpegLastErrorMsg );

    /* Jump to the setjmp point */
    throw std::runtime_error( jpegLastErrorMsg ); // or your preffered exception ...
}

Use it:

FILE* fileHandler;
/* ... */
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error( &jerr );
jerr.error_exit = jpegErrorExit;
try {
    jpeg_create_decompress( &cinfo );
    jpeg_stdio_src( &cinfo, fileHandler );
    /// ...
    jpeg_destroy_decompress( &cinfo );
    fclose( fileHandler );
}
catch ( std::runtime_exception & e ) {
    jpeg_destroy_decompress( &cinfo );
    fclose( fileHandler );
    throw; // or return an error code
}
Noun answered 9/2, 2015 at 21:39 Comment(4)
Thanks for your comment. I think that is interesting to note that C++ does not mean that we should use exceptions. In fact, the Google C++ coding style says "We do not use exceptions" and no more :-)Tommietommy
I agree, it doesn't mean you should, I just means you can. Furthermore: basically jumps can cause the same potential issues as exceptions do, are typically not well known to C++ programmers plus even bare further risks: they can easily mess up with your RAII. From www.cplusplus.com: "In C++, the implementation may perform stack unwinding that destroys objects with automatic duration. If this invokes any non-trivial destructors, it causes undefined behavior." And Google styling guides btw also mentions that there are exceptions for all those rules, especially when libaries are used.Noun
But libjpeg is C, not C++, so it has no destructors, nontrivial or otherwise, that setjmp/longjmp would tangle with.Solemnize
If the code calling libjpeg is C++, all kind of resources could be created using RAII within the code calling libjpeg.Noun
F
6

Using c++11 I implemented this using a lambda (similar to Marco's answer):

[](j_common_ptr cinfo){throw cinfo->err;}

which works well. Only then catch on 'struct jpeg_error_mgr *err'

i.e.

struct jpeg_error_mgr jerr_mgr;
cinfo.err = jpeg_std_error(&jerr_mgr);
jerr_mgr.error_exit = [](j_common_ptr cinfo){throw cinfo->err;};

and

   try
   {
      jpeg_create_decompress(&cinfo);

      ...
   }
   catch (struct jpeg_error_mgr *err)
    {
        char pszErr[1024];
        (cinfo.err->format_message)((j_common_ptr)&cinfo, pszErr);

         ...
    }
Femi answered 18/12, 2015 at 8:5 Comment(2)
Is a ptr to a lambda guaranteed to work? jpeglib is a C library -- if the lambda is not strictly a block of code (e.g., some other callable object that the compiler generates), then the unerlying C code will fail.Gabfest
Throwing exception through C code is tricky: #50070115Undersized

© 2022 - 2024 — McMap. All rights reserved.