{"id":103559,"date":"2020-03-13T07:00:00","date_gmt":"2020-03-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103559"},"modified":"2020-03-12T21:47:00","modified_gmt":"2020-03-13T04:47:00","slug":"20200313-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200313-00\/?p=103559","title":{"rendered":"Providing a better error message when someone tries to use <CODE>std::vector&lt;bool&gt;<\/CODE> as a buffer"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200312-00\/?p=103556\"> Last time<\/a>, we looked at how we could generate a useful error message if somebody tried to pass <code>std::vector&lt;bool&gt;<\/code> to our <code>buffer_<\/code><code>view<\/code> class. The <code>std::vector&lt;bool&gt;<\/code> is unlike all the other <code>std::vector&lt;T&gt;<\/code> types because it does not require its storage to be in the form of a traditional C array, which means that it is not possible to obtain direct access to the underlying storage.<\/p>\n<p>Last time, we struggled with this buffer type:<\/p>\n<pre>struct buffer_view\r\n{\r\n  template&lt;typename C&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v) :\r\n    data(v.data()), size(v.size() * sizeof(C)) { }\r\n\r\n  \/\/ Imagine other constructors for std::array, etc.\r\n\r\n  void const* data;\r\n  std::size_t size;\r\n};\r\n<\/pre>\n<p>If somebody tries to create a <code>buffer_<\/code><code>view<\/code> from a <code>std::vector&lt;bool&gt;<\/code>, they get an incomprehensible error message because there is no <code>v.data()<\/code> method. (For some reason, gcc and clang do have a <code>data()<\/code> method, but it doesn&#8217;t return anything interesting, so the error message is <i>even more<\/i> incomprehensible.)<\/p>\n<p>We addressed the problem last time by introducing an overload of the constructor that is active only for <code>std::vector&lt;bool&gt;<\/code>, and putting a <code>static_<\/code><code>assert<\/code> in the body with a deceptively type-dependent expression so that the assertion wasn&#8217;t raised until the overload was invoked.<\/p>\n<p>I noted that <a href=\"https:\/\/kennykerr.ca\/\"> Kenny Kerr<\/a> came up with a simpler solution: <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/commit\/0a9256c5b2bd75568c25130c819b383f8871c179\"> Move the call to <code>data()<\/code> to a helper function<\/a>, and templatize that helper.<\/p>\n<pre>struct buffer_view\r\n{\r\n  template&lt;typename C&gt;\r\n  buffer_view(std::vector&lt;C&gt; const&amp; v) :\r\n    data(<span style=\"color: blue;\">get_data(v)<\/span>), size(v.size() * sizeof(C)) { }\r\n\r\n  \/\/ Imagine other constructors for std::array, etc.\r\n\r\n  void const* data;\r\n  std::size_t size;\r\n\r\n<span style=\"color: blue;\">private:\r\n  void const* get_data(std::vector&lt;C&gt; const&amp; v)\r\n  {\r\n    static_assert(!is_same_v&lt;C, bool&gt;,\r\n      \"Can't use std::vector&lt;bool&gt;. Try std::array instead.\");\r\n    return v.data();\r\n  }<\/span>\r\n};\r\n<\/pre>\n<p>The <code>static_<\/code><code>assert<\/code> comes ahead of the call to <code>v.data()<\/code>, so it becomes the first error message.<\/p>\n<p>You could go even further and make it the <i>only<\/i> error message by adding some <code>if constexpr<\/code>:<\/p>\n<pre>  void const* get_data(std::vector&lt;C&gt; const&amp; v)\r\n  {\r\n    static_assert(!is_same_v&lt;C, bool&gt;,\r\n      \"Can't use std::vector&lt;bool&gt;. Try std::array instead.\");\r\n    <span style=\"color: blue;\">if constexpr (!is_same_v&lt;C, bool&gt;) {\r\n      return v.data();\r\n    } else {\r\n      return nullptr;\r\n    }<\/span>\r\n  }\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Defer the problem further.<\/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-103559","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Defer the problem further.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103559","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=103559"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103559\/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=103559"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103559"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103559"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}