[{"id":3371,"web_url":"https://patchwork.libcamera.org/comment/3371/","msgid":"<20200107195706.GP533370@oden.dyn.berto.se>","date":"2020-01-07T19:57:06","subject":"Re: [libcamera-devel] [PATCH 10/14] libcamera: bound_method:\n\tPropagate method return value","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your work.\n\nOn 2020-01-04 07:09:43 +0200, Laurent Pinchart wrote:\n> Propagate the return value of the bound method all the way to the caller\n> of activate(). The value is stored in the arguments pack for indirect\n> invocation.\n> \n> As C++ doesn't allow instantiating a variable of type void, we need to\n> specialize the template class BoundMethodPack for methods returning\n> void. This in turn requires template specialization for the\n> BoundMethodArgs class in order to store the return value in the pack,\n> and for the BoundMemberMethod class to extract the return value from the\n> pack.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nI think the penny dropped for me at the end after reading it 10+ times \n;-)\n\nReviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\n> ---\n>  include/libcamera/bound_method.h | 99 +++++++++++++++++++++++++++-----\n>  include/libcamera/object.h       |  6 +-\n>  src/libcamera/bound_method.cpp   | 23 ++++++--\n>  src/libcamera/object.cpp         |  6 +-\n>  4 files changed, 113 insertions(+), 21 deletions(-)\n> \n> diff --git a/include/libcamera/bound_method.h b/include/libcamera/bound_method.h\n> index a74d2c503cdb..0a91f44a2054 100644\n> --- a/include/libcamera/bound_method.h\n> +++ b/include/libcamera/bound_method.h\n> @@ -28,7 +28,7 @@ public:\n>  \tvirtual ~BoundMethodPackBase() {}\n>  };\n>  \n> -template<typename... Args>\n> +template<typename R, typename... Args>\n>  class BoundMethodPack : public BoundMethodPackBase\n>  {\n>  public:\n> @@ -37,6 +37,19 @@ public:\n>  \t{\n>  \t}\n>  \n> +\tstd::tuple<typename std::remove_reference<Args>::type...> args_;\n> +\tR ret_;\n> +};\n> +\n> +template<typename... Args>\n> +class BoundMethodPack<void, Args...> : public BoundMethodPackBase\n> +{\n> +public:\n> +\tBoundMethodPack(const Args &... args)\n> +\t\t: args_(args...)\n> +\t{\n> +\t}\n> +\n>  \tstd::tuple<typename std::remove_reference<Args>::type...> args_;\n>  };\n>  \n> @@ -77,7 +90,7 @@ protected:\n>  \t};\n>  #endif\n>  \n> -\tvoid activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n> +\tbool activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n>  \t\t\t  bool deleteMethod);\n>  \n>  \tvoid *obj_;\n> @@ -91,7 +104,34 @@ template<typename R, typename... Args>\n>  class BoundMethodArgs : public BoundMethodBase\n>  {\n>  public:\n> -\tusing PackType = BoundMethodPack<Args...>;\n> +\tusing PackType = BoundMethodPack<R, Args...>;\n> +\n> +private:\n> +\ttemplate<int... S>\n> +\tvoid invokePack(BoundMethodPackBase *pack, BoundMethodBase::sequence<S...>)\n> +\t{\n> +\t\tPackType *args = static_cast<PackType *>(pack);\n> +\t\targs->ret_ = invoke(std::get<S>(args->args_)...);\n> +\t}\n> +\n> +public:\n> +\tBoundMethodArgs(void *obj, Object *object, ConnectionType type)\n> +\t\t: BoundMethodBase(obj, object, type) {}\n> +\n> +\tvoid invokePack(BoundMethodPackBase *pack) override\n> +\t{\n> +\t\tinvokePack(pack, typename BoundMethodBase::generator<sizeof...(Args)>::type());\n> +\t}\n> +\n> +\tvirtual R activate(Args... args, bool deleteMethod = false) = 0;\n> +\tvirtual R invoke(Args... args) = 0;\n> +};\n> +\n> +template<typename... Args>\n> +class BoundMethodArgs<void, Args...> : public BoundMethodBase\n> +{\n> +public:\n> +\tusing PackType = BoundMethodPack<void, Args...>;\n>  \n>  private:\n>  \ttemplate<int... S>\n> @@ -129,15 +169,45 @@ public:\n>  \n>  \tbool match(R (T::*func)(Args...)) const { return func == func_; }\n>  \n> +\tR activate(Args... args, bool deleteMethod = false) override\n> +\t{\n> +\t\tif (!this->object_)\n> +\t\t\treturn (static_cast<T *>(this->obj_)->*func_)(args...);\n> +\n> +\t\tauto pack = std::make_shared<PackType>(args...);\n> +\t\tbool sync = BoundMethodBase::activatePack(pack, deleteMethod);\n> +\t\treturn sync ? pack->ret_ : R();\n> +\t}\n> +\n> +\tR invoke(Args... args) override\n> +\t{\n> +\t\treturn (static_cast<T *>(this->obj_)->*func_)(args...);\n> +\t}\n> +\n> +private:\n> +\tR (T::*func_)(Args...);\n> +};\n> +\n> +template<typename T, typename... Args>\n> +class BoundMemberMethod<T, void, Args...> : public BoundMethodArgs<void, Args...>\n> +{\n> +public:\n> +\tusing PackType = typename BoundMethodArgs<void *, Args...>::PackType;\n> +\n> +\tBoundMemberMethod(T *obj, Object *object, void (T::*func)(Args...),\n> +\t\t\t  ConnectionType type = ConnectionTypeAuto)\n> +\t\t: BoundMethodArgs<void, Args...>(obj, object, type), func_(func)\n> +\t{\n> +\t}\n> +\n> +\tbool match(void (T::*func)(Args...)) const { return func == func_; }\n> +\n>  \tvoid activate(Args... args, bool deleteMethod = false) override\n>  \t{\n> -\t\tif (!this->object_) {\n> -\t\t\t(static_cast<T *>(this->obj_)->*func_)(args...);\n> -\t\t\treturn;\n> -\t\t}\n> +\t\tif (!this->object_)\n> +\t\t\treturn (static_cast<T *>(this->obj_)->*func_)(args...);\n>  \n> -\t\tstd::shared_ptr<BoundMethodPackBase> pack =\n> -\t\t\tstd::make_shared<typename BoundMemberMethod<T, R, Args...>::PackType>(args...);\n> +\t\tauto pack = std::make_shared<PackType>(args...);\n>  \t\tBoundMethodBase::activatePack(pack, deleteMethod);\n>  \t}\n>  \n> @@ -147,7 +217,7 @@ public:\n>  \t}\n>  \n>  private:\n> -\tR (T::*func_)(Args...);\n> +\tvoid (T::*func_)(Args...);\n>  };\n>  \n>  template<typename R, typename... Args>\n> @@ -162,12 +232,15 @@ public:\n>  \n>  \tbool match(R (*func)(Args...)) const { return func == func_; }\n>  \n> -\tvoid activate(Args... args, bool deleteMethod = false) override\n> +\tR activate(Args... args, bool deleteMethod = false) override\n>  \t{\n> -\t\t(*func_)(args...);\n> +\t\treturn (*func_)(args...);\n>  \t}\n>  \n> -\tvoid invoke(Args...) override {}\n> +\tR invoke(Args...) override\n> +\t{\n> +\t\treturn R();\n> +\t}\n>  \n>  private:\n>  \tR (*func_)(Args...);\n> diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> index 586c2cf6fc70..04aa18394d55 100644\n> --- a/include/libcamera/object.h\n> +++ b/include/libcamera/object.h\n> @@ -31,12 +31,12 @@ public:\n>  \n>  \ttemplate<typename T, typename R, typename... FuncArgs, typename... Args,\n>  \t\t typename std::enable_if<std::is_base_of<Object, T>::value>::type * = nullptr>\n> -\tvoid invokeMethod(R (T::*func)(FuncArgs...), ConnectionType type,\n> -\t\t\t  Args... args)\n> +\tR invokeMethod(R (T::*func)(FuncArgs...), ConnectionType type,\n> +\t\t       Args... args)\n>  \t{\n>  \t\tT *obj = static_cast<T *>(this);\n>  \t\tauto *method = new BoundMemberMethod<T, R, FuncArgs...>(obj, this, func, type);\n> -\t\tmethod->activate(args..., true);\n> +\t\treturn method->activate(args..., true);\n>  \t}\n>  \n>  \tThread *thread() const { return thread_; }\n> diff --git a/src/libcamera/bound_method.cpp b/src/libcamera/bound_method.cpp\n> index 82339b7e9c95..8e95c7eec92c 100644\n> --- a/src/libcamera/bound_method.cpp\n> +++ b/src/libcamera/bound_method.cpp\n> @@ -48,7 +48,22 @@ namespace libcamera {\n>   * deadlock will occur.\n>   */\n>  \n> -void BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n> +/**\n> + * \\brief Invoke the bound method with packed arguments\n> + * \\param[in] pack Packed arguments\n> + * \\param[in] deleteMethod True to delete \\a this bound method instance when\n> + * method invocation completes\n> + *\n> + * The bound method stores its return value, if any, in the arguments \\a pack.\n> + * For direct and blocking invocations, this is performed synchronously, and\n> + * the return value contained in the pack may be used. For queued invocations,\n> + * the return value is stored at an undefined point of time and shall thus not\n> + * be used by the caller.\n> + *\n> + * \\return True if the return value contained in the \\a pack may be used by the\n> + * caller, false otherwise\n> + */\n> +bool BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n>  \t\t\t\t   bool deleteMethod)\n>  {\n>  \tConnectionType type = connectionType_;\n> @@ -65,13 +80,13 @@ void BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n>  \t\tinvokePack(pack.get());\n>  \t\tif (deleteMethod)\n>  \t\t\tdelete this;\n> -\t\tbreak;\n> +\t\treturn true;\n>  \n>  \tcase ConnectionTypeQueued: {\n>  \t\tstd::unique_ptr<Message> msg =\n>  \t\t\tutils::make_unique<InvokeMessage>(this, pack, nullptr, deleteMethod);\n>  \t\tobject_->postMessage(std::move(msg));\n> -\t\tbreak;\n> +\t\treturn false;\n>  \t}\n>  \n>  \tcase ConnectionTypeBlocking: {\n> @@ -82,7 +97,7 @@ void BoundMethodBase::activatePack(std::shared_ptr<BoundMethodPackBase> pack,\n>  \t\tobject_->postMessage(std::move(msg));\n>  \n>  \t\tsemaphore.acquire();\n> -\t\tbreak;\n> +\t\treturn true;\n>  \t}\n>  \t}\n>  }\n> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> index e76faf48b8ed..21aad5652b38 100644\n> --- a/src/libcamera/object.cpp\n> +++ b/src/libcamera/object.cpp\n> @@ -143,7 +143,7 @@ void Object::message(Message *msg)\n>  }\n>  \n>  /**\n> - * \\fn void Object::invokeMethod()\n> + * \\fn R Object::invokeMethod()\n>   * \\brief Invoke a method asynchronously on an Object instance\n>   * \\param[in] func The object method to invoke\n>   * \\param[in] type Connection type for method invocation\n> @@ -156,6 +156,10 @@ void Object::message(Message *msg)\n>   * Arguments \\a args passed by value or reference are copied, while pointers\n>   * are passed untouched. The caller shall ensure that any pointer argument\n>   * remains valid until the method is invoked.\n> + *\n> + * \\return For connection types ConnectionTypeDirect and\n> + * ConnectionTypeBlocking, return the return value of the invoked method. For\n> + * connection type ConnectionTypeQueued, return a default-constructed R value.\n>   */\n>  \n>  /**\n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lj1-x242.google.com (mail-lj1-x242.google.com\n\t[IPv6:2a00:1450:4864:20::242])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 77B6E6063B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  7 Jan 2020 20:57:08 +0100 (CET)","by mail-lj1-x242.google.com with SMTP id y6so889123lji.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 07 Jan 2020 11:57:08 -0800 (PST)","from localhost (h-93-159.A463.priv.bahnhof.se. [46.59.93.159])\n\tby smtp.gmail.com with ESMTPSA id\n\tw8sm262596ljd.13.2020.01.07.11.57.06\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 07 Jan 2020 11:57:07 -0800 (PST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=Fjsg4WG1JqWtFLYoiN82acwK/ugBGD930tDYJvQ/sg8=;\n\tb=Pj/Xm06IrxLfgjpqvLxpw01fnY6SN0RGTBhZ627Kkade1HgaiBIm4vGAGIOwDv5RV3\n\te4FC+gSrkp6GHb4lp2jU7gYtUi+npzdLjpAaPLFSfl1vd4wFvfFBkgmDqrVCZmv/HZF4\n\tEJaHAKZ1u6dMhYiApuhC/E9r7HE3CphTU/CbFBctmopzaD0NTWn7vTgkYpd8+UQ8S5o8\n\t+WATzdfvlLEQetBHr2j6YsjBzBBa+EMpgEdZBSfk79DN+w8kBM5PAc9DEfJUYQG4EA7T\n\tpyXJ6OSrmdsymLs/abUR1ATjbLxDJ/2IZIG4efzYtNrp+fjvq1Un+qbYHJ9pqpJd8/ea\n\t8jmg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=Fjsg4WG1JqWtFLYoiN82acwK/ugBGD930tDYJvQ/sg8=;\n\tb=EtLmQZ1N6yvnB2e0tegqihDNBYKcm1OeCfSRqfYkm4WVLEG9iIASk5aVMoo+kE8Da6\n\tvnpw8nWPyqGRhzofq0gZ7+MX2weyzN9SjKFSN8t/7xBwMZQjDjTmxRMggXMdFiH8ltEx\n\tZP84wneUSfus42uDcyBJpQCFJpqNErgb5FPHFJKiczNZNcZKz3h4F08ooabTQlpG3rag\n\tRtFfHz0P3STJaAtRuUGJ48n7och8D+f+T6dHGDoCiRsvEhu20Sm4/omSmjmW44TP5eHA\n\t9G2XgZG2yyeDlewNszt5b6ekkJuZDKPKHRktr2pvthkzKNkqHSvriV+6teWke6m8ZK5M\n\tPIrQ==","X-Gm-Message-State":"APjAAAXXjv2wapI7MdJb4zy+FR/3hbj3bRjlDLX6how+8E2EeiTkgySq\n\tB+tSWU+F2fPKQ+4NuJs0fVnd8/P4RIzLXw==","X-Google-Smtp-Source":"APXvYqxbQbhOztmYegmFdZqsT6fQFkFCGj8UXVbx0TST/+5naTIyL0zY+T4/qEi6wKKQVv95GMMhdg==","X-Received":"by 2002:a2e:b0c4:: with SMTP id g4mr659990ljl.83.1578427027667; \n\tTue, 07 Jan 2020 11:57:07 -0800 (PST)","Date":"Tue, 7 Jan 2020 20:57:06 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200107195706.GP533370@oden.dyn.berto.se>","References":"<20200104050947.7673-1-laurent.pinchart@ideasonboard.com>\n\t<20200104050947.7673-11-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200104050947.7673-11-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH 10/14] libcamera: bound_method:\n\tPropagate method return value","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 07 Jan 2020 19:57:08 -0000"}}]