{"id":103984,"date":"2020-07-15T07:00:00","date_gmt":"2020-07-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103984"},"modified":"2020-07-15T07:36:09","modified_gmt":"2020-07-15T14:36:09","slug":"20200715-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200715-00\/?p=103984","title":{"rendered":"Deconstructing function pointers in a C++ template, vexing variadics"},"content":{"rendered":"<p>Last time, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200714-00\/?p=103981\"> we taught our little traits class about <code>noexcept<\/code> functions<\/a>. One of many oddball cases in the world of function pointers is that of the variadic function, a classic example of which is <code>printf<\/code>.<\/p>\n<p>Recall that we had this:<\/p>\n<pre>template&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraitsBase\r\n{\r\n   using RetType = R;\r\n   using ArgTypes = std::tuple&lt;Args...&gt;;\r\n   static constexpr std::size_t ArgCount = sizeof...(Args);\r\n   template&lt;std::size_t N&gt;\r\n   using NthArg = std::tuple_element_t&lt;N, ArgTypes&gt;;\r\n};\r\n\r\ntemplate&lt;typename F&gt; struct FunctionTraits;\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n  constexpr static bool IsNoexcept = false;\r\n};\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...) noexcept&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n  constexpr static bool IsNoexcept = true;\r\n};\r\n<\/pre>\n<p>But it falls apart when we give it a function like <code>printf<\/code> because none of our specializations handle that case. Let&#8217;s fix that.<\/p>\n<pre>template&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraitsBase\r\n{\r\n  using RetType = R;\r\n  using ArgTypes = std::tuple&lt;Args...&gt;;\r\n  static constexpr std::size_t ArgCount = sizeof...(Args);\r\n  template&lt;std::size_t N&gt;\r\n  using NthArg = std::tuple_element_t&lt;N, ArgTypes&gt;;\r\n};\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...);\r\n  constexpr static bool IsNoexcept = false;\r\n  <span style=\"color: blue;\">static constexpr bool IsVariadic = false;<\/span>\r\n};\r\n\r\ntemplate&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...<span style=\"color: blue;\">, ...<\/span>)&gt; <span style=\"color: blue;\">\/\/ variadic<\/span>\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  using Pointer = R(*)(Args...<span style=\"color: blue;\">, ...<\/span>); <span style=\"color: blue;\">\/\/ variadic<\/span>\r\n  static constexpr bool IsNoexcept = false;\r\n  <span style=\"color: blue;\">static constexpr bool IsVariadic = true;<\/span>\r\n};\r\n<\/pre>\n<p>We have to repeat this for the <code>noexcept<\/code> version, so things are getting pretty repetitive now that we&#8217;re up to four flavors of this structure. We saw with <code>noexcept<\/code> that the varieties cannot be deduced via templating, but we&#8217;ll reduce the repetitiveness with a temporary macro.<\/p>\n<pre>#define MAKE_TRAITS(Noexcept, Variadic, ...)                \\\r\ntemplate&lt;typename R, typename... Args&gt;                      \\\r\nstruct FunctionTraits&lt;R(*)(__VA_ARGS__) noexcept(Noexcept)&gt; \\\r\n    : FunctionTraitsBase&lt;R, Args...&gt;                        \\\r\n{                                                           \\\r\n  using Pointer = R(*)(__VA_ARGS__) noexcept(Noexcept);     \\\r\n  constexpr static bool IsNoexcept = Noexcept;              \\\r\n  constexpr static bool IsVariadic = Variadic;              \\\r\n}\r\n\r\nMAKE_TRAITS(false, false, Args...);\r\nMAKE_TRAITS(false,  true, Args..., ...);\r\nMAKE_TRAITS( true, false, Args...);\r\nMAKE_TRAITS( true,  true, Args..., ...);\r\n\r\n#undef MAKE_TRAITS\r\n<\/pre>\n<p>The <code>MAKE_TRAITS<\/code> macro takes three-ish parameters.<\/p>\n<ul>\n<li><code>Noexcept<\/code> is <code>true<\/code> to create the <code>noexcept<\/code> version, or <code>false<\/code> to create the regular potentially-throwing version.<\/li>\n<li><code>Variadic<\/code> is <code>true<\/code> to mark the result as variadic, or <code>false<\/code> if not.<\/li>\n<li>The &#8220;third&#8221; parameter is either <code>Args...<\/code> to create the non-variadic version or <code>Args..., ...<\/code> to create the variadic version.<\/li>\n<\/ul>\n<p>The treatment of <code>Noexcept<\/code> takes advantage of the optional argument to the <code>noexcept<\/code> specifier, discussed earlier.<\/p>\n<p>The way we interpret the &#8220;third&#8221; parameter is a workaround for preprocessor limitations.<\/p>\n<p>Passing a comma in a macro parameter is complicated, because it is normally interpreted as a parameter separator. In order to protect it, you need to enclose the comma in parentheses, <!-- backref: The C++ preprocessor doesn't understand anything, and certainly not templates --> as we discussed some time ago.<\/p>\n<p>The hack is to use a variadic macro. The remaining parameters are all captured into the pseudo-parameter <code>__VA_ARGS__<\/code>, and you can spit them back out in the macro expansion.<\/p>\n<p>If you fail to pass anything for the variadic parameter, then the result is emptiness, but that creates a problem, because we want to include a comma prior to the <code>...<\/code> if it is present, but omit it if we are generating the non-variadic version. C++20 adds the <code>__VA_OPT__<\/code> pseudo-macro which expands its argument only if the <code>__VA_ARGS__<\/code> is nonempty. I&#8217;m trying to stick with C++17 for now, so I can&#8217;t use that.<\/p>\n<p>Instead, I make the caller pass the prior parameter <code>Args...<\/code> as well, so that the comma is &#8220;baked into&#8221; the <code>__VA_ARGS__<\/code>.<\/p>\n<p>I can simplify the above macro a bit, by inferring whether the result is variadic by comparing the function pointer against an explicitly non-variadic version.<\/p>\n<pre>#define MAKE_TRAITS(Noexcept, ...)                          \\\r\ntemplate&lt;typename R, typename... Args&gt;                      \\\r\nstruct FunctionTraits&lt;R(*)(__VA_ARGS__) noexcept(Noexcept)&gt; \\\r\n    : FunctionTraitsBase&lt;R, Args...&gt;                        \\\r\n{                                                           \\\r\n  using Pointer = R(*)(__VA_ARGS__) noexcept(Noexcept);     \\\r\n  constexpr static bool IsNoexcept = Noexcept;              \\\r\n  constexpr static bool IsVariadic =                        \\\r\n    !std::is_same_v&lt;void(*)(__VA_ARGS__), void(*)(Args...)&gt;;\\\r\n}\r\n\r\nMAKE_TRAITS(false, Args...);\r\nMAKE_TRAITS(false, Args..., ...);\r\nMAKE_TRAITS( true, Args...);\r\nMAKE_TRAITS( true, Args..., ...);\r\n\r\n#undef MAKE_TRAITS\r\n<\/pre>\n<p>I could also avoid the whole <code>__VA_ARGS__<\/code> nonsense by requiring that the argument list be parenthesized:<\/p>\n<pre>#define MAKE_TRAITS(Noexcept, ArgsList)                \\\r\ntemplate&lt;typename R, typename... Args&gt;                 \\\r\nstruct FunctionTraits&lt;R(*)ArgsList noexcept(Noexcept)&gt; \\\r\n    : FunctionTraitsBase&lt;R, Args...&gt;                   \\\r\n{                                                      \\\r\n  using Pointer = R(*)ArgsList noexcept(Noexcept);     \\\r\n  constexpr static bool IsNoexcept = Noexcept;         \\\r\n  constexpr static bool IsVariadic =                   \\\r\n    !std::is_same_v&lt;void(*)ArgsList, void(*)(Args...)&gt;;\\\r\n}\r\n\r\nMAKE_TRAITS(false, (Args...));\r\nMAKE_TRAITS(false, (Args..., ...));\r\nMAKE_TRAITS( true, (Args...));\r\nMAKE_TRAITS( true, (Args..., ...));\r\n\r\n#undef MAKE_TRAITS\r\n<\/pre>\n<p>Are we done?<\/p>\n<p>Nope, there&#8217;s still more that needs to be done to cover function pointers. We&#8217;ll look at another complication next time.<\/p>\n<p><b>In case you were wondering<\/b>: I&#8217;m not talking about <a href=\"http:\/\/www.open-std.org\/JTC1\/SC22\/WG21\/docs\/papers\/2015\/p0172r0.html\"> abominable functions<\/a>. Those things are evil, and I&#8217;m going to pretend they simply don&#8217;t exist. There&#8217;s nothing you can do with them anyway.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yet another function signature to specialize.<\/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-103984","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Yet another function signature to specialize.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103984","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=103984"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103984\/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=103984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}