Update set of supported alias-safe types (#158)
Renames `IsChar` to `IsAliasSafe`, removes `signed char` as an alias
safe type and adds in conditional support for `std::byte` as an anlias
safe type if available.
Fixes: #116
diff --git a/runtime/cpp/emboss_cpp_types.h b/runtime/cpp/emboss_cpp_types.h
index 1b02d47..9a1c211 100644
--- a/runtime/cpp/emboss_cpp_types.h
+++ b/runtime/cpp/emboss_cpp_types.h
@@ -17,6 +17,7 @@
#define EMBOSS_RUNTIME_CPP_EMBOSS_CPP_TYPES_H_
#include <climits>
+#include <cstddef>
#include <cstdint>
#include <type_traits>
@@ -77,21 +78,23 @@
using Signed = ::std::int8_t;
};
-// IsChar<T>::value is true if T is a character type; i.e. const? volatile?
-// (signed|unsigned)? char.
+// IsAliasSafe<T>::value is true if T is an alias safe type; i.e. const?
+// volatile? (unsigned)? char | std::byte.
template <typename T>
-struct IsChar {
+struct IsAliasSafe {
// Note that 'char' is a distinct type from 'signed char' and 'unsigned char'.
static constexpr bool value =
::std::is_same<char, typename ::std::remove_cv<T>::type>::value ||
- ::std::is_same<unsigned char,
- typename ::std::remove_cv<T>::type>::value ||
- ::std::is_same<signed char, typename ::std::remove_cv<T>::type>::value;
+ ::std::is_same<unsigned char, typename ::std::remove_cv<T>::type>::value
+#if __cplusplus >= 201703
+ || ::std::is_same<::std::byte, typename ::std::remove_cv<T>::type>::value
+#endif
+ ;
};
// The static member variable requires a definition.
template <typename T>
-constexpr bool IsChar<T>::value;
+constexpr bool IsAliasSafe<T>::value;
// AddSourceConst<SourceT, DestT>::Type is DestT's base type with const added if
// SourceT is const.
diff --git a/runtime/cpp/emboss_memory_util.h b/runtime/cpp/emboss_memory_util.h
index 4195e99..b1c45fa 100644
--- a/runtime/cpp/emboss_memory_util.h
+++ b/runtime/cpp/emboss_memory_util.h
@@ -74,7 +74,7 @@
struct MemoryAccessor<CharT, 1, 0, kBits> {
static_assert(kBits % 8 == 0,
"MemoryAccessor can only read and write whole-byte values.");
- static_assert(IsChar<CharT>::value,
+ static_assert(IsAliasSafe<CharT>::value,
"MemoryAccessor can only be used on pointers to char types.");
using Unsigned = typename LeastWidthInteger<kBits>::Unsigned;
@@ -323,7 +323,8 @@
// unlikely that any compiler vendor will actually change it, as there is
// probably enough real-world code that relies on uint8_t being allowed to
// alias.
- static_assert(IsChar<Byte>::value, "ContiguousBuffer requires char type.");
+ static_assert(IsAliasSafe<Byte>::value,
+ "ContiguousBuffer requires char type.");
// Because real-world processors only care about power-of-2 alignments,
// ContiguousBuffer only supports power-of-2 alignments. Note that
@@ -378,12 +379,12 @@
template <
typename T,
typename = typename ::std::enable_if<
- IsChar<typename ::std::remove_cv<typename ::std::remove_reference<
- decltype(*(::std::declval<T>().data()))>::type>::type>::value
- && ::std::is_same<
- typename AddSourceCV<decltype(*::std::declval<T>().data()),
- Byte>::Type,
- Byte>::value>::type>
+ IsAliasSafe<typename ::std::remove_cv<
+ typename ::std::remove_reference<decltype(*(
+ ::std::declval<T>().data()))>::type>::type>::value && ::std::
+ is_same<typename AddSourceCV<
+ decltype(*::std::declval<T>().data()), Byte>::Type,
+ Byte>::value>::type>
explicit ContiguousBuffer(T *bytes)
: bytes_{reinterpret_cast<Byte *>(bytes->data())}, size_{bytes->size()} {
if (bytes != nullptr)
@@ -393,10 +394,10 @@
// Constructs a ContiguousBuffer from a pointer to a char type and a size. As
// with the constructor from a container, above, Byte must be at least as
// cv-qualified as T.
- template <
- typename T,
- typename = typename ::std::enable_if<IsChar<T>::value && ::std::is_same<
- typename AddSourceCV<T, Byte>::Type, Byte>::value>>
+ template <typename T,
+ typename = typename ::std::enable_if<
+ IsAliasSafe<T>::value && ::std::is_same<
+ typename AddSourceCV<T, Byte>::Type, Byte>::value>>
explicit ContiguousBuffer(T *bytes, ::std::size_t size)
: bytes_{reinterpret_cast<Byte *>(bytes)},
size_{bytes == nullptr ? 0 : size} {
@@ -661,7 +662,7 @@
// depending on the behavior of the given string type.
template <typename String>
typename ::std::enable_if<
- IsChar<typename ::std::remove_reference<
+ IsAliasSafe<typename ::std::remove_reference<
decltype(*::std::declval<String>().data())>::type>::value,
String>::type
ToString() const {
diff --git a/runtime/cpp/test/emboss_cpp_types_test.cc b/runtime/cpp/test/emboss_cpp_types_test.cc
index 6a71d73..c8976b9 100644
--- a/runtime/cpp/test/emboss_cpp_types_test.cc
+++ b/runtime/cpp/test/emboss_cpp_types_test.cc
@@ -100,30 +100,37 @@
(::std::is_same<LeastWidthInteger<64>::Signed, ::std::int64_t>::value));
}
-TEST(IsChar, CharTypes) {
- EXPECT_TRUE(IsChar<char>::value);
- EXPECT_TRUE(IsChar<unsigned char>::value);
- EXPECT_TRUE(IsChar<signed char>::value);
- EXPECT_TRUE(IsChar<const char>::value);
- EXPECT_TRUE(IsChar<const unsigned char>::value);
- EXPECT_TRUE(IsChar<const signed char>::value);
- EXPECT_TRUE(IsChar<volatile char>::value);
- EXPECT_TRUE(IsChar<volatile unsigned char>::value);
- EXPECT_TRUE(IsChar<volatile signed char>::value);
- EXPECT_TRUE(IsChar<const volatile char>::value);
- EXPECT_TRUE(IsChar<const volatile unsigned char>::value);
- EXPECT_TRUE(IsChar<const volatile signed char>::value);
+TEST(IsAliasSafe, CharTypes) {
+ EXPECT_TRUE(IsAliasSafe<char>::value);
+ EXPECT_TRUE(IsAliasSafe<unsigned char>::value);
+ EXPECT_TRUE(IsAliasSafe<const char>::value);
+ EXPECT_TRUE(IsAliasSafe<const unsigned char>::value);
+ EXPECT_TRUE(IsAliasSafe<volatile char>::value);
+ EXPECT_TRUE(IsAliasSafe<volatile unsigned char>::value);
+ EXPECT_TRUE(IsAliasSafe<const volatile char>::value);
+ EXPECT_TRUE(IsAliasSafe<const volatile unsigned char>::value);
+#if __cplusplus >= 201703
+ EXPECT_TRUE(IsAliasSafe<::std::byte>::value);
+ EXPECT_TRUE(IsAliasSafe<const ::std::byte>::value);
+ EXPECT_TRUE(IsAliasSafe<volatile ::std::byte>::value);
+ EXPECT_TRUE(IsAliasSafe<const volatile ::std::byte>::value);
+#endif
}
-TEST(IsChar, NonCharTypes) {
+TEST(IsAliasSafe, NonCharTypes) {
struct OneByte {
char c;
};
EXPECT_EQ(1U, sizeof(OneByte));
- EXPECT_FALSE(IsChar<int>::value);
- EXPECT_FALSE(IsChar<unsigned>::value);
- EXPECT_FALSE(IsChar<const int>::value);
- EXPECT_FALSE(IsChar<OneByte>::value);
+ EXPECT_FALSE(IsAliasSafe<int>::value);
+ EXPECT_FALSE(IsAliasSafe<unsigned>::value);
+ EXPECT_FALSE(IsAliasSafe<const int>::value);
+ EXPECT_FALSE(IsAliasSafe<OneByte>::value);
+
+ EXPECT_FALSE(IsAliasSafe<signed char>::value);
+ EXPECT_FALSE(IsAliasSafe<const signed char>::value);
+ EXPECT_FALSE(IsAliasSafe<volatile signed char>::value);
+ EXPECT_FALSE(IsAliasSafe<const volatile signed char>::value);
}
TEST(AddSourceConst, AddSourceConst) {
diff --git a/runtime/cpp/test/emboss_memory_util_test.cc b/runtime/cpp/test/emboss_memory_util_test.cc
index 6974e59..6399b4c 100644
--- a/runtime/cpp/test/emboss_memory_util_test.cc
+++ b/runtime/cpp/test/emboss_memory_util_test.cc
@@ -15,6 +15,7 @@
#include <array>
#include <string>
#if __cplusplus >= 201703L
+#include <cstddef> // std::byte
#include <string_view>
#endif // __cplusplus >= 201703L
#include <vector>
@@ -38,6 +39,18 @@
using LittleEndianBitBlockN =
BitBlock<LittleEndianByteOrderer<ReadWriteContiguousBuffer>, kBits>;
+template <typename T, typename... Args>
+std::array<T, sizeof...(Args)> constexpr init_array(Args &&...args) {
+ return {T(std::forward<Args>(args))...};
+}
+
+template <typename Container, typename... Args>
+auto constexpr init_container(Args &&...args) -> Container {
+ using CharType =
+ typename ::std::remove_reference<decltype(*Container().data())>::type;
+ return {CharType(std::forward<Args>(args))...};
+}
+
TEST(GreatestCommonDivisor, GreatestCommonDivisor) {
EXPECT_EQ(4U, GreatestCommonDivisor(12, 20));
EXPECT_EQ(4U, GreatestCommonDivisor(20, 12));
@@ -56,42 +69,42 @@
template <typename CharT, ::std::size_t kAlignment, ::std::size_t kOffset,
::std::size_t kBits>
void TestMemoryAccessor() {
- alignas(kAlignment)
- CharT bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+ alignas(kAlignment) auto bytes =
+ init_array<CharT>(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
EXPECT_EQ(
0x0807060504030201UL & (~0x0UL >> (64 - kBits)),
(MemoryAccessor<CharT, kAlignment, kOffset, kBits>::ReadLittleEndianUInt(
- bytes)))
+ bytes.data())))
<< "kAlignment = " << kAlignment << "; kOffset = " << kOffset
<< "; kBits = " << kBits;
EXPECT_EQ(
0x0102030405060708UL >> (64 - kBits),
(MemoryAccessor<CharT, kAlignment, kOffset, kBits>::ReadBigEndianUInt(
- bytes)))
+ bytes.data())))
<< "kAlignment = " << kAlignment << "; kOffset = " << kOffset
<< "; kBits = " << kBits;
MemoryAccessor<CharT, kAlignment, kOffset, kBits>::WriteLittleEndianUInt(
- bytes, 0x7172737475767778UL & (~0x0UL >> (64 - kBits)));
- ::std::vector<CharT> expected_vector_after_write = {
- {0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71}};
+ bytes.data(), 0x7172737475767778UL & (~0x0UL >> (64 - kBits)));
+ auto expected_vector_after_write = init_container<std::vector<CharT>>(
+ 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71);
for (int i = kBits / 8; i < 8; ++i) {
- expected_vector_after_write[i] = i + 1;
+ expected_vector_after_write[i] = CharT(i + 1);
}
EXPECT_EQ(expected_vector_after_write,
- ::std::vector<CharT>(bytes, bytes + sizeof bytes))
+ ::std::vector<CharT>(std::begin(bytes), std::end(bytes)))
<< "kAlignment = " << kAlignment << "; kOffset = " << kOffset
<< "; kBits = " << kBits;
MemoryAccessor<CharT, kAlignment, kOffset, kBits>::WriteBigEndianUInt(
- bytes, 0x7172737475767778UL >> (64 - kBits));
- expected_vector_after_write = {
- {0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78}};
+ bytes.data(), 0x7172737475767778UL >> (64 - kBits));
+ expected_vector_after_write = init_container<std::vector<CharT>>(
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78);
for (int i = kBits / 8; i < 8; ++i) {
- expected_vector_after_write[i] = i + 1;
+ expected_vector_after_write[i] = CharT(i + 1);
}
EXPECT_EQ(expected_vector_after_write,
- ::std::vector<CharT>(bytes, bytes + sizeof bytes))
+ ::std::vector<CharT>(std::begin(bytes), std::end(bytes)))
<< "kAlignment = " << kAlignment << "; kOffset = " << kOffset
<< "; kBits = " << kBits;
@@ -119,15 +132,19 @@
template <>
void TestMemoryAccessor<char, 0, 0, 64>() {}
+#if __cplusplus >= 201703L
template <>
-void TestMemoryAccessor<signed char, 0, 0, 64>() {}
+void TestMemoryAccessor<std::byte, 0, 0, 64>() {}
+#endif
template <>
void TestMemoryAccessor<unsigned char, 0, 0, 64>() {}
TEST(MemoryAccessor, LittleEndianReads) {
TestMemoryAccessor<char, 8, 0, 64>();
- TestMemoryAccessor<signed char, 8, 0, 64>();
+#if __cplusplus >= 201703L
+ TestMemoryAccessor<std::byte, 8, 0, 64>();
+#endif
TestMemoryAccessor<unsigned char, 8, 0, 64>();
}
@@ -227,8 +244,11 @@
class ReadOnlyContiguousBufferTest : public ::testing::Test {};
typedef ::testing::Types<
/**/ ::std::vector<char>, ::std::array<char, 8>,
- ::std::vector<unsigned char>, ::std::vector<signed char>, ::std::string,
- ::std::basic_string<char>,
+ ::std::vector<unsigned char>,
+#if __cplusplus >= 201703L
+ ::std::vector<std::byte>,
+#endif
+ ::std::string, ::std::basic_string<char>,
::std::vector<unsigned char, NonstandardAllocator<unsigned char>>,
::std::basic_string<char, ::std::char_traits<char>,
NonstandardAllocator<char>>>
@@ -237,7 +257,8 @@
ReadOnlyContiguousContainerTypes);
TYPED_TEST(ReadOnlyContiguousBufferTest, ConstructionFromContainers) {
- const TypeParam bytes = {{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}};
+ const TypeParam bytes =
+ init_container<TypeParam>(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01);
using CharType =
typename ::std::remove_reference<decltype(*bytes.data())>::type;
const auto buffer = ContiguousBuffer<const CharType, 1, 0>{&bytes};
@@ -263,14 +284,17 @@
template <typename T>
class ReadWriteContiguousBufferTest : public ::testing::Test {};
typedef ::testing::Types</**/ ::std::vector<char>, ::std::array<char, 8>,
- ::std::vector<unsigned char>,
- ::std::vector<signed char>>
+#if __cplusplus >= 201703L
+ ::std::vector<std::byte>,
+#endif
+ ::std::vector<unsigned char>>
ReadWriteContiguousContainerTypes;
TYPED_TEST_SUITE(ReadWriteContiguousBufferTest,
ReadWriteContiguousContainerTypes);
TYPED_TEST(ReadWriteContiguousBufferTest, ConstructionFromContainers) {
- TypeParam bytes = {{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}};
+ TypeParam bytes =
+ init_container<TypeParam>(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01);
using CharType =
typename ::std::remove_reference<decltype(*bytes.data())>::type;
const auto buffer = ContiguousBuffer<CharType, 1, 0>{&bytes};
@@ -281,7 +305,8 @@
EXPECT_EQ(0x0807060504030201UL, buffer.template ReadBigEndianUInt<64>());
buffer.template WriteBigEndianUInt<64>(0x0102030405060708UL);
- EXPECT_EQ((TypeParam{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}),
+ EXPECT_EQ((init_container<TypeParam>(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08)),
bytes);
bytes[4] = static_cast<CharType>(255);
@@ -457,11 +482,11 @@
EXPECT_TRUE(buffer.Ok());
EXPECT_EQ(buffer.data(), reinterpret_cast<unsigned char *>(data + 1));
- ContiguousBuffer<const signed char, 2, 1> aligned_buffer;
+ ContiguousBuffer<const unsigned char, 2, 1> aligned_buffer;
aligned_buffer =
ContiguousBuffer<unsigned char, 4, 3>(data + 3, sizeof data - 3);
EXPECT_TRUE(aligned_buffer.Ok());
- EXPECT_EQ(aligned_buffer.data(), reinterpret_cast<signed char *>(data + 3));
+ EXPECT_EQ(aligned_buffer.data(), reinterpret_cast<unsigned char *>(data + 3));
}
TEST(ContiguousBuffer, ConstructionFromCompatibleContiguousBuffers) {
@@ -471,10 +496,10 @@
EXPECT_TRUE(buffer.Ok());
EXPECT_EQ(buffer.data(), reinterpret_cast<unsigned char *>(data + 1));
- ContiguousBuffer<const signed char, 2, 1> aligned_buffer{
+ ContiguousBuffer<const char, 2, 1> aligned_buffer{
ContiguousBuffer<unsigned char, 4, 3>(data + 3, sizeof data - 3)};
EXPECT_TRUE(aligned_buffer.Ok());
- EXPECT_EQ(aligned_buffer.data(), reinterpret_cast<signed char *>(data + 3));
+ EXPECT_EQ(aligned_buffer.data(), reinterpret_cast<char *>(data + 3));
}
TEST(ContiguousBuffer, ToString) {