Boost logo

Boost :

From: Jean-Louis Leroy (jl_at_[hidden])
Date: 2025-04-30 01:15:31


Hi Andrzej,

Thank you for your input.

> > When an error is encountered, the program is terminated by a call to abort.
>
> And then in the next sentence it talks about different debug and release
> policies, and ultimately, I do not know if by default abort() is called or
> not.

The entire paragraph reads:

> When an error is encountered, the program is terminated by a call to abort. If
> the policy contains an error_handler facet, it provides an error member
> function (or overloaded functions) to be called with an object identifying the
> error. The release and debug policies implement the error facet with
> vectored_error_handler, which wraps the error object in a variant, and calls a
> handler via a std::function. By default, it prints a description of the error
> to stderr in the debug policy, and does nothing in the release policy.

It is intended to say "abort is called", but in debug builds (only) you get an
error message.

But I can see how all the verbiage can be confusing. It is important to me that
error processing be customizable. That's what policies are for. But that is also
an "advanced feature". I did not want to write something that is incorrect
(diagnostic in debug builds only, abort always) because error handling is
customizable. The flip side is that I wrote something obscure.

I am beginning to think that I should split the tutorials in two parts:

Tutorials
    hello world
    multiple dispatch
    headers and namespaces
    friendship
    smart pointers
    error handling
        high-level explanation of what an "error" is
        HERE just say "abort always", with diagnostics in debug builds
        (don't mention policies)
    performance
Advanced features
    virtual ptr alternatives
    core api
    policies
    custom advanced error handling
    custom rtti
    dynamic loading

> So, I dun a Compiler Explorer test:
> https://godbolt.org/z/3fW8j4eTM
> And indeed, I get something that looks like abort()

What you get is a SIGSEGV, and that is because you did not register class YNode
- which is the dynamic type of `n`. In release mode there are no checks,
whatsoever. The perfect hash function works only with inputs from its
domain. Otherwise it returns a random value. Let's fix this:
https://godbolt.org/z/31abxnTPv

  At this point I was very surprised to still get a SIGSEGV. When I compile
  locally, I get a SIGABRT as expected - we are calling the method with a
  virtual tuple that doesn't have a matching overrider.

  Then I tried:

> #include <stdlib.h>
>
> int main() {
> abort();
> return 0;
> }

With your choice of compiler, CE says: Program terminated with signal: SIGSEGV.
See https://godbolt.org/z/c69d76feE

> This is the worst thing that can happen: a random runtime behavior. Is this
> a bug? Or am I missing something?

I agree with you, `abort` has a random runtime behavior, SIGABRT or SIGSEGV,
god(bolt) knows why, that is baaad ;-) ;-)

> But when I wrap the call into a try-catch block, all of a sudden, the
> implementation seems to pick the wrong overrider and return a value,
> clearly not the intended one. And the program goes on:
> https://godbolt.org/z/Y6z3o56h5

Let's add an executor, shall we? https://godbolt.org/z/8sf3GsTrc Now we get:

> Program returned: 139.
> Program stderr
> Program terminated with signal: SIGSEGV

For the same reason: YClass is not registered. Let's register it:
https://godbolt.org/z/WqTMxhn94 ... and now we get a SIGSEGV again, which seems
to be what `abort` does in these examples.

Let's switch to a debug build: https://godbolt.org/z/EK5nz3daz

Now we get:

> Program returned: 139
> Program stderr
> no applicable overrider for boost::openmethod::method<value_boost_openmethod
> (boost::openmethod::virtual_ptr<Node, > boost::openmethod::policies::debug>),
> int, boost::openmethod::policies::debug>(void)
> Program terminated with signal: SIGSEGV

Better!

You did help me find a bug though. Let's reinstate the error (the missing YNode
registration) and compile again in debug mode. This is wrong:
https://godbolt.org/z/57xorYsGT The program is incorrect all right, but, in
*debug* mode, you should get an error. Something was lost in translation from
YOMM2 to OpenMethod.

I created a "review" branch where I will put code and doc fixes. Here is what
you get with the fix: https://godbolt.org/z/WGEn3xvG6

> Program returned: 139
> Program stderr
> unknown class YNode
> Program terminated with signal: SIGSEGV

...which is the intended output.

I should write a troubleshooting guide.

> Next question that naturally arises: how do I declare `noexcept` open
> methods with this library?

I took a shot at implementing noexcept support. It was not difficult. I didn't
keep it because MSVC was not capable enough. And also because noexcept didn't
seem hugely useful outside of specific contexts which, I believe, don't
intersect much with the contexts where open-methods make sense. But I am super
flexible on this, I can bring back noexcept support for capable compilers.

J-L

On Tue, Apr 29, 2025 at 5:30 PM Andrzej Krzemienski via Boost
<boost_at_[hidden]> wrote:
>
> Hi Everyone,
> I have concerns with the design philosophy of OpenMethod library. Giving
> this flexibility that one person can add new open methods to a class
> hierarchy in one file and another person can add classes to a class
> hierarchy in another file independently, this is asking for
> miscommunication problems (programmer bugs) discovered only at runtime.
>
> I tried to recreate this situation that I expect would be common. My first
> question, even before I run tests is what will happen when I have a class
> hierarchy:
>
> struct Node { virtual ~Node(); };
> struct XNode : Node {};
>
> I declare an open method `value()` and override it for XNode in one file,
> adn in another file I extend the hierarchy with:
>
> struct YNode : Node {};
>
> And then in main(), in yet another file, I will call `value()` with `YNode`.
> The documentation does not give me a clear answer. Section Error Handling
> says,
>
> > When an error is encountered, the program is terminated by a call to abort.
>
>
> And then in the next sentence it talks about different debug and release
> policies, and ultimately, I do not know if by default abort() is called or
> not.
>
> But that is not the only problem. The docs use the term " When an error is
> encountered". What is considered "an error" here? How many situations
> constitute "an error"? The docs should enumerate all these situations. I
> need to know when my program will be calling `abort()`.
>
> So, I dun a Compiler Explorer test:
> https://godbolt.org/z/3fW8j4eTM
> And indeed, I get something that looks like abort()
>
> But when I wrap the call into a try-catch block, all of a sudden, the
> implementation seems to pick the wrong overrider and return a value,
> clearly not the intended one. And the program goes on:
> https://godbolt.org/z/Y6z3o56h5
>
> This is the worst thing that can happen: a random runtime behavior. Is this
> a bug? Or am I missing something?
>
> Next question that naturally arises: how do I declare `noexcept` open
> methods with this library?
>
> Regards,
> &rzej;
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk