span_tests.cpp raw

   1  // Copyright (c) 2023 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <span.h>
   6  
   7  #include <boost/test/unit_test.hpp>
   8  #include <array>
   9  #include <set>
  10  #include <vector>
  11  
  12  namespace {
  13  struct Ignore
  14  {
  15      template<typename T> Ignore(T&&) {}
  16  };
  17  template<typename T>
  18  bool Spannable(T&& value, decltype(Span{value})* enable = nullptr)
  19  {
  20      return true;
  21  }
  22  bool Spannable(Ignore)
  23  {
  24      return false;
  25  }
  26  
  27  #if defined(__clang__)
  28  #    pragma clang diagnostic push
  29  #    pragma clang diagnostic ignored "-Wunneeded-member-function"
  30  #    pragma clang diagnostic ignored "-Wunused-member-function"
  31  #endif
  32  struct SpannableYes
  33  {
  34      int* data();
  35      size_t size();
  36  };
  37  struct SpannableNo
  38  {
  39      void* data();
  40      size_t size();
  41  };
  42  #if defined(__clang__)
  43  #    pragma clang diagnostic pop
  44  #endif
  45  } // namespace
  46  
  47  BOOST_AUTO_TEST_SUITE(span_tests)
  48  
  49  // Make sure template Span template deduction guides accurately enable calls to
  50  // Span constructor overloads that work, and disable calls to constructor overloads that
  51  // don't work. This makes it is possible to use the Span constructor in a SFINAE
  52  // contexts like in the Spannable function above to detect whether types are or
  53  // aren't compatible with Spans at compile time.
  54  //
  55  // Previously there was a bug where writing a SFINAE check for vector<bool> was
  56  // not possible, because in libstdc++ vector<bool> has a data() member
  57  // returning void*, and the Span template guide ignored the data() return value,
  58  // so the template substitution would succeed, but the constructor would fail,
  59  // resulting in a fatal compile error, rather than a SFINAE error that could be
  60  // handled.
  61  BOOST_AUTO_TEST_CASE(span_constructor_sfinae)
  62  {
  63      BOOST_CHECK(Spannable(std::vector<int>{}));
  64      BOOST_CHECK(!Spannable(std::set<int>{}));
  65      BOOST_CHECK(!Spannable(std::vector<bool>{}));
  66      BOOST_CHECK(Spannable(std::array<int, 3>{}));
  67      BOOST_CHECK(Spannable(Span<int>{}));
  68      BOOST_CHECK(Spannable("char array"));
  69      BOOST_CHECK(Spannable(SpannableYes{}));
  70      BOOST_CHECK(!Spannable(SpannableNo{}));
  71  }
  72  
  73  BOOST_AUTO_TEST_SUITE_END()
  74