Friday, December 13, 2013

Using a Lippincott Function for Centralized Exception Handling

The lippincott function is a way to wrap the handling of many different exceptions into a single reusable function.

Consider this use case:
  • A C++ library (say, libfoo) has many distinct exception types.
  • You want to wrap the C++ library with a C API.
  • You want error codes instead of exceptions for the C API.

A simple approach is to write every C API function as follows:
typedef enum foo_Result {
    FOO_OK,
    FOO_ERROR1,
    FOO_ERROR2,
    FOO_UNKNOWN
} foo_Result;

foo_Result foo_dothing()
{
    try
    {
        // Can throw MyException1 or MyException2
        foo::DoThing();
    }
    catch (const MyException1&)
    {
        return FOO_ERROR1;
    }
    catch (const MyException2&)
    {
        return FOO_ERROR2;
    }
    catch (...)
    {
        return FOO_UNKNOWN;
    }

    return FOO_OK;
}
There are some maintenance problems with the above approach:
  • What if you update DoThing() to also throw MyException3?
  • You must repeat the exception handling code for every API function.

Jon Kalb suggests the following refactoring method, which is named after Lisa Lippincott, who taught him the technique.
foo_Result lippincott()
{
    try
    {
        throw;
    }
    catch (const MyException1&)
    {
        return FOO_ERROR1;
    }
    catch (const MyException2&)
    {
        return FOO_ERROR2;
    }
    catch (...)
    {
        return FOO_UNKNOWN;
    }
}

foo_Result foo_dothing()
{
    try
    {
        foo::DoThing();
        return FOO_OK;
    }
    catch (...)
    {
        return lippincott();
    }
}
"throw;", when inside a catch block, simply rethrows the currently caught exception. It is not directly placed in a catch block within the lippincott() function, but it works anyways because it is transitively called from within the catch block of foo_dothing().

There are some important preconditions to calling this lippincott():
  • You cannot call the lippincott function from outside a catch block.
    • "throw;" outside of a catch block will call std::terminate().
  •  lippincott() must be noexcept. No exceptions should escape it.
    • It will leak out of the C API otherwise.

If we want extra safety, we can implement safeguards for the aforementioned preconditions:

To safely handle a violation of the first precondition, we can check that std::current_exception()  is not null previous to doing the "throw;"

To prevent an exception being thrown out of lippincott(), we can wrap the whole body of the function in a try/catch.

Here is the "extra safe"/paranoid version of lippincott():
foo_Result lippincott()
try
{
    try
    {
        if (std::exception_ptr eptr = std::current_exception())
        {
            std::rethrow_exception(eptr);
        }
        else
        {
            return FOO_UNKNOWN;
        }
    }
    catch (const MyException1&)
    {
        return FOO_ERROR1;
    }
    catch (const MyException2&)
    {
        return FOO_ERROR2;
    }
    catch (...)
    {
        return FOO_UNKNOWN;
    }
}
catch (...)
{
    return FOO_UNKNOWN;
}
The C API can now be written entirely in the style of foo_dothing(), which will centralize the error code conversion through the lippincott function.

Another interesting idea is to use the lippincott function to convert exceptions into string representations for debugging. This area is especially in need of the extra try/catch surrounding the function, since allocating dynamic strings can fail.

Full working example: http://ideone.com/m2ZfHN

The lippincott function was popularized by Jon Kalb in his exception-safe coding talks.
You can find his explanation here: youtube link

21 comments:

  1. comments: http://www.reddit.com/r/programming/comments/1ss9j2/c_secrets_using_a_lippincott_function_for/

    ReplyDelete
  2. This is an inferior solution compared to simply using a lambda. http://coliru.stacked-crooked.com/a/fce340725ab31872
    Only useful if you don't have access to lambdas.

    ReplyDelete
    Replies
    1. Thanks for sharing, that's a neat way to write your bindings. However, it doesn't quite solve the same problem as the Lippincott function.

      The Lippincott function is used as a generic way to recover the type of an exception and apply an operation on it from within a "catch(...)" block.

      Delete
    2. Yes, but that's a completely unnecessary operation.

      Delete
  3. Awesome code!!
    ty for sharing ...
    8-))

    ReplyDelete
  4. Please, do you know if this approuch works well into c++11/g++?

    ReplyDelete
  5. Good one.I appreciate you for sharing this knowledge.Thank you so much for the examples.As a developer in custom software development company namely yiioverflow it provides you the best custom software services.

    ReplyDelete
  6. You won't believe me, but I was planning to write a blog very similar to the one you have posted here. Great work!

    Java Training In Chennai

    AWS Training In Chennai

    ReplyDelete
  7. Do not use `catch(...)` if you intend to terminate on unknown exceptions, and definitely don't use the paranoid wrapper. You should rely on the runtime to give you an uncaught-exception termination, which is better because it does not invalidate the thrower's stack. If `noexcept` fails it's the runtime's fault.

    See https://devblogs.microsoft.com/oldnewthing/20191024-00/?p=103022.

    ReplyDelete
  8. Tamamen Otomatik Sistem ile Siparişleriniz 7 Gün 24 Saat Hızlı ve Sorunsuz Bir Şekilde Tamamlanmaktadır. instagram takipçi satın al ve daha fazlası.

    instagram takipçi satın al

    instagram beğeni satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram beğeni satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al

    instagram takipçi satın al


    takipçi satın al

    ucuz takipçi satın al

    tiktok takipçi satın al

    ReplyDelete
  9. Do you think to lead twitter page about C++ secrets? It is very easy to promote such page. You can just buy twitter followers for your page.

    ReplyDelete
  10. I am here to appreciate Dr OGU for using his herbal medicine to cure my Herpes virus. Is about 2 years and 6 months now I have been living with this virus and it has been a serious problem to me, I was so confused cause i have been taking several drugs to be cured but all of my effort was in vain,one morning i was browsing through the internet then i saw several testimonies about Dr. OGU curing people from Herpes virus and immediately i contacted Dr. on his email: drogugusolutionhome@gmail.com , i told him about my troubles and he told me that i must be cured, he gave me some instructions and which i rightly followed. so he prepared a herbal medicine and sent it to me via DHL which i used for 2 weeks and i was cured everything was like a dream to me and my Herpes virus was totally gone, dr .OGU , God bless you and give you more power and ability for more cures.i dont know if there is any one out there suffering for herpes virus or any of these diseases.. DIABETES, CANCER,GENITAL AND SIMPLEX HERPES,LOW SPERMS COUNT,SYPHILIS, HIV/AIDS, FIBRIOD,COPD,MENINGITIES,HEPATITIES B [HBV] DISEASES .etc why don't you contact dr.Ogu today and be free from your diseases because he is very good and honest Doctor. contact him via email; drogugusolutionhome@gmail.com or text/call via: +1 (719) 629 0982

    ReplyDelete