{"id":103978,"date":"2020-07-13T07:00:00","date_gmt":"2020-07-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103978"},"modified":"2020-07-13T08:17:45","modified_gmt":"2020-07-13T15:17:45","slug":"20200713-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200713-00\/?p=103978","title":{"rendered":"Deconstructing function pointers in a C++ template"},"content":{"rendered":"<p>I always forget how to deconstruct a function pointer type in a C++ template, so I&#8217;m writing it down.<\/p>\n<pre>template&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{\r\n    using Pointer = R(*)(Args...);\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    using FirstArg = NthArg&lt;0&gt;;\r\n    using LastArg = NthArg&lt;ArgCount - 1&gt;;\r\n};\r\n<\/pre>\n<p>We start by saying that <code>FunctionTraits<\/code> is a template class that takes a single type parameter, but don&#8217;t actually provide any class definition yet. The class definition will come from the specializations.<\/p>\n<p>We then define a specialization that takes a function pointer, where <code>R<\/code> is the return type, and where <code>Args...<\/code> are the argument types. This specialization defines a bunch of stuff.<\/p>\n<p>This gets us started, but there are a bunch of weird things that cause this definition to fail.<\/p>\n<p>One is the case of a function with no parameters. In that case, the <code>FirstArg<\/code> and <code>LastArg<\/code> will not compile, since there are no arguments to be the first or last of. We&#8217;ll need to specialize for that case.<\/p>\n<pre>template&lt;typename R&gt;\r\nstruct FunctionTraits&lt;R(*)()&gt;\r\n{\r\n    using Pointer = R(*)();\r\n    using RetType = R;\r\n    using ArgTypes = std::tuple&lt;&gt;;\r\n    static constexpr std::size_t ArgCount = 0;\r\n};\r\n<\/pre>\n<p>The specialization for the no-parameter case doesn&#8217;t define <code>NthArg<\/code>, <code>FirstArg<\/code> or <code>LastArg<\/code>, since those things don&#8217;t exist.<\/p>\n<p>We can factor the common pieces into a base class to avoid repeating ourselves quite so much.<\/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};\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  template&lt;std::size_t N&gt;\r\n  using NthArg = std::tuple_element_t&lt;N, ArgTypes&gt;;\r\n  using FirstArg = NthArg&lt;0&gt;;\r\n  using LastArg = NthArg&lt;ArgCount - 1&gt;;\r\n};\r\n\r\ntemplate&lt;typename R&gt;\r\nstruct FunctionTraits&lt;R(*)()&gt;\r\n    : FunctionTraitsBase&lt;R&gt;\r\n{\r\n  using Pointer = R(*)();\r\n};\r\n<\/pre>\n<p>Unfortunately, this doesn&#8217;t work because <code>ArgCount<\/code> is not not recognized as dependent names. We have to give the compiler a little help:\u00b9<\/p>\n<pre>template&lt;typename R, typename... Args&gt;\r\nstruct FunctionTraits&lt;R(*)(Args...)&gt;\r\n    : FunctionTraitsBase&lt;R, Args...&gt;\r\n{\r\n  <span style=\"color: blue;\">using base = FunctionTraitsBase&lt;R, Args...&gt;;<\/span>\r\n  using Pointer = R(*)(Args...);\r\n  template&lt;std::size_t N&gt;\r\n  using NthArg = std::tuple_element_t&lt;N, ArgTypes&gt;;\r\n  using FirstArg = NthArg&lt;0&gt;;\r\n  using LastArg = NthArg&lt;<span style=\"color: blue;\">base::<\/span>ArgCount - 1&gt;;\r\n};\r\n<\/pre>\n<p>That looks pretty good. But we&#8217;re not done yet, not by a longshot. Next time, we&#8217;ll investigate one of the categories of function pointers that this template cannot recognize.<\/p>\n<p>\u00b9 For dependent names that represent members, you can use <code>this-&gt;<\/code> to mark them as dependent names if you are using them in an instance member function. But for dependent names that are types, or for use where there is no <code>this<\/code>, there doesn&#8217;t seem to be an easy way to identify them as dependent.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learning the magic incantation.<\/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-103978","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Learning the magic incantation.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103978","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=103978"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103978\/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=103978"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103978"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103978"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}