[v2,1/2] libcamera: utils: Add scope_exit class
diff mbox series

Message ID 20250729162101.13836-2-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • libcamera: Introduce and use scope_exit
Related show

Commit Message

Laurent Pinchart July 29, 2025, 4:21 p.m. UTC
The scope_exit class is an implementation of the identically named C++
library fundamentals TS v3 class, as documented in [1]. It is a simpler
version of the libcamera-specific ScopeExitActions class and doesn't
require dynamic heap memory allocation, making it more suitable for hot
paths.

The class is not documented as it implements a C++ standard (even if
experimental) API.

[1] https://en.cppreference.com/w/cpp/experimental/scope_exit/scope_exit.html

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
I have named the class scope_exit instead of ScopeExit to match the C++
library fundamentals TS v3 class, and have not documented it as it's
meant as an implementation of the standard. It is however not clear if
and when scope_exit would become part of a ratified C++ standard, so I'm
open to instead create a libcamera-specific ScopeExit class and document
it.
---
 include/libcamera/base/utils.h | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

--
Regards,

Laurent Pinchart

Comments

Barnabás Pőcze July 30, 2025, 7:58 a.m. UTC | #1
Hi

2025. 07. 29. 18:21 keltezéssel, Laurent Pinchart írta:
> +template<typename EF>
> +class scope_exit
> +{
> +public:
> +	template<typename Fn,
> +		 std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Fn>>, scope_exit> &&
> +				  std::is_constructible_v<EF, Fn> &&
> +				  !std::is_lvalue_reference_v<Fn>> * = nullptr>

I think it should be fine to remove the lvalue condition since we require
nothrow constructibility.


> +	explicit scope_exit(Fn &&fn)
> +		: exitFunction_(std::forward<Fn>(fn))
> +	{
> +		static_assert(std::is_nothrow_constructible_v<EF, Fn>);
> +	}
> +
> +	scope_exit(scope_exit &&other)

I would probably =delete this until a need arises.


> +		: exitFunction_(std::move(other.exitFunction_)),
> +		  active_(other.active_)

I like std::exchange a lot... so I'd write

   std::exchange(other.active_, false)


Regards,
Barnabás Pőcze

> +	{
> +		other.release();
> +	}
> +
> +	scope_exit(const scope_exit &) = delete;
> +
> +	~scope_exit()
> +	{
> +		if (active_)
> +			exitFunction_();
> +	}
> +
> +	void release()
> +	{
> +		active_ = false;
> +	}
> +
> +private:
> +	EF exitFunction_;
> +	bool active_ = true;
> +};
> +
> +template<typename EF>
> +scope_exit(EF) -> scope_exit<EF>;
> +
Laurent Pinchart July 30, 2025, 9:23 a.m. UTC | #2
On Wed, Jul 30, 2025 at 09:58:51AM +0200, Barnabás Pőcze wrote:
> 2025. 07. 29. 18:21 keltezéssel, Laurent Pinchart írta:
> > +template<typename EF>
> > +class scope_exit
> > +{
> > +public:
> > +	template<typename Fn,
> > +		 std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Fn>>, scope_exit> &&
> > +				  std::is_constructible_v<EF, Fn> &&
> > +				  !std::is_lvalue_reference_v<Fn>> * = nullptr>
> 
> I think it should be fine to remove the lvalue condition since we require
> nothrow constructibility.

Will be done in v3.

> > +	explicit scope_exit(Fn &&fn)
> > +		: exitFunction_(std::forward<Fn>(fn))
> > +	{
> > +		static_assert(std::is_nothrow_constructible_v<EF, Fn>);
> > +	}
> > +
> > +	scope_exit(scope_exit &&other)
> 
> I would probably =delete this until a need arises.
> 
> 
> > +		: exitFunction_(std::move(other.exitFunction_)),
> > +		  active_(other.active_)
> 
> I like std::exchange a lot... so I'd write
> 
>    std::exchange(other.active_, false)

Agreed, but I'll instead delete the function :-)

> > +	{
> > +		other.release();
> > +	}
> > +
> > +	scope_exit(const scope_exit &) = delete;
> > +
> > +	~scope_exit()
> > +	{
> > +		if (active_)
> > +			exitFunction_();
> > +	}
> > +
> > +	void release()
> > +	{
> > +		active_ = false;
> > +	}
> > +
> > +private:
> > +	EF exitFunction_;
> > +	bool active_ = true;
> > +};
> > +
> > +template<typename EF>
> > +scope_exit(EF) -> scope_exit<EF>;
> > +

Patch
diff mbox series

diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h
index 8d5c35782ee3..1d8e5aef9b54 100644
--- a/include/libcamera/base/utils.h
+++ b/include/libcamera/base/utils.h
@@ -428,6 +428,51 @@  private:
 	std::vector<std::function<void()>> actions_;
 };

+#ifndef __DOXYGEN__
+template<typename EF>
+class scope_exit
+{
+public:
+	template<typename Fn,
+		 std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Fn>>, scope_exit> &&
+				  std::is_constructible_v<EF, Fn> &&
+				  !std::is_lvalue_reference_v<Fn>> * = nullptr>
+	explicit scope_exit(Fn &&fn)
+		: exitFunction_(std::forward<Fn>(fn))
+	{
+		static_assert(std::is_nothrow_constructible_v<EF, Fn>);
+	}
+
+	scope_exit(scope_exit &&other)
+		: exitFunction_(std::move(other.exitFunction_)),
+		  active_(other.active_)
+	{
+		other.release();
+	}
+
+	scope_exit(const scope_exit &) = delete;
+
+	~scope_exit()
+	{
+		if (active_)
+			exitFunction_();
+	}
+
+	void release()
+	{
+		active_ = false;
+	}
+
+private:
+	EF exitFunction_;
+	bool active_ = true;
+};
+
+template<typename EF>
+scope_exit(EF) -> scope_exit<EF>;
+
+#endif /* __DOXYGEN__ */
+
 } /* namespace utils */

 #ifndef __DOXYGEN__