encoding: avoid implicit instantiation of `std::char_traits<unsigned char>`
According to the C++ standard, char_traits is not defined for unsigned
char, so don't attempt to instantiate it. This fixes building against
LLVM libc++ 19 and later, and thus with Xcode 16.3 and presumably also
on other platforms/toolchains that use libc++ by default such as Android
and FreeBSD.
Change-Id: Id0fe9c72268176a1e51d4cf2a88b9ab5d67c61d1
diff --git a/ndn-cxx/encoding/tlv.hpp b/ndn-cxx/encoding/tlv.hpp
index 43a603b..85a1398 100644
--- a/ndn-cxx/encoding/tlv.hpp
+++ b/ndn-cxx/encoding/tlv.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2025 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -280,21 +280,36 @@
*
* This is not a full ContiguousIterator detection implementation. It returns true for
* the most common ContiguousIterator types used with TLV decoding function templates.
+ *
+ * @todo Replace with std::contiguous_iterator concept when we migrate to C++20.
*/
template<typename Iterator,
typename DecayedIterator = std::decay_t<Iterator>,
typename ValueType = typename std::iterator_traits<DecayedIterator>::value_type>
-inline constexpr bool IsContiguousIterator =
- (std::is_convertible_v<DecayedIterator, const ValueType*> ||
- std::is_convertible_v<DecayedIterator, typename std::basic_string<ValueType>::const_iterator> ||
- std::is_convertible_v<DecayedIterator, typename std::vector<ValueType>::const_iterator>) &&
- sizeof(ValueType) == 1 && !std::is_same_v<ValueType, bool>;
+using IsContiguousIterator =
+#ifndef _LIBCPP_VERSION
+ std::disjunction<
+#endif
+ std::conjunction<
+ std::bool_constant<sizeof(ValueType) == 1>,
+ std::negation<std::is_same<ValueType, bool>>,
+ std::disjunction<
+ std::is_convertible<DecayedIterator, const ValueType*>,
+ std::is_convertible<DecayedIterator, typename std::vector<ValueType>::const_iterator>>>
+// ugly hack to unbreak the build with libc++ >= 19, which doesn't define char_traits<unsigned char>
+#ifndef _LIBCPP_VERSION
+ ,
+ std::conjunction<
+ std::is_same<ValueType, char>,
+ std::is_convertible<DecayedIterator, typename std::basic_string<ValueType>::const_iterator>>>
+#endif
+ ;
template<typename Iterator>
constexpr bool
readNumber(size_t size, Iterator& begin, Iterator end, uint64_t& number) noexcept
{
- if constexpr (IsContiguousIterator<Iterator>) {
+ if constexpr (IsContiguousIterator<Iterator>()) {
// fast path
if (begin + size > end) {
return false;
diff --git a/tests/unit/encoding/tlv.t.cpp b/tests/unit/encoding/tlv.t.cpp
index e38b5f2..be0d0df 100644
--- a/tests/unit/encoding/tlv.t.cpp
+++ b/tests/unit/encoding/tlv.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2025 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -59,9 +59,9 @@
using StreamIterator = std::istream_iterator<uint8_t>;
#define ASSERT_READ_NUMBER_IS_FAST(T) \
- static_assert(ndn::tlv::detail::IsContiguousIterator<T>, #T " is not fast")
+ static_assert(ndn::tlv::detail::IsContiguousIterator<T>(), #T " is not fast")
#define ASSERT_READ_NUMBER_IS_SLOW(T) \
- static_assert(!ndn::tlv::detail::IsContiguousIterator<T>, #T " is not slow")
+ static_assert(!ndn::tlv::detail::IsContiguousIterator<T>(), #T " is not slow")
ASSERT_READ_NUMBER_IS_FAST(const uint8_t*);
ASSERT_READ_NUMBER_IS_FAST(uint8_t*);
@@ -80,6 +80,8 @@
ASSERT_READ_NUMBER_IS_FAST(CharArray::iterator);
ASSERT_READ_NUMBER_IS_FAST(span<const uint8_t>::iterator);
ASSERT_READ_NUMBER_IS_FAST(span<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(span<int8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(span<char>::iterator);
ASSERT_READ_NUMBER_IS_FAST(std::string::const_iterator);
ASSERT_READ_NUMBER_IS_FAST(std::string::iterator);
ASSERT_READ_NUMBER_IS_FAST(Buffer::const_iterator);
@@ -94,6 +96,7 @@
ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint16_t>::iterator);
ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint32_t>::iterator);
ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint64_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<std::vector<uint8_t>>::iterator);
ASSERT_READ_NUMBER_IS_SLOW(std::deque<uint8_t>::iterator);
ASSERT_READ_NUMBER_IS_SLOW(std::list<uint8_t>::iterator);
ASSERT_READ_NUMBER_IS_SLOW(StreamIterator);