[libcamera-devel,RFC,v2,1/4] hack: Camera public destructor
diff mbox series

Message ID 20201127133738.880859-2-tomi.valkeinen@iki.fi
State RFC
Headers show
Series
  • prototype python bindings
Related show

Commit Message

Tomi Valkeinen Nov. 27, 2020, 1:37 p.m. UTC
Not clear why we need this with pybind11...

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@iki.fi>
---
 include/libcamera/camera.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Laurent Pinchart Nov. 29, 2020, 11:27 p.m. UTC | #1
Hi Tomi,

Thank you for the patch.

On Fri, Nov 27, 2020 at 03:37:35PM +0200, Tomi Valkeinen wrote:
> Not clear why we need this with pybind11...

We'll have to figure it out :-)

I've been considering a while ago handling camera instances a bit
differently, with a facade object create by libcamera (and returned a
unique pointer), with a reference-counted (through shared_ptr) backend
object not visible to applications. We could implement this, either in
the libcamera core, or in the python bindings, if it helps.

> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@iki.fi>
> ---
>  include/libcamera/camera.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> index 5c5f1a05..d8e0bdbc 100644
> --- a/include/libcamera/camera.h
> +++ b/include/libcamera/camera.h
> @@ -106,10 +106,10 @@ public:
>  	int start();
>  	int stop();
>  
> +	~Camera();
>  private:
>  	Camera(PipelineHandler *pipe, const std::string &id,
>  	       const std::set<Stream *> &streams);
> -	~Camera();
>  
>  	friend class PipelineHandler;
>  	void disconnect();
Tomi Valkeinen Nov. 30, 2020, 2:26 p.m. UTC | #2
On 30/11/2020 01:27, Laurent Pinchart wrote:
> Hi Tomi,
> 
> Thank you for the patch.
> 
> On Fri, Nov 27, 2020 at 03:37:35PM +0200, Tomi Valkeinen wrote:
>> Not clear why we need this with pybind11...
> 
> We'll have to figure it out :-)

My guess is that pybind11 generates code to create a shared_ptr<Camera>. You need a public
destructor for that.

Ah, no, Camera constructor is private too, so that cannot be the case.

> I've been considering a while ago handling camera instances a bit
> differently, with a facade object create by libcamera (and returned a
> unique pointer), with a reference-counted (through shared_ptr) backend
> object not visible to applications. We could implement this, either in
> the libcamera core, or in the python bindings, if it helps.

What would be the benefit of that approach in libcamera core? Isn't that just re-implementing the
same feature with more complex code? Unless you want to hide something with the backend object that
can't be hidden at the moment.

I'm not sure, but we might have the same problem with unique_ptr too. If something in pybind11
requires the destructor to be visible with shared_ptr, that could also be the case with unique_ptr.

I have considered adding facades/wrappers for some classes in the py bindings, so that I could store
extra state in those objects. But it does get complex, as I need to re-use existing objects instead
of just creating a fresh one every time I get an instance from c++ side.

That's a bit different than what you're suggesting, but would also remove the use of
shared_ptr<Camera> from the bindings itself.

But if all this is just to avoid making the Camera destructor public, I have to ask if it's worth it =).

 Tomi
Laurent Pinchart Nov. 30, 2020, 3:53 p.m. UTC | #3
Hi Tomi,

On Mon, Nov 30, 2020 at 04:26:48PM +0200, Tomi Valkeinen wrote:
> On 30/11/2020 01:27, Laurent Pinchart wrote:
> > Hi Tomi,
> > 
> > Thank you for the patch.
> > 
> > On Fri, Nov 27, 2020 at 03:37:35PM +0200, Tomi Valkeinen wrote:
> >> Not clear why we need this with pybind11...
> > 
> > We'll have to figure it out :-)
> 
> My guess is that pybind11 generates code to create a shared_ptr<Camera>. You need a public
> destructor for that.
> 
> Ah, no, Camera constructor is private too, so that cannot be the case.

In pybind11::class_::init_holder(), 

 1.    /// Initialize holder object, variant 1: object derives from enable_shared_from_this
 2.    template <typename T>
 3.    static void init_holder(detail::instance *inst, detail::value_and_holder &v_h,
 4.            const holder_type * /* unused */, const std::enable_shared_from_this<T> * /* dummy */) {
 5.        try {
 6.            auto sh = std::dynamic_pointer_cast<typename holder_type::element_type>(
 7.                    v_h.value_ptr<type>()->shared_from_this());
 8.            if (sh) {
 9.                new (std::addressof(v_h.holder<holder_type>())) holder_type(std::move(sh));
10.                v_h.set_holder_constructed();
11.            }
12.        } catch (const std::bad_weak_ptr &) {}
13.
14.        if (!v_h.holder_constructed() && inst->owned) {
15.            new (std::addressof(v_h.holder<holder_type>())) holder_type(v_h.value_ptr<type>());
16.            v_h.set_holder_constructed();
17.        }
18.    }

On line 15 a holder_type is constructed. holder_type is
std::shared_ptr<Camera>, given that the bindings use

	py::class_<Camera, shared_ptr<Camera>>(m, "Camera", py::dynamic_attr())

I think this code will never be executed in practice, as the holder
should always be initialized from a std::shared_ptr<>, but the compiler
can't know that.

> > I've been considering a while ago handling camera instances a bit
> > differently, with a facade object create by libcamera (and returned a
> > unique pointer), with a reference-counted (through shared_ptr) backend
> > object not visible to applications. We could implement this, either in
> > the libcamera core, or in the python bindings, if it helps.
> 
> What would be the benefit of that approach in libcamera core? Isn't that just re-implementing the
> same feature with more complex code? Unless you want to hide something with the backend object that
> can't be hidden at the moment.

It was partly about hiding the Camera object private fields, but that
has since been done through usage of the d-pointer design pattern. The
other reason is to avoid proliferation of std::shared_ptr<> on the
application side. It could help with other language bindings too.

> I'm not sure, but we might have the same problem with unique_ptr too. If something in pybind11
> requires the destructor to be visible with shared_ptr, that could also be the case with unique_ptr.

The destructor for the facade object would be visible. That's the whole
point, it would be constructed by libcamera, but destroyed by
applications. I'm however not sure how practical that would be, given
that we wouldn't be able to expose a vector of cameras anymore (at least
not in the same way).

> I have considered adding facades/wrappers for some classes in the py bindings, so that I could store
> extra state in those objects. But it does get complex, as I need to re-use existing objects instead
> of just creating a fresh one every time I get an instance from c++ side.

I have a feeling we'll have to do so at some point, for some of the
objects, but have no proof for now :-)

> That's a bit different than what you're suggesting, but would also remove the use of
> shared_ptr<Camera> from the bindings itself.
> 
> But if all this is just to avoid making the Camera destructor public, I have to ask if it's worth it =).

Patch
diff mbox series

diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 5c5f1a05..d8e0bdbc 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -106,10 +106,10 @@  public:
 	int start();
 	int stop();
 
+	~Camera();
 private:
 	Camera(PipelineHandler *pipe, const std::string &id,
 	       const std::set<Stream *> &streams);
-	~Camera();
 
 	friend class PipelineHandler;
 	void disconnect();