One more thing to keep in mind for the c to c++ conversion...  The c++
compiler is stricter on some type checking.  You will have to make real
code changes to allow it to compile some c code.  Those changes are not
significant, but do have to be correct and verified.  At least that is what
happened when I did this for some test driver source files.

Stefan

On Fri, Feb 6, 2026 at 9:28 AM <[email protected]> wrote:

> John, thank you for your detailed reply.
> I've responded inline below
> If you are still unconvinced please say so and I'll abandon this.
> Thanks again.
> - russ
>
> > -----Original Message-----
> > From: John Ralls <[email protected]>
> > Sent: Thursday, February 5, 2026 9:04 PM
> > To: [email protected]
> > Cc: [email protected]
> > Subject: Re: exception fencing
> >
> >
> >
> > > On Feb 5, 2026, at 3:03 PM, <[email protected]>
> > <[email protected]> wrote:
> > >
> > > Since this is my first attempt to submit code to the GNC project I
> > > thought I’d pass my approach through the established developers first.
> > > I am interested in converting GNC code from C to C++ which will occur
> over
> > time so that the review quanta is not too overwhelming.
> > > I plan to convert build targets to only use the C++ compiler over time.
> > > Anything that is exposed with C linkage I plan to carry forward
> expecting at
> > some point all the callers will be C++ as well and most if not all of
> the C-
> > linkage requirements will go away.
> > > Any C++ code that is called from C-code will need a mechanism to
> prevent
> > C++ exceptions from bleeding back.
> > > I’ve developed a header file except-fence.hpp that provides the
> required
> > fencing.
> > > My first patch will be just this header file and the associated test
> files, so no
> > GNC code will be affected initially.
> > > I wanted to make the footprint small since I expect the fences will
> eventually
> > be removed.
> > > My approach is to replace the definition if the C-API functions with a
> > > macro that defines a stub C-API (extern “C”) Which instantiates an
> > ExceptFence object and uses the “forward_to” method call a local static
> > function with the original body.
> > >  For Example:
> > >  const char *
> > > func1(int a, gpointer b) // defined as extern “C” in header file { … }
> > >  Becomes:
> > >  SAFE_C_API_ARGS(const char *, func1, (int a, gpointer b), (a, b)) { …
> > > }  Which (essentially) expands to:
> > >  static const char *loc_func1(int a, gpointer b); extern “C” const
> > > char *func1(int a, gpointer b) {
> > >     ExceptFence exf;
> > >     return exf.forward_to(loc_func1, a, b); } static const char
> > > *loc_func1 (int a, gpointer b) { … }
> > >   The essence of forward_to is the following:
> > >         template<typename Func, typename... Args>
> > >         auto forward_to(Func&& func, Args&&... args)
> > >         {
> > >             try {
> > >                 m_except_data.exception_hit = true;
> > >                 auto result = func(std::forward<Args>(args)...);
> > >                 m_except_data.exception_hit = false;
> > >                 return result;
> > >             }
> > >             catch (const std::exception& ex) {
> > >                 m_except_data.exception_type = &typeid(ex);
> > >                 m_except_data.exception_message = ex.what();
> > >             }
> > >             using ReturnType = std::invoke_result_t<Func, Args...>;
> > >             if constexpr (std::is_integral_v<ReturnType>) {
> > >                 return
> static_cast<ReturnType>(m_exception_error_integer);
> > >             } else if constexpr (std::is_pointer_v<ReturnType>) {
> > >                 return static_cast<ReturnType>(nullptr);
> > >             } else {
> > >                 return ReturnType{};
> > >             }
> > >         }
> > >  This definition of forward_to() supports functions that return
> integer types,
> > pointer types, and structure types.
> > > That appears to support most of what we need that I’ve seen.
> > > There are 4 different SAFE_C_API macros to support all the
> combinations of
> > void args and void return types.
> > >  Anyway, if there are concerns about this approach, or request for more
> > details please let me know.
> > >  Thanks for your attention and support.
> >
> > Getting rid of C entirely requires changing the GUI framework; the
> minimum
> > effort would be gtkmm but even that will be a lot of work. It isn’t
> going to
> > happen any time soon.
>
> [russ] I figured there might be some elements that are impractical to
> convert
> But I still think it makes sense to enable C++ coding in the main. There
> are just a lot
> A nice features of C++ that would be good to take advantage of.
>
> > Also to consider is that exceptions either can’t be
> > allowed to leak through the Python and Scheme bindings or those bindings
> > need to acquire exception converters.
>
> [russ] Yeah I thought that the binding interfaces would need to remain as
> C-Linkage but can still be C++ code underneath.
>
> >
> > Just switching to compiling C code with a C++ compiler doesn’t introduce
> > exceptions so the need to wrap calls in try/catch is limited to cases
> when the
> > function is reimplemented to use some C++ thing that throws.
>
> [russ] right but the code will be available for use  of C++ constructs by
> developers but still callable from C compilation units
>
> > Note that
> > loc_func1 still has to check ext.m_except_data.exception_hit and if it’s
> true do
> > something with the rest. I suspect that that something will turn out to
> be
> > sufficiently not generic that having the template won’t really save much
> over
> > just catching the exception in the implementation function or writing a C
> > wrapper that handles exceptions in a way that’s appropriate for the
> execution
> > context at hand.
>
> [russ] I'm not sure I agree completely. The fence is only for catching
> unhandled exceptions (coding bugs), and the fence is in func1(), but the
> implementation logic is in loc_func1(). Even carefully designed C++ can
> have unusual circumstance that result in an unexpected exception. If there
> is any cleanup that needs to be done (beyond returning some error value),
> that will need to be done with a try/catch in loc_func1(). For functions
> that generate a lot of side effects and have complicated unwind logic its
> likely they won't be able to do that with a giant catch-all at the top
> anyway and need layers of try/catch to properly deal with them. I agree
> that if we mandate a giant catch-all at the top of all the C-APIs when
> implementing that might work for catching everything but in my experience
> its tricky to get right and error prone. E.g. if we instantiate an object
> before the try, those constructors can throw unprotected.
>
> >
> > BTW, “m_exception_error_integer” is a problem because it implies that
> that’s
> > a single value used for all cases and it’s not hard at all to think of
> cases where
> > different values would be needed for signaling an error.
>
> [russ] in the implementation of ExceptFence, the constructor can take any
> integer value and use that as the error value for integer return types. But
> you are right that the macros don't currently support that, but that would
> be easy to change. From what I've seen so far the return types for these
> APIs in the code base are either some kind of integer, some kind of
> pointer, or some kind of structure. The class currently supports specifying
> what the integer error value is for uncaught exceptions, but for the other
> return types it currently only supports nullptr/zeroed-out-structure, but
> that also could be fixed to support any error value of those types.
>
> So the exception fence really just ensures that uncaught exceptions return
> a known error type and that the implementation function does not need a v.
> carefully designed giant catch-all at the top.
>
> Void functions are tougher. Caught exceptions currently just return and
> leave an unknown state behind without any side effects. If there was some
> notification possible that would be nice.
>
> Once nice thing of having the fencing in a single class is if you suspect
> an unhandled exception e.g. in a void C-API func, it’s a single breakpoint
> in the code.
>
>
>
> >
> > Regards,
> > John Ralls
>
>
> _______________________________________________
> gnucash-devel mailing list
> [email protected]
> https://lists.gnucash.org/mailman/listinfo/gnucash-devel
>
_______________________________________________
gnucash-devel mailing list
[email protected]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel

Reply via email to