Merge pull request #1866 from ylavic/std_allocator_traits

Make StdAllocator C++17-20 compatible.
diff --git a/include/rapidjson/allocators.h b/include/rapidjson/allocators.h
index 03b2dcc..2871542 100644
--- a/include/rapidjson/allocators.h
+++ b/include/rapidjson/allocators.h
@@ -19,6 +19,10 @@
 
 #include <memory>
 
+#if RAPIDJSON_HAS_CXX11
+#include <type_traits>
+#endif
+
 RAPIDJSON_NAMESPACE_BEGIN
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -420,6 +424,11 @@
     public std::allocator<T>
 {
     typedef std::allocator<T> allocator_type;
+#if RAPIDJSON_HAS_CXX11
+    typedef std::allocator_traits<allocator_type> traits_type;
+#else
+    typedef allocator_type traits_type;
+#endif
 
 public:
     typedef BaseAllocator BaseAllocatorType;
@@ -449,29 +458,51 @@
     ~StdAllocator() RAPIDJSON_NOEXCEPT
     { }
 
-    typedef typename allocator_type::value_type       value_type;
-    typedef typename allocator_type::pointer          pointer;
-    typedef typename allocator_type::const_pointer    const_pointer;
-    typedef typename allocator_type::reference        reference;
-    typedef typename allocator_type::const_reference  const_reference;
-    typedef typename allocator_type::size_type        size_type;
-    typedef typename allocator_type::difference_type  difference_type;
-
     template<typename U>
     struct rebind {
         typedef StdAllocator<U, BaseAllocator> other;
     };
 
+    typedef typename traits_type::size_type         size_type;
+    typedef typename traits_type::difference_type   difference_type;
+
+    typedef typename traits_type::value_type        value_type;
+    typedef typename traits_type::pointer           pointer;
+    typedef typename traits_type::const_pointer     const_pointer;
+
 #if RAPIDJSON_HAS_CXX11
-    using allocator_type::max_size;
-    using allocator_type::address;
-    using allocator_type::construct;
-    using allocator_type::destroy;
-#else
-    size_t max_size() const RAPIDJSON_NOEXCEPT
+
+    typedef typename std::add_lvalue_reference<value_type>::type &reference;
+    typedef typename std::add_lvalue_reference<typename std::add_const<value_type>::type>::type &const_reference;
+
+    pointer address(reference r) const RAPIDJSON_NOEXCEPT
     {
-        return allocator_type::max_size();
+        return std::addressof(r);
     }
+    const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
+    {
+        return std::addressof(r);
+    }
+
+    size_type max_size() const RAPIDJSON_NOEXCEPT
+    {
+        return traits_type::max_size(*this);
+    }
+
+    template <typename ...Args>
+    void construct(pointer p, Args&&... args)
+    {
+        traits_type::construct(*this, p, std::forward<Args>(args)...);
+    }
+    void destroy(pointer p)
+    {
+        traits_type::destroy(*this, p);
+    }
+
+#else // !RAPIDJSON_HAS_CXX11
+
+    typedef typename allocator_type::reference       reference;
+    typedef typename allocator_type::const_reference const_reference;
 
     pointer address(reference r) const RAPIDJSON_NOEXCEPT
     {
@@ -482,6 +513,11 @@
         return allocator_type::address(r);
     }
 
+    size_type max_size() const RAPIDJSON_NOEXCEPT
+    {
+        return allocator_type::max_size();
+    }
+
     void construct(pointer p, const_reference r)
     {
         allocator_type::construct(p, r);
@@ -490,7 +526,8 @@
     {
         allocator_type::destroy(p);
     }
-#endif
+
+#endif // !RAPIDJSON_HAS_CXX11
 
     template <typename U>
     U* allocate(size_type n = 1, const void* = 0)
@@ -578,13 +615,13 @@
     ~StdAllocator() RAPIDJSON_NOEXCEPT
     { }
 
-    typedef typename allocator_type::value_type value_type;
-
     template<typename U>
     struct rebind {
         typedef StdAllocator<U, BaseAllocator> other;
     };
 
+    typedef typename allocator_type::value_type value_type;
+
 private:
     template <typename, typename>
     friend class StdAllocator; // access to StdAllocator<!T>.*
diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h
index 8034c49..1093581 100644
--- a/include/rapidjson/rapidjson.h
+++ b/include/rapidjson/rapidjson.h
@@ -125,6 +125,19 @@
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
+// __cplusplus macro
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+
+#if defined(_MSC_VER)
+#define RAPIDJSON_CPLUSPLUS _MSVC_LANG
+#else
+#define RAPIDJSON_CPLUSPLUS __cplusplus
+#endif
+
+//!@endcond
+
+///////////////////////////////////////////////////////////////////////////////
 // RAPIDJSON_HAS_STDSTRING
 
 #ifndef RAPIDJSON_HAS_STDSTRING
@@ -411,7 +424,7 @@
 
 // Prefer C++11 static_assert, if available
 #ifndef RAPIDJSON_STATIC_ASSERT
-#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
+#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
 #define RAPIDJSON_STATIC_ASSERT(x) \
    static_assert(x, RAPIDJSON_STRINGIFY(x))
 #endif // C++11
@@ -542,7 +555,7 @@
 // C++11 features
 
 #ifndef RAPIDJSON_HAS_CXX11
-#define RAPIDJSON_HAS_CXX11 (__cplusplus >= 201103L)
+#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L)
 #endif
 
 #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
@@ -610,9 +623,17 @@
 ///////////////////////////////////////////////////////////////////////////////
 // C++17 features
 
-#if defined(__has_cpp_attribute)
-# if __has_cpp_attribute(fallthrough)
-#  define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]]
+#ifndef RAPIDJSON_HAS_CXX17
+#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L)
+#endif
+
+#if RAPIDJSON_HAS_CXX17
+# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]]
+#elif defined(__has_cpp_attribute)
+# if __has_cpp_attribute(clang::fallthrough)
+#  define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]]
+# elif __has_cpp_attribute(fallthrough)
+#  define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough))
 # else
 #  define RAPIDJSON_DELIBERATE_FALLTHROUGH
 # endif
diff --git a/test/unittest/allocatorstest.cpp b/test/unittest/allocatorstest.cpp
index b8a2191..76e34b5 100644
--- a/test/unittest/allocatorstest.cpp
+++ b/test/unittest/allocatorstest.cpp
@@ -107,12 +107,12 @@
         arr[i] = 0x0f0f0f0f;
     }
     ia.deallocate(arr, 10);
-    arr = (int *)ia.Malloc(10 * sizeof(int));
+    arr = Malloc<int>(ia, 10);
     EXPECT_TRUE(arr != 0);
     for (int i = 0; i < 10; ++i) {
         arr[i] = 0x0f0f0f0f;
     }
-    arr = (int *)ia.Realloc(arr, 10 * sizeof(int), 20 * sizeof(int));
+    arr = Realloc<int>(ia, arr, 10, 20);
     EXPECT_TRUE(arr != 0);
     for (int i = 0; i < 10; ++i) {
         EXPECT_EQ(arr[i], 0x0f0f0f0f);
@@ -120,7 +120,7 @@
     for (int i = 10; i < 20; i++) {
         arr[i] = 0x0f0f0f0f;
     }
-    ia.Free(arr);
+    Free<int>(ia, arr, 20);
 
     int cons = 0, dest = 0;
     StdAllocator<TestStdAllocatorData, Allocator> da(a);