llvm/libcxx/test/std/strings/string.view/string.view.cons/from_range.pass.cpp

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// <string_view>

//  template <class Range>
//  constexpr basic_string_view(Range&& range);

#include <string_view>
#include <array>
#include <cassert>
#include <iterator>
#include <ranges>
#include <type_traits>
#include <vector>

#include "make_string.h"
#include "test_iterators.h"
#include "test_range.h"

template <class CharT>
constexpr void test() {
  auto data = MAKE_STRING_VIEW(CharT, "test");
  std::array<CharT, 4> arr;
  for (int i = 0; i < 4; ++i) {
    arr[i] = data[i];
  }
  auto sv = std::basic_string_view<CharT>(arr);

  ASSERT_SAME_TYPE(decltype(sv), std::basic_string_view<CharT>);
  assert(sv.size() == arr.size());
  assert(sv.data() == arr.data());
}

constexpr bool test() {
  test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
  test<wchar_t>();
#endif
  test<char8_t>();
  test<char16_t>();
  test<char32_t>();

  {
    struct NonConstConversionOperator {
      const char* data_ = "test";
      constexpr const char* begin() const { return data_; }
      constexpr const char* end() const { return data_ + 4; }
      constexpr operator std::basic_string_view<char>() { return "NonConstConversionOp"; }
    };

    NonConstConversionOperator nc;
    std::string_view sv = nc;
    assert(sv == "NonConstConversionOp");
    static_assert(!std::is_constructible_v<std::string_view,
                                           const NonConstConversionOperator&>); // conversion operator is non-const
  }

  {
    struct ConstConversionOperator {
      const char* data_ = "test";
      constexpr const char* begin() const { return data_; }
      constexpr const char* end() const { return data_ + 4; }
      constexpr operator std::basic_string_view<char>() const { return "ConstConversionOp"; }
    };
    ConstConversionOperator cv;
    std::basic_string_view<char> sv = cv;
    assert(sv == "ConstConversionOp");
  }

  struct DeletedConversionOperator {
    const char* data_ = "test";
    constexpr const char* begin() const { return data_; }
    constexpr const char* end() const { return data_ + 4; }
    operator std::basic_string_view<char>() = delete;
  };

  struct DeletedConstConversionOperator {
    const char* data_ = "test";
    constexpr const char* begin() const { return data_; }
    constexpr const char* end() const { return data_ + 4; }
    operator std::basic_string_view<char>() const = delete;
  };

  static_assert(std::is_constructible_v<std::string_view, DeletedConversionOperator>);
  static_assert(std::is_constructible_v<std::string_view, const DeletedConversionOperator>);
  static_assert(std::is_constructible_v<std::string_view, DeletedConstConversionOperator>);
  static_assert(std::is_constructible_v<std::string_view, const DeletedConstConversionOperator>);

  // Test that we're not trying to use the type's conversion operator to string_view in the constructor.
  {
    const DeletedConversionOperator d;
    std::basic_string_view<char> csv = std::basic_string_view<char>(d);
    assert(csv == "test");
  }

  {
    DeletedConstConversionOperator dc;
    std::basic_string_view<char> sv = std::basic_string_view<char>(dc);
    assert(sv == "test");
  }

  // Different trait types
  {
    struct OtherTraits : std::char_traits<char> {};
    std::basic_string_view<char> sv1{"hello"};
    std::basic_string_view<char, OtherTraits> sv2(sv1);
    assert(sv1.size() == sv2.size());
    assert(sv1.data() == sv2.data());
  }

  return true;
}

static_assert(std::is_constructible_v<std::string_view, std::vector<char>&>);
static_assert(std::is_constructible_v<std::string_view, const std::vector<char>&>);
static_assert(std::is_constructible_v<std::string_view, std::vector<char>&&>);
static_assert(std::is_constructible_v<std::string_view, const std::vector<char>&&>);

using SizedButNotContiguousRange = std::ranges::subrange<random_access_iterator<char*>>;
static_assert(!std::ranges::contiguous_range<SizedButNotContiguousRange>);
static_assert(std::ranges::sized_range<SizedButNotContiguousRange>);
static_assert(!std::is_constructible_v<std::string_view, SizedButNotContiguousRange>);

using ContiguousButNotSizedRange =
    std::ranges::subrange<contiguous_iterator<char*>,
                          sentinel_wrapper<contiguous_iterator<char*>>,
                          std::ranges::subrange_kind::unsized>;
static_assert(std::ranges::contiguous_range<ContiguousButNotSizedRange>);
static_assert(!std::ranges::sized_range<ContiguousButNotSizedRange>);
static_assert(!std::is_constructible_v<std::string_view, ContiguousButNotSizedRange>);

static_assert(!std::is_constructible_v<std::string_view, std::vector<char16_t>>); // different CharT

struct WithStringViewConversionOperator {
  char* begin() const;
  char* end() const;
  operator std::string_view() const { return {}; }
};

static_assert(std::is_constructible_v<std::string_view, WithStringViewConversionOperator>);        // lvalue
static_assert(std::is_constructible_v<std::string_view, const WithStringViewConversionOperator&>); // const lvalue
static_assert(std::is_constructible_v<std::string_view, WithStringViewConversionOperator&&>);      // rvalue

#ifndef TEST_HAS_NO_EXCEPTIONS
void test_throwing() {
  struct ThrowingData {
    char* begin() const { return nullptr; }
    char* end() const { return nullptr; }
    char* data() const {
      throw 42;
      return nullptr;
    }
  };
  try {
    ThrowingData x;
    (void)std::string_view(x);
    assert(false);
  } catch (int i) {
    assert(i == 42);
  }

  struct ThrowingSize {
    char* begin() const { return nullptr; }
    char* end() const { return nullptr; }
    std::size_t size() const {
      throw 42;
      return 0;
    }
  };
  try {
    ThrowingSize x;
    (void)std::string_view(x);
    assert(false);
  } catch (int i) {
    assert(i == 42);
  }
}
#endif

static_assert(!std::is_convertible_v<std::vector<char>, std::string_view>);

int main(int, char**) {
  test();
  static_assert(test());
#ifndef TEST_HAS_NO_EXCEPTIONS
  test_throwing();
#endif

  return 0;
}