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?

Arno Duvenhage
  • 1,910
  • 17
  • 36
Didac Perez Parera
  • 3,734
  • 3
  • 52
  • 87

3 Answers3

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.

zakinster
  • 10,508
  • 1
  • 41
  • 52
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
}
  • 1
    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 :-) – Didac Perez Parera Feb 10 '15 at 21:26
  • 4
    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. – Marco Freudenberger Apr 27 '15 at 07:52
  • But libjpeg is C, not C++, so it has no destructors, nontrivial or otherwise, that setjmp/longjmp would tangle with. – Camille Goudeseune May 06 '15 at 20:00
  • 4
    If the code **calling** libjpeg is C++, all kind of resources could be created using RAII within the code calling libjpeg. – Marco Freudenberger May 19 '15 at 07:01
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);

         ...
    }
Arno Duvenhage
  • 1,910
  • 17
  • 36
  • 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. – wcochran Jan 10 '23 at 23:44
  • Throwing exception through C code is tricky: https://stackoverflow.com/questions/50069615/throwing-c-exception-through-c-function-call – Rémi May 06 '23 at 16:36