{"id":106516,"date":"2022-04-22T07:00:00","date_gmt":"2022-04-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106516"},"modified":"2022-04-22T06:45:05","modified_gmt":"2022-04-22T13:45:05","slug":"20220422-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220422-00\/?p=106516","title":{"rendered":"Trying to create a factory that remembers the parameters to pass to another method"},"content":{"rendered":"<p>Continuing from <a title=\"Class template argument deduction may be the new hotness, but we'll always have maker functions\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220420-00\/?p=106506\"> using maker functions to work around limitations of class template argument deduction<\/a>, let&#8217;s suppose you want to create a generic factory. This sounds like an arbitrary academic exercise, but this stemmed from a real problem. This has practical use, say, if you have a common object and need to generate various factories that produce preconfigured objects.<\/p>\n<pre>\/\/ Imaginary code that doesn't even compile.\r\n\r\ntemplate&lt;typename T, typename...Args&gt;\r\nstruct generic_factory\r\n{\r\n    template&lt;typename... Actuals&gt;\r\n    generic_factory(Actuals&amp;&amp;... args) : \/* something *\/ { }\r\n\r\n    auto make()\r\n    {\r\n        return std::make_unique&lt;T&gt;(args...);\r\n    }\r\n};\r\n\r\ntemplate&lt;typename T, typename... Args&gt;\r\nauto make_generic_factory(Args&amp;&amp;... args)\r\n{\r\n    return generic_factory&lt;T, std::decay_t&lt;Args&gt;...&gt;\r\n        (std::forward&lt;Args&gt;(args)...);\r\n}\r\n<\/pre>\n<p>The idea is that you create the generic factory by telling it what type you want to create (<code>T<\/code>) and the arguments to pass to the constructor (<code>args...<\/code>). You can then call the <code>make<\/code> method on the generic factory, and out comes a new <code>T<\/code> object, constructed with exactly those parameters.<\/p>\n<p>The question is how to convert this sketch into a real class.<\/p>\n<p>A handy place to store a bunch of values is a tuple, and getting the values out of a tuple to pass them as parameters can be done with <code>std::apply<\/code>: While we&#8217;re at it, we&#8217;ll move the <code>std::decay<\/code> into the <code>generic_<wbr \/>factory<\/code>, to make things a little less awkward.<\/p>\n<pre>\/\/ Compiles but doesn't work.\r\n\r\ntemplate&lt;typename T, typename...Args&gt;\r\nstruct generic_factory\r\n{\r\n    using Tuple = std::tuple&lt;std::decay_t&lt;Args&gt;...&gt;;\r\n    Tuple captured;\r\n\r\n    generic_factory(Args&amp;&amp;... args) :\r\n        captured(std::forward&lt;Args&gt;(args)...) {}\r\n\r\n    auto make()\r\n    {\r\n        return std::apply(std::make_unique&lt;T&gt;, captured);\r\n    }\r\n};\r\n\r\ntemplate&lt;typename T, typename...Args&gt;\r\nauto make_generic_factory(Args&amp;&amp;... args)\r\n{\r\n    return generic_factory&lt;T, Args...&gt;(std::forward&lt;Args&gt;(args)...);\r\n}\r\n<\/pre>\n<p>Now, it looks like we&#8217;re reinventing <code>std::bind<\/code>, and yes, that&#8217;s very similar what we&#8217;re doing. We don&#8217;t have the special rules about <code>std::ref<\/code> and <code>std::cref<\/code> that <code>std::bind<\/code> has, nor do our parameters decay, nor do we support placeholders. But one annoying thing about <code>std::bind<\/code> is that the return type <i>has no name<\/i>, so it&#8217;s hard to store it in a variable for later use. (I guess what you usually do is immediately put it inside a <code>std::function<\/code>, but that comes with its own issues.)<\/p>\n<p>And it turns out that <code>std::bind<\/code> has the same problem that our <code>make<\/code> does:<\/p>\n<pre>    \/\/ with std::bind\r\n    auto make = std::bind(std::make_unique&lt;T&gt;, args...);\r\n    make();\r\n\r\n    \/\/ with std::apply\r\n    return std::apply(std::make_unique&lt;T&gt;, captured);\r\n<\/pre>\n<p>Both of these fail with some horrible error message:<\/p>\n<pre style=\"white-space: pre-wrap;\">\/\/ std::bind\r\nFailed to specialize function template 'unknown-type std::_Binder&lt;std::_Unforced, std::unique_ptr&lt;T, std::default_delete&lt;T&gt;&gt; (__cdecl &amp;)(void),T&gt;::operator ()(_Unbound &amp;&amp;...) noexcept() const'\r\n\r\n\/\/ std::apply\r\n'std::invoke': no matching overloaded function found\r\nsee reference to function template instantiation 'decltype(auto) std::_Apply_impl&lt;std::unique_ptr&lt;T, std::default_delete&lt;T&gt;&gt; (__cdecl &amp;)(void),std::tuple&lt;Args...&gt;&amp;,0&gt;(std::unique_ptr&lt;T,std::default_delete&lt;T&gt;&gt; (__cdecl &amp;)(void), std::tuple&lt;int&gt; &amp;, std::integer_sequence&lt;size_t, 0&gt;)' being compiled\r\n<\/pre>\n<p>I find it interesting that calling <code>std::bind<\/code> does compile. However, it produces an object that cannot be used for anything: Trying to invoke the bound call generates the compiler error.<\/p>\n<p>These calls fail because the first parameter to <code>std::bind<\/code> and <code>std::apply<\/code> is a <i>callable<\/i>. No overloading or template type inference is happening here: In order for those to occur, the expression needs to be cast to a specific type, or there need to be parameters to force a resolution to occur. But <code>std::bind<\/code> and <code>std::apply<\/code> accept their first parameters as an arbitrary type, so there is no coersion to any particular parameter list.<\/p>\n<p>I mean, you and I know that the parameter is given by the remaining arguments, but that&#8217;s only because we understand the semantics of what <code>std::bind<\/code> and <code>std::apply<\/code> are going to do, namely, combine the first parameter with the other parameters to create a function call. But the compiler doesn&#8217;t know that. It just sees a bunch of parameters and doesn&#8217;t know that they&#8217;re going to be combined at some point in the future.<\/p>\n<p>This means that when we write <code>std::<wbr \/>make_<wbr \/>unique&lt;T&gt;<\/code>, we are specifying the version of <code>make_<wbr \/>unique<\/code> that takes no parameters. The parameters to <code>make_<wbr \/>unique<\/code> correspond to the second and subsequent template type paramters, for which we passed none.<\/p>\n<p>Since the compiler doesn&#8217;t have enough information to infer the extra parameters, we have to specify them explicitly:<\/p>\n<pre>    \/\/ with std::bind\r\n    auto make = std::bind(std::make_unique&lt;T<span style=\"color: blue;\">, Args...<\/span>&gt;, args...);\r\n    make();\r\n\r\n    \/\/ with std::apply\r\n    return std::apply(std::make_unique&lt;T<span style=\"color: blue;\">, Args...<\/span>&gt;, captured);\r\n<\/pre>\n<p>Unfortunately, this still doesn&#8217;t work. The confusing error message this time is<\/p>\n<pre style=\"white-space: pre-wrap;\">\/\/ std::bind\r\n'operator __surrogate_func': no matching overloaded function found\r\nFailed to specialize function template 'unknown-type std::_Binder&lt;std::_Unforced, std::unique_ptr&lt;T, std::default_delete&lt;T&gt;&gt; (__cdecl &amp;)(void), T&gt;::operator ()(_Unbound &amp;&amp;...) noexcept() const'\r\n\r\n\/\/ std::apply\r\n'std::invoke': no matching overloaded function found\r\nsee reference to function template instantiation 'decltype(auto) std::_Apply_impl&lt;std::unique_ptr&lt;T, std::default_delete&lt;T&gt;&gt;(__cdecl &amp;)(int &amp;&amp;), std::tuple&lt;int&gt;&amp;, 0&gt;(std::unique_ptr&lt;T, std::default_delete&lt;T&gt;&gt; (__cdecl &amp;)(int &amp;&amp;), std::tuple&lt;int&gt;, std::integer_sequence&lt;size_t, 0&gt;)' being compiled\r\n<\/pre>\n<p>Buried in all that error message is the interesting part:<\/p>\n<pre>(__cdecl &amp;)(int &amp;&amp;)\r\n<\/pre>\n<p>The specialization of <code>make_<wbr \/>unique<\/code> we are calling wants an <code>int&amp;&amp;<\/code>. Why does it want an <code>int&amp;&amp;<\/code>?<\/p>\n<p>The corresponding parameter is an <code>int&amp;&amp;<\/code> because the declaration of <code>make_<wbr \/>unique<\/code> uses a universal reference:<\/p>\n<pre>template&lt;typename T, typename... Args&gt;\r\nunique_ptr&lt;T&gt; make_unique(Args&amp;&amp;... args);\r\n<\/pre>\n<p>Since we explicitly passed <code>Args = int<\/code>, this makes the parameter list <code>make_<wbr \/>unique(int&amp;&amp; args)<\/code>. And that&#8217;s why the function wants an <code>int&amp;&amp;<\/code>.<\/p>\n<p>Okay, so we need to pass an <code>int&amp;&amp;<\/code>. But what does <code>std::apply<\/code> actually pass?<\/p>\n<p>The <code>std::apply<\/code> function passes <code>std::get&lt;N&gt;(tuple)<\/code> for each parameter. Since the tuple we passed was an lvalue, <code>std::get&lt;N&gt;(tuple)<\/code> returns an lvalue reference to the tuple element.<\/p>\n<p>And that&#8217;s where the error is coming from. The function wants an rvalue reference to <code>int<\/code>, but we&#8217;re passing an lvalue reference.<\/p>\n<p>Before we try to solve this problem, we need to understand what we are trying to do.<\/p>\n<p>We want to capture the parameters to pass to <code>make_<wbr \/>unique<\/code>, and each time someone calls <code>make<\/code>, we want to call <code>make_<wbr \/>unique<\/code> again with the same parameters. Therefore, we don&#8217;t want to pass rvalue references to our tuple elements: The first call to <code>make_<wbr \/>unique<\/code> would be able to steal the resources from our captured parameters, leaving nothing for the the second and subsequent calls.<\/p>\n<p>Similarly, we don&#8217;t want to pass a straight lvalue reference, because that allows the <code>T<\/code> constructor to mutate the parameter, which would mess up the captured parameters for the second and subsequent calls.<\/p>\n<p>What we really want to pass is a <code>const<\/code> lvalue reference. And we can make this easier to enforce by making our <code>captured<\/code> member variable also <code>const<\/code>.<\/p>\n<pre>template&lt;typename T, typename...Args&gt;\r\nstruct generic_factory\r\n{\r\n    using Tuple = std::tuple&lt;std::decay_t&lt;Args&gt;...&gt;;\r\n    Tuple <span style=\"color: blue;\">const<\/span> captured;\r\n\r\n    generic_factory(Args&amp;&amp;... args) :\r\n        captured(std::forward&lt;Args&gt;(args)...) {}\r\n\r\n    auto make()\r\n    {\r\n        return std::apply(std::make_unique&lt;T,\r\n            <span style=\"color: blue;\">std::decay_t&lt;Args&gt; const&amp;...<\/span>&gt;, captured);\r\n    }\r\n};\r\n\r\ntemplate&lt;typename T, typename...Args&gt;\r\nauto make_generic_factory(Args&amp;&amp;... args)\r\n{\r\n    return generic_factory&lt;T, Args...&gt;(std::forward&lt;Args&gt;(args)...);\r\n}\r\n<\/pre>\n<p>A note of caution here: The parameters passed to <code>make_<wbr \/>generic_<wbr \/>factory<\/code> are captured as-is. If constructing a <code>T<\/code> from them requires a parameter conversion, the conversion is applied at the time the <code>T<\/code> is constructed, not at the time the parameters are captured.<\/p>\n<p>Here&#8217;s an example:<\/p>\n<pre>struct widget\r\n{\r\n    widget(std::string const&amp; name) : m_name(name) { }\r\n    std::string m_name;\r\n};\r\n\r\nauto factory = make_generic_factory&lt;widget&gt;(\"bob\");\r\n<\/pre>\n<p>The parameter captured by <code>make_<wbr \/>generic_<wbr \/>factory<\/code> is the string literal, not a <code>std::string<\/code>. Each time you call <code>make<\/code>, the string literal is converted to a <code>std::string<\/code>, which is then passed to the <code>widget<\/code> constructor, and then when the constructor returns, the temporary <code>std::string<\/code> is destructed. If you want to construct the <code>std::string<\/code> only once, you&#8217;ll have to capture it as a <code>std::string<\/code>:<\/p>\n<pre>auto factory = make_generic_factory&lt;widget&gt;(\"bob\"s);\r\n<\/pre>\n<p>This can get scary for some conversion constructors:<\/p>\n<pre>auto make_widget_factory(int id)\r\n{\r\n    char name[80];\r\n    snprintf(name, 80, \"item #%d\", id);\r\n    return make_generic_factory&lt;widget&gt;(name);\r\n}\r\n<\/pre>\n<p>In this case, the captured parameter is the raw pointer to the stack buffer, which immediately goes out of scope. When you call <code>make()<\/code>, that raw pointer is then passed to <code>make_<wbr \/>unique<\/code>, which will try to convert it to a <code>std::string<\/code>, but it&#8217;s too late. The raw pointer is dangling.<\/p>\n<p><b>Bonus chatter<\/b>: A lambda would also do the trick. The <code>make<\/code> method becomes the <code>operator()<\/code>:<\/p>\n<pre>template&lt;typename T, typename...Args&gt;\r\nauto make_generic_factory(Args&amp;&amp;... args)\r\n{\r\n    return [captured = std::make_tuple(args...)]() {\r\n        return std::apply(std::make_unique&lt;T,\r\n            std::decay_t&lt;Args&gt; const&amp;...&gt;, captured);\r\n    };\r\n}\r\n<\/pre>\n<p>C++20 adds the ability to capture a parameter pack into a lambda without having to hide it inside a tuple:<\/p>\n<pre>template&lt;typename T, typename...Args&gt;\r\nauto make_generic_factory(Args&amp;&amp;... args)\r\n{\r\n    return [...args = std::forward&lt;Args&gt;(args)]() {\r\n        return std::make_unique&lt;T&gt;(args...);\r\n    };\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Lost in a twisty maze of universal references.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-106516","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Lost in a twisty maze of universal references.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106516","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=106516"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106516\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=106516"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106516"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106516"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}