{"id":1491,"date":"2016-01-14T18:40:06","date_gmt":"2016-01-14T08:40:06","guid":{"rendered":"http:\/\/brnz.org\/hbr\/?p=1491"},"modified":"2016-01-16T17:03:36","modified_gmt":"2016-01-16T07:03:36","slug":"c11-countof-return-of-the-preprocessor","status":"publish","type":"post","link":"https:\/\/brnz.org\/hbr\/?p=1491","title":{"rendered":"Another C++11 &#8216;countof&#8217;"},"content":{"rendered":"<p><em>Note: <a href=\"https:\/\/brnz.org\/hbr\/?p=1501\">There&#8217;s an update here<\/a>.<\/em><\/p>\n<p>Read &#8220;<a href=\"http:\/\/www.g-truc.net\/post-0708.html\">Better array &#8216;countof&#8217; implementation with C++ 11<\/a>&#8221; for context. Specifically, it presents Listing 5 as an implementation of <code>countof()<\/code> using C++11 constexpr:<\/p>\n<ul class=\"code-list\">\n<li class=\"code-line\">\n<pre escaped=\"true\" lang=\"cpp\" line=\"1\">template&lt;typename T, std::size_t N&gt;\r\nconstexpr std::size_t countof(T const (&amp;)[N]) noexcept\r\n{\r\n  return N;\r\n}<\/pre>\n<\/li>\n<\/ul>\n<p>But this falls short. Just a little.<\/p>\n<p>There are arguments that could be passed to a naive <code>sizeof(a)\/sizeof(a[0])<\/code> macro that will cause the above to fail to compile.<\/p>\n<p>Consider:<\/p>\n<pre escaped=\"true\" lang=\"cpp\" line=\"1\">struct S\r\n{\r\n  int a[4];\r\n};\r\n\r\nvoid f(S* s)\r\n{\r\n  constexpr size_t s_a_count = countof(s-&gt;a); \r\n  int b[s_a_count]; \r\n  \/\/ do things...\r\n}<\/pre>\n<p>This does not compile. <code>s<\/code> is not constant, and <code>countof()<\/code> is a constexpr function whose result is needed at compile time, and so expects a constexpr-friendly argument. Even though it is not used.<\/p>\n<p>Errors from this kind of thing can look like this from clang-3.7.0:<\/p>\n<pre escaped=\"true\">error: constexpr variable 's_a_count' must be initialized by a \r\n       constant expression\r\nnote:  read of non-constexpr variable 's' is not allowed in a \r\n       constant expression<\/pre>\n<p>or this from Visual Studio 2015 Update 1:<\/p>\n<pre escaped=\"true\">error: C2131: expression did not evaluate to a constant<\/pre>\n<p>(Aside: At the time of writing, the error C2131 seems to be undocumented for VS2015. But <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/aa229516(v=vs.60).aspx\">Visual Studio 6.0 had an error with the same number<\/a>)<\/p>\n<p>Here&#8217;s a C++11 version of <code>countof()<\/code> that will give the correct result for <code>countof(s-&gt;a)<\/code> above:<\/p>\n<pre escaped=\"true\" lang=\"cpp\" line=\"1\">#include &lt;type_traits&gt;\r\n\r\ntemplate&lt;typename Tin&gt;\r\nconstexpr std::size_t\u00a0countof()\r\n{\r\n  using T = typename std::remove_reference&lt;Tin&gt;::type;\r\n  static_assert(std::is_array&lt;T&gt;::value, \r\n                \"countof() requires an array argument\");\r\n  static_assert(std::extent&lt;T&gt;::value &gt; 0,  \/\/ [0]\r\n                \"zero- or unknown-size array\");\r\n  return std::extent&lt;T&gt;::value;\r\n}\r\n\r\n#define countof(a) countof&lt;decltype(a)&gt;()<\/pre>\n<p>Some of the details:<\/p>\n<p>Adding a <code>countof()<\/code> macro allows use of <code>decltype()<\/code> in the caller&#8217;s context, which provides the type of the member array of a non-const object at compile time.<\/p>\n<p><code>std::remove_reference<\/code> is needed to get the array type from the result of <code>decltype()<\/code>. Without it, <code>std::is_array<\/code> and <code>std::extent<\/code> produce false and zero, respectively.<\/p>\n<p>The first static assert ensures that <code>countof()<\/code> is being called on an actual array. The upside over failed template instantiation or specialization is that you can write your own human-readable, slightly more context aware error message (better than mine).<\/p>\n<p>The second static assert validates that the array size is known, and is greater than zero. Without it,\u00a0<code>countof&lt;int[]&gt;()<\/code> will return zero (which will be wrong) without error. And zero-sized arrays will also result in zero &#8212; in practice they rarely actually contain zero elements. This isn&#8217;t a function for finding the size of those arrays.<\/p>\n<p><span style=\"line-height: 1.6471;\">And then <code>std::extent&lt;T&gt;::value<\/code> produces the actual count of the elements of the array.<\/span><\/p>\n<hr \/>\n<p>Addendum:<\/p>\n<p>If replacing an existing <code>sizeof<\/code>-based macro with a constexpr\u00a0<code>countof()<\/code>\u00a0alternate, Visual Studio 2015 Update 1 will trigger warnings in certain cases where there previously were no warnings.<\/p>\n<pre escaped=\"true\">warning C4267: conversion from 'size_t' to 'int', possible loss of data\r\n<\/pre>\n<p>It is unfortunate to have to add explicit casts when the safety of such operations is able to be determined by the compiler. I have optimistically\u00a0<a href=\"https:\/\/connect.microsoft.com\/VisualStudio\/feedback\/details\/2242614\">submitted this as an issue at connect.microsoft.com<\/a>.<\/p>\n<p>[0] Typo fix thanks to <a href=\"https:\/\/www.reddit.com\/r\/cpp\/comments\/40znpn\/another_c11_countof\/cyylwr0\">this commentor<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Note: There&#8217;s an update here. Read &#8220;Better array &#8216;countof&#8217; implementation with C++ 11&#8221; for context. Specifically, it presents Listing 5 as an implementation of countof() using C++11 constexpr: template&lt;typename T, std::size_t N&gt; constexpr std::size_t countof(T const (&amp;)[N]) noexcept { return N; } But this falls short. Just a little. There are arguments that could be &hellip; <a href=\"https:\/\/brnz.org\/hbr\/?p=1491\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Another C++11 &#8216;countof&#8217;&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,26],"tags":[],"_links":{"self":[{"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/posts\/1491"}],"collection":[{"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1491"}],"version-history":[{"count":10,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/posts\/1491\/revisions"}],"predecessor-version":[{"id":1504,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=\/wp\/v2\/posts\/1491\/revisions\/1504"}],"wp:attachment":[{"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1491"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1491"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/brnz.org\/hbr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1491"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}