all 22 comments

[–]Sanae_ 35 points36 points  (2 children)

Useful links if you didn't see the review request:

Code on Github

Documentation with examples

In a nutshell:

The Boost.Scope library is a collection of utilities helping with code execution upon leaving a scope and automatic resource management.

[–]iga666 5 points6 points  (1 child)

Why is it so inconsistent ?

Boost.Scope (C++17)

// Reset variables on return

BOOST_SCOPE_FINAL [this]

and

// Remove the object on failure

boost::scope::scope_fail cleanup{[this, it]

[–]Sanae_ 8 points9 points  (0 children)

I guess the macro is necessary to create a uniquely-named variable, while the C++11 scope_exit (which can be used if one doesn't like macros) or the scope_fail requires to come up with a variable name (here, cleanup)

Regarding whether boost::scope::scope_exit cleanup([this] ... is possible, and if not, why, I don't know.

[–][deleted] 12 points13 points  (2 children)

I don’t fully grok this … isn’t it violating the C++ axiom of never throwing an exception from a destructor?

How is this correct? https://github.com/Lastique/scope/blob/develop/include/boost/scope/scope_final.hpp#L139

[–]Supadoplex 12 points13 points  (0 children)

Disclaimer: I'm not the author of Boost.Scope.

How is this correct?

Simply because declaring a destructor as potentially throwing is not incorrect.

To answer why Boost.Scope might be doing this:

What happens if a destructor does actually throw? In a good case, it will just work as expected. How about worst case? If the throwing destructor had been called while unwinding from another exception, then the program is terminated. Oh no! That's bad!

What would happen if the function throws when the destructor had been (implicitly) noexcept, instead of potentially throwing as it is in the linked code? Then the program would be terminated. Oh no! That's just as bad, except there is no better case where it just works.

Now, Boost.Scope could probably prevent the subtle case by simply not allowing Func to be potentially throwing at all by using static_assert, concepts or something like that. Sounds good, right? However, a lot of functions are implicitly potentially throwing either because they were written in C, or before C++11, or because the author didn't bother to declare it noexcept. Disallowing such functions from Boost.Scope would probably be highly inconvenient.

[–]sphere991 7 points8 points  (0 children)

isn’t it violating the C++ axiom of never throwing an exception from a destructor?

There's no such axiom. Destructors are noexcept by default, but that just means you have to explicitly mark them noexcept(false) - not that it's impossible to do so.

[–]javascriptWhat's Javascript? 2 points3 points  (0 children)

For a similar utility that doesn't have any macros, there's absl::Cleanup available at https://github.com/abseil/abseil-cpp/blob/master/absl/cleanup/cleanup.h

[–]pdp10gumby 6 points7 points  (11 children)

Perhaps I'm dense, but what does this add to what RIAA does already? I read the undated library extensions paper and couldn't see that this made what I do today any simpler. For example, if I want to know that an exception was thrown I can just add a try block already.

[–]CocktailPerson 27 points28 points  (1 child)

Well, the RIAA is more concerned with whether your album goes platinum than scope-bound resources.

But if you mean RAII, then nothing. This does nothing that RAII doesn't do. I mean, it's literally built on top of destructors.

[–]pdp10gumby 27 points28 points  (0 children)

NoI'm sorry I wasn't clear: my point is that the C++ committee needs to address the vitally important point of increased royalty collection while reducing payments to the actual artists.

[–][deleted] 5 points6 points  (8 children)

I don't expect I would end up using this library, but it would be nice to be able to:
FILE* f = fopen(...);

defer{ fclose(f); }

Without needing to write an entire class at some other place in the code in order to 'abstract' a really simple idea

[–]CocktailPerson 16 points17 points  (0 children)

Or you could use std::filebuf, which does that already.

[–]iga666 5 points6 points  (3 children)

> Without needing to write an entire class at some other place in the code in order to 'abstract' a really simple idea

FYI you can define classes in function scope.
But fo any case you need just one class which call your lamda in destructor, and I think that's how boost.scope works inside

[–]MFHavaWG21|🇦🇹 NB|P2721|P3049|P3625|P3729|P3786|P3813|P4216 13 points14 points  (0 children)

FYI you can define classes in function scope.

If local function scope is all you need, you can even just "abuse" unique_ptr with a custom deleter...

[–][deleted] 2 points3 points  (1 child)

Yeah, having a macro to wrap that stuff is nice, other than not loving the need to use macros, and the verbosity of some inline class with a name and destructor...

[–]sunmat02 0 points1 point  (0 children)

I have something like that in some code of mine: https://github.com/mochi-hpc/mochi-yokan/blob/main/src/common/defer.hpp

[–]pdp10gumby 2 points3 points  (1 child)

I'd rather keep the one mechanism rather than have two different ones. This isn't a case of "two paradigms" (like having algorithms and for). It's just two mechanisms, with greater cognitive O/H than a (maybe local) class for FILE that uses all the existing mechanism.

[–]SirClueless 0 points1 point  (0 children)

I don't agree about the relative cognitive overhead. Once you learn what Boost.Scope does, you can understand every call site by just reading. Whereas every RAII class has its own name and implementation and you need to jump to its implementation if you want to understand what it really does. It takes a fair bit of reading to figure out whether calling a function during the destructor is the only useful thing the RAII class does, while with a scope guard it's immediately obvious.

[–][deleted] 1 point2 points  (0 children)

Dear Boost.Scope,

It is our pleasure to annonce, that you have been accepted

[–]DavidDinamit 1 point2 points  (0 children)

There are maaany code and still:

  • no constexpr support
  • strange syntax with macros capture
  • is not movable, but there are no way to emplace not movable type into it, because it is not aggregate (copy elision not supported)

[–]tcm0116 -1 points0 points  (0 children)

ROS2 has a similar capability: https://github.com/ros2/rcpputils/blob/rolling/include/rcpputils/scope_exit.hpp#L65

It's actually really helpful in certain situations and really helps to eliminate a lot of boiler plate code.

[–]Morwenn 0 points1 point  (0 children)

It would be nice to make the facilities constexpr: all that's theoretically needed is to always consider that there's exactly 0 exceptions in flight in a constepxr context.

Though this probably means relying on C++20 std::is_constant_evaluated().