[libc] Add a library of standalone C++ utilities.
authorSiva Chandra Reddy <sivachandra@google.com>
Mon, 27 Jan 2020 05:50:27 +0000 (21:50 -0800)
committerSiva Chandra Reddy <sivachandra@google.com>
Wed, 29 Jan 2020 21:44:02 +0000 (13:44 -0800)
Some of the existing utils in utils/UnitTest/Test.h have been moved to
this new library.

Reviewers: abrachet, gchatelet

Tags: #libc-project

Differential Revision: https://reviews.llvm.org/D73530

12 files changed:
libc/CMakeLists.txt
libc/cmake/modules/LLVMLibCRules.cmake
libc/utils/CMakeLists.txt
libc/utils/CPP/Array.h [new file with mode: 0644]
libc/utils/CPP/ArrayRef.h [new file with mode: 0644]
libc/utils/CPP/CMakeLists.txt [new file with mode: 0644]
libc/utils/CPP/README.md [new file with mode: 0644]
libc/utils/CPP/StringRef.h [new file with mode: 0644]
libc/utils/CPP/TypeTraits.h [new file with mode: 0644]
libc/utils/UnitTest/CMakeLists.txt
libc/utils/UnitTest/Test.cpp
libc/utils/UnitTest/Test.h

index ee57e5d..a4c95ee 100644 (file)
@@ -1,5 +1,8 @@
 cmake_minimum_required(VERSION 3.4.3)
 
+# Use old version of target_sources command which converts the source
+# file paths to full paths.
+cmake_policy(SET CMP0076 OLD)
 list(APPEND CMAKE_MODULE_PATH  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
 
 # The top-level source directory of libc.
index cef6673..f24b96a 100644 (file)
@@ -375,3 +375,47 @@ function(add_libc_testsuite suite_name)
   add_custom_target(${suite_name})
   add_dependencies(check-libc ${suite_name})
 endfunction(add_libc_testsuite)
+
+# Rule to add header only libraries.
+# Usage
+#    add_header_library(
+#      <target name>
+#      HDRS  <list of .h files part of the library>
+#      DEPENDS <list of dependencies>
+#    )
+function(add_header_library target_name)
+  cmake_parse_arguments(
+    "ADD_HEADER"
+    "" # No optional arguments
+    "" # No Single value arguments
+    "HDRS;DEPENDS" # Multi-value arguments
+    ${ARGN}
+  )
+
+  if(NOT ADD_HEADER_HDRS)
+    message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
+  endif()
+
+  set(FULL_HDR_PATHS "")
+  # TODO: Remove this foreach block when we can switch to the new
+  # version of the CMake policy CMP0076.
+  foreach(hdr IN LISTS ADD_HEADER_HDRS)
+    list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr})
+  endforeach()
+
+  set(interface_target_name "${target_name}_header_library__")
+
+  add_library(${interface_target_name} INTERFACE)
+  target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS})
+  if(ADD_HEADER_DEPENDS)
+    add_dependencies(${interface_target_name} ${ADD_HEADER_DEPENDS})
+  endif()
+
+  add_custom_target(${target_name})
+  add_dependencies(${target_name} ${interface_target_name})
+  set_target_properties(
+    ${target_name}
+    PROPERTIES
+      "TARGET_TYPE" "HDR_LIBRARY"
+  )
+endfunction(add_header_library)
index 3b9dfed..cbc551b 100644 (file)
@@ -1,3 +1,4 @@
+add_subdirectory(CPP)
 add_subdirectory(HdrGen)
 add_subdirectory(UnitTest)
 add_subdirectory(benchmarks)
diff --git a/libc/utils/CPP/Array.h b/libc/utils/CPP/Array.h
new file mode 100644 (file)
index 0000000..f81eeb2
--- /dev/null
@@ -0,0 +1,47 @@
+//===--------- A self contained equivalent of std::array --------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <stddef.h> // For size_t.
+
+namespace __llvm_libc {
+namespace cpp {
+
+template <class T, size_t N> struct Array {
+  static_assert(N != 0, "Cannot create a __llvm_libc::cpp::Array of size 0.");
+
+  T Data[N];
+
+  using iterator = T *;
+  using const_iterator = const T *;
+
+  constexpr T *data() { return Data; }
+  constexpr const T *data() const { return Data; }
+
+  constexpr T &front() { return Data[0]; }
+  constexpr T &front() const { return Data[0]; }
+
+  constexpr T &back() { return Data[N - 1]; }
+  constexpr T &back() const { return Data[N - 1]; }
+
+  constexpr T &operator[](size_t Index) { return Data[Index]; }
+
+  constexpr const T &operator[](size_t Index) const { return Data[Index]; }
+
+  constexpr size_t size() const { return N; }
+
+  constexpr bool empty() const { return N == 0; }
+
+  constexpr iterator begin() { return Data; }
+  constexpr const_iterator begin() const { return Data; }
+
+  constexpr iterator end() { return Data + N; }
+  const_iterator end() const { return Data + N; }
+};
+
+} // namespace cpp
+} // namespace __llvm_libc
diff --git a/libc/utils/CPP/ArrayRef.h b/libc/utils/CPP/ArrayRef.h
new file mode 100644 (file)
index 0000000..ad6e25d
--- /dev/null
@@ -0,0 +1,90 @@
+//===----------------- Self contained ArrayRef type -------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Array.h"
+
+#include <stddef.h> // For size_t.
+
+namespace __llvm_libc {
+namespace cpp {
+
+// The implementations of ArrayRef and MutualArrayRef in this file are based
+// on the implementations of the types with the same names in
+// llvm/ADT/ArrayRef.h. The implementations in this file are of a limited
+// functionality, but can be extended in an as needed basis.
+
+template <typename T> class ArrayRef {
+public:
+  using iterator = const T *;
+
+private:
+  const T *Data = nullptr;
+  size_t Length = 0;
+
+public:
+  ArrayRef() = default;
+
+  // From Array.
+  template <size_t N>
+  ArrayRef(const Array<T, N> &Arr) : Data(Arr.Data), Length(N) {}
+
+  // Construct an ArrayRef from a single element.
+  explicit ArrayRef(const T &OneElt) : Data(&OneElt), Length(1) {}
+
+  // Construct an ArrayRef from a pointer and length.
+  ArrayRef(const T *data, size_t length) : Data(data), Length(length) {}
+
+  // Construct an ArrayRef from a range.
+  ArrayRef(const T *begin, const T *end) : Data(begin), Length(end - begin) {}
+
+  // Construct an ArrayRef from a C array.
+  template <size_t N>
+  constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {}
+
+  iterator begin() const { return Data; }
+  iterator end() const { return Data + Length; }
+
+  bool empty() const { return Length == 0; }
+
+  const T *data() const { return Data; }
+
+  size_t size() const { return Length; }
+
+  const T &operator[](size_t Index) const { return Data[Index]; }
+};
+
+template <typename T> class MutableArrayRef : public ArrayRef<T> {
+public:
+  using iterator = T *;
+
+  // From Array.
+  template <size_t N> MutableArrayRef(Array<T, N> &Arr) : ArrayRef<T>(Arr) {}
+
+  // Construct from a single element.
+  explicit MutableArrayRef(T &OneElt) : ArrayRef<T>(OneElt) {}
+
+  // Construct from a pointer and length.
+  MutableArrayRef(T *data, size_t length) : ArrayRef<T>(data, length) {}
+
+  // Construct from a range.
+  MutableArrayRef(T *begin, T *end) : ArrayRef<T>(begin, end) {}
+
+  // Construct from a C array.
+  template <size_t N>
+  constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef<T>(Arr) {}
+
+  T *data() const { return const_cast<T *>(ArrayRef<T>::data()); }
+
+  iterator begin() const { return data(); }
+  iterator end() const { return data() + size(); }
+
+  T &operator[](size_t Index) const { return data()[Index]; }
+};
+
+} // namespace cpp
+} // namespace __llvm_libc
diff --git a/libc/utils/CPP/CMakeLists.txt b/libc/utils/CPP/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5cbfec2
--- /dev/null
@@ -0,0 +1,7 @@
+add_header_library(
+  standalone_cpp
+  HDRS
+    Array.h
+    ArrayRef.h
+    TypeTraits.h
+)
diff --git a/libc/utils/CPP/README.md b/libc/utils/CPP/README.md
new file mode 100644 (file)
index 0000000..756663e
--- /dev/null
@@ -0,0 +1,12 @@
+This directory contains re-implementations of some C++ standard library as well
+as some LLVM utilities. These are to be used with internal LLVM libc code and
+tests. More utilities will be added on an as needed basis. There are certain
+rules to be followed for future changes and additions:
+
+1. Only two kind of headers can be included: Other headers from this directory,
+and free standing C headers.
+2. Free standing C headers are to be included as C headers and not as C++
+headers. That is, use `#include <stddef.h>` and not `#include <cstddef>`.
+3. The utilities should be defined in the namespace `__llvm_libc::cpp`. The
+higher level namespace should have a `__` prefix to avoid symbol name pollution
+when the utilities are used in implementation of public functions.
diff --git a/libc/utils/CPP/StringRef.h b/libc/utils/CPP/StringRef.h
new file mode 100644 (file)
index 0000000..60f43b5
--- /dev/null
@@ -0,0 +1,19 @@
+//===----------------- A standalone StringRef type  -------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArrayRef.h"
+
+namespace __llvm_libc {
+namespace cpp {
+
+class StringRef : public ArrayRef<char> {
+  // More methods like those in llvm::StringRef can be added as needed.
+};
+
+} // namespace cpp
+} // namespace __llvm_libc
diff --git a/libc/utils/CPP/TypeTraits.h b/libc/utils/CPP/TypeTraits.h
new file mode 100644 (file)
index 0000000..9f55762
--- /dev/null
@@ -0,0 +1,50 @@
+//===----------------- Self contained C++ type traits -----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+namespace __llvm_libc {
+namespace cpp {
+
+template <bool B, typename T> struct EnableIf;
+template <typename T> struct EnableIf<true, T> { typedef T Type; };
+
+template <bool B, typename T>
+using EnableIfType = typename EnableIf<B, T>::Type;
+
+struct TrueValue {
+  static constexpr bool Value = true;
+};
+
+struct FalseValue {
+  static constexpr bool Value = false;
+};
+
+template <typename Type> struct IsIntegral : public FalseValue {};
+template <> struct IsIntegral<char> : public TrueValue {};
+template <> struct IsIntegral<signed char> : public TrueValue {};
+template <> struct IsIntegral<unsigned char> : public TrueValue {};
+template <> struct IsIntegral<short> : public TrueValue {};
+template <> struct IsIntegral<unsigned short> : public TrueValue {};
+template <> struct IsIntegral<int> : public TrueValue {};
+template <> struct IsIntegral<unsigned int> : public TrueValue {};
+template <> struct IsIntegral<long> : public TrueValue {};
+template <> struct IsIntegral<unsigned long> : public TrueValue {};
+template <> struct IsIntegral<long long> : public TrueValue {};
+template <> struct IsIntegral<unsigned long long> : public TrueValue {};
+template <> struct IsIntegral<bool> : public TrueValue {};
+
+template <typename Type> struct IsIntegralNotBool : public IsIntegral<Type> {};
+template <> struct IsIntegralNotBool<bool> : public FalseValue {};
+
+template <typename T> struct IsPointerType : public FalseValue {};
+template <typename T> struct IsPointerType<T *> : public TrueValue {};
+
+template <typename T1, typename T2> struct IsSame : public FalseValue {};
+template <typename T> struct IsSame<T, T> : public TrueValue {};
+
+} // namespace cpp
+} // namespace __llvm_libc
index 9d1ea10..c6e5d9a 100644 (file)
@@ -4,3 +4,5 @@ add_llvm_library(
   Test.h
   LINK_COMPONENTS Support
 )
+target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR})
+add_dependencies(LibcUnitTest standalone_cpp)
index 4b08906..1532922 100644 (file)
@@ -11,7 +11,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
 
-namespace llvm_libc {
+namespace __llvm_libc {
 namespace testing {
 
 // This need not be a class as all it has is a single read-write state variable.
@@ -224,6 +224,6 @@ bool Test::testStrNe(RunContext &Ctx, const char *LHS, const char *RHS,
 }
 
 } // namespace testing
-} // namespace llvm_libc
+} // namespace __llvm_libc
 
-int main() { return llvm_libc::testing::Test::runTests(); }
+int main() { return __llvm_libc::testing::Test::runTests(); }
index 3bdf6f0..fc1002a 100644 (file)
@@ -6,61 +6,20 @@
 //
 //===----------------------------------------------------------------------===//
 
-// This file should stricly not include any other file. Not even standard
-// library headers.
+// This file can only include headers from utils/CPP/. No other header should be
+// included.
 
-namespace llvm_libc {
-namespace testing {
-
-// We define our own EnableIf and IsIntegerType traits because we do not want to
-// include even the standard header <type_traits>.
-template <bool B, typename T> struct EnableIf;
-template <typename T> struct EnableIf<true, T> { typedef T Type; };
-
-template <bool B, typename T>
-using EnableIfType = typename EnableIf<B, T>::Type;
-
-template <typename Type> struct IsIntegerType {
-  static const bool Value = false;
-};
-
-template <> struct IsIntegerType<char> { static const bool Value = true; };
-template <> struct IsIntegerType<unsigned char> {
-  static const bool Value = true;
-};
-
-template <> struct IsIntegerType<short> { static const bool Value = true; };
-template <> struct IsIntegerType<unsigned short> {
-  static const bool Value = true;
-};
+#include "utils/CPP/TypeTraits.h"
 
-template <> struct IsIntegerType<int> { static const bool Value = true; };
-template <> struct IsIntegerType<unsigned int> {
-  static const bool Value = true;
-};
-
-template <> struct IsIntegerType<long> { static const bool Value = true; };
-template <> struct IsIntegerType<unsigned long> {
-  static const bool Value = true;
-};
-
-template <> struct IsIntegerType<long long> { static const bool Value = true; };
-template <> struct IsIntegerType<unsigned long long> {
-  static const bool Value = true;
-};
-
-template <typename T> struct IsPointerType;
-
-template <typename T> struct IsPointerType<T *> {
-  static const bool Value = true;
-};
+namespace __llvm_libc {
+namespace testing {
 
 class RunContext;
 
 // Only the following conditions are supported. Notice that we do not have
 // a TRUE or FALSE condition. That is because, C library funtions do not
-// return, but use integral return values to indicate true or false
-// conditions. Hence, it is more appropriate to use the other comparison
+// return boolean values, but use integral return values to indicate true or
+// false conditions. Hence, it is more appropriate to use the other comparison
 // condtions for such cases.
 enum TestCondition {
   Cond_None,
@@ -98,22 +57,24 @@ protected:
   static void addTest(Test *T);
 
   // We make use of a template function, with |LHS| and |RHS| as explicit
-  // parameters, for enhanced type checking. Other gtest like test unittest
-  // frameworks have a similar functions which takes a boolean argument
+  // parameters, for enhanced type checking. Other gtest like unittest
+  // frameworks have a similar function which takes a boolean argument
   // instead of the explicit |LHS| and |RHS| arguments. This boolean argument
   // is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad,
-  // mismatched |LHS| and |RHS| types can potentially succeed because of type
-  // promotion.
-  template <typename ValType,
-            EnableIfType<IsIntegerType<ValType>::Value, ValType> = 0>
+  // |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
+  // of type promotion.
+  template <
+      typename ValType,
+      cpp::EnableIfType<cpp::IsIntegralNotBool<ValType>::Value, ValType> = 0>
   static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
                    ValType RHS, const char *LHSStr, const char *RHSStr,
                    const char *File, unsigned long Line) {
     return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, File, Line);
   }
 
-  template <typename ValType,
-            EnableIfType<IsPointerType<ValType>::Value, ValType> = nullptr>
+  template <
+      typename ValType,
+      cpp::EnableIfType<cpp::IsPointerType<ValType>::Value, ValType> = nullptr>
   static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
                    ValType RHS, const char *LHSStr, const char *RHSStr,
                    const char *File, unsigned long Line) {
@@ -138,80 +99,80 @@ private:
 };
 
 } // namespace testing
-} // namespace llvm_libc
+} // namespace __llvm_libc
 
 #define TEST(SuiteName, TestName)                                              \
-  class SuiteName##_##TestName : public llvm_libc::testing::Test {             \
+  class SuiteName##_##TestName : public __llvm_libc::testing::Test {           \
   public:                                                                      \
     SuiteName##_##TestName() { addTest(this); }                                \
-    void Run(llvm_libc::testing::RunContext &) override;                       \
+    void Run(__llvm_libc::testing::RunContext &) override;                     \
     const char *getName() const override { return #SuiteName "." #TestName; }  \
   };                                                                           \
   SuiteName##_##TestName SuiteName##_##TestName##_Instance;                    \
-  void SuiteName##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
+  void SuiteName##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx)
 
 #define TEST_F(SuiteClass, TestName)                                           \
   class SuiteClass##_##TestName : public SuiteClass {                          \
   public:                                                                      \
     SuiteClass##_##TestName() { addTest(this); }                               \
-    void Run(llvm_libc::testing::RunContext &) override;                       \
+    void Run(__llvm_libc::testing::RunContext &) override;                     \
     const char *getName() const override { return #SuiteClass "." #TestName; } \
   };                                                                           \
   SuiteClass##_##TestName SuiteClass##_##TestName##_Instance;                  \
-  void SuiteClass##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
+  void SuiteClass##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx)
 
 #define EXPECT_EQ(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_EQ, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_EQ, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_EQ(LHS, RHS)                                                    \
   if (!EXPECT_EQ(LHS, RHS))                                                    \
   return
 
 #define EXPECT_NE(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_NE, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_NE, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_NE(LHS, RHS)                                                    \
   if (!EXPECT_NE(LHS, RHS))                                                    \
   return
 
 #define EXPECT_LT(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LT, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LT, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_LT(LHS, RHS)                                                    \
   if (!EXPECT_LT(LHS, RHS))                                                    \
   return
 
 #define EXPECT_LE(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LE, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LE, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_LE(LHS, RHS)                                                    \
   if (!EXPECT_LE(LHS, RHS))                                                    \
   return
 
 #define EXPECT_GT(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GT, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GT, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_GT(LHS, RHS)                                                    \
   if (!EXPECT_GT(LHS, RHS))                                                    \
   return
 
 #define EXPECT_GE(LHS, RHS)                                                    \
-  llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GE, (LHS),      \
-                                 (RHS), #LHS, #RHS, __FILE__, __LINE__)
+  __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GE, (LHS),  \
+                                   (RHS), #LHS, #RHS, __FILE__, __LINE__)
 #define ASSERT_GE(LHS, RHS)                                                    \
   if (!EXPECT_GE(LHS, RHS))                                                    \
   return
 
 #define EXPECT_STREQ(LHS, RHS)                                                 \
-  llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
-                                      __LINE__)
+  __llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS,         \
+                                        __FILE__, __LINE__)
 #define ASSERT_STREQ(LHS, RHS)                                                 \
   if (!EXPECT_STREQ(LHS, RHS))                                                 \
   return
 
 #define EXPECT_STRNE(LHS, RHS)                                                 \
-  llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
-                                      __LINE__)
+  __llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS,         \
+                                        __FILE__, __LINE__)
 #define ASSERT_STRNE(LHS, RHS)                                                 \
   if (!EXPECT_STRNE(LHS, RHS))                                                 \
   return