Skip to content

Commit

Permalink
Merge pull request #1342 from davedissian/bugfix/sfinae-iterator-traits
Browse files Browse the repository at this point in the history
Add a SFINAE friendly iterator_traits and use that instead.
  • Loading branch information
nlohmann authored Dec 19, 2018
2 parents 5d390e9 + f1080d7 commit 4f270e3
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 20 deletions.
6 changes: 3 additions & 3 deletions include/nlohmann/detail/input/input_adapters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ class input_adapter
/// input adapter for iterator range with contiguous storage
template<class IteratorType,
typename std::enable_if<
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
input_adapter(IteratorType first, IteratorType last)
{
Expand All @@ -379,7 +379,7 @@ class input_adapter

// assertion to check that each element is 1 byte long
static_assert(
sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
sizeof(typename iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");

const auto len = static_cast<size_t>(std::distance(first, last));
Expand All @@ -403,7 +403,7 @@ class input_adapter
/// input adapter for contiguous container
template<class ContiguousContainer, typename
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
int>::type = 0>
input_adapter(const ContiguousContainer& c)
: input_adapter(std::begin(c), std::end(c)) {}
Expand Down
49 changes: 49 additions & 0 deletions include/nlohmann/detail/iterators/iterator_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once

#include <iterator> // random_access_iterator_tag

#include <nlohmann/detail/meta/void_t.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>

namespace nlohmann
{
namespace detail
{
template <typename It, typename = void>
struct iterator_types {};

template <typename It>
struct iterator_types<
It,
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
typename It::reference, typename It::iterator_category>> {
using difference_type = typename It::difference_type;
using value_type = typename It::value_type;
using pointer = typename It::pointer;
using reference = typename It::reference;
using iterator_category = typename It::iterator_category;
};

// This is required as some compilers implement std::iterator_traits in a way that
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
template <typename T, typename = void>
struct iterator_traits
{
};

template <typename T>
struct iterator_traits<T, enable_if_t<!std::is_pointer<T>::value>>
: iterator_types<T>
{
};

template <typename T>
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>> {
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
};
}
}
9 changes: 5 additions & 4 deletions include/nlohmann/detail/meta/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <utility> // declval

#include <nlohmann/json_fwd.hpp>
#include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/macro_scope.hpp>
Expand Down Expand Up @@ -131,10 +132,10 @@ template <typename T, typename = void>
struct is_iterator_traits : std::false_type {};

template <typename T>
struct is_iterator_traits<std::iterator_traits<T>>
struct is_iterator_traits<iterator_traits<T>>
{
private:
using traits = std::iterator_traits<T>;
using traits = iterator_traits<T>;

public:
static constexpr auto value =
Expand Down Expand Up @@ -251,7 +252,7 @@ struct is_compatible_array_type_impl <
// Therefore it is detected as a CompatibleArrayType.
// The real fix would be to have an Iterable concept.
not is_iterator_traits<
std::iterator_traits<CompatibleArrayType>>::value >>
iterator_traits<CompatibleArrayType>>::value >>
{
static constexpr bool value =
std::is_constructible<BasicJsonType,
Expand Down Expand Up @@ -288,7 +289,7 @@ struct is_constructible_array_type_impl <
// Therefore it is detected as a ConstructibleArrayType.
// The real fix would be to have an Iterable concept.
not is_iterator_traits <
std::iterator_traits<ConstructibleArrayType >>::value and
iterator_traits<ConstructibleArrayType >>::value and

(std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or
has_from_json<BasicJsonType,
Expand Down
2 changes: 1 addition & 1 deletion include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ SOFTWARE.
#include <functional> // hash, less
#include <initializer_list> // initializer_list
#include <iosfwd> // istream, ostream
#include <iterator> // iterator_traits, random_access_iterator_tag
#include <iterator> // random_access_iterator_tag
#include <numeric> // accumulate
#include <string> // string, stoi, to_string
#include <utility> // declval, forward, move, pair, swap
Expand Down
79 changes: 67 additions & 12 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ SOFTWARE.
#include <functional> // hash, less
#include <initializer_list> // initializer_list
#include <iosfwd> // istream, ostream
#include <iterator> // iterator_traits, random_access_iterator_tag
#include <iterator> // random_access_iterator_tag
#include <numeric> // accumulate
#include <string> // string, stoi, to_string
#include <utility> // declval, forward, move, pair, swap
Expand Down Expand Up @@ -324,12 +324,10 @@ constexpr T static_const<T>::value;

// #include <nlohmann/json_fwd.hpp>

// #include <nlohmann/detail/meta/cpp_future.hpp>
// #include <nlohmann/detail/iterators/iterator_traits.hpp>

// #include <nlohmann/detail/meta/detected.hpp>


#include <type_traits>
#include <iterator> // random_access_iterator_tag

// #include <nlohmann/detail/meta/void_t.hpp>

Expand All @@ -346,6 +344,63 @@ template <typename ...Ts> using void_t = typename make_void<Ts...>::type;
} // namespace detail
} // namespace nlohmann

// #include <nlohmann/detail/meta/cpp_future.hpp>


namespace nlohmann
{
namespace detail
{
template <typename It, typename = void>
struct iterator_types {};

template <typename It>
struct iterator_types <
It,
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
typename It::reference, typename It::iterator_category >>
{
using difference_type = typename It::difference_type;
using value_type = typename It::value_type;
using pointer = typename It::pointer;
using reference = typename It::reference;
using iterator_category = typename It::iterator_category;
};

// This is required as some compilers implement std::iterator_traits in a way that
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
template <typename T, typename = void>
struct iterator_traits
{
};

template <typename T>
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
: iterator_types<T>
{
};

template <typename T>
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
{
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
};
}
}

// #include <nlohmann/detail/meta/cpp_future.hpp>

// #include <nlohmann/detail/meta/detected.hpp>


#include <type_traits>

// #include <nlohmann/detail/meta/void_t.hpp>


// http://en.cppreference.com/w/cpp/experimental/is_detected
namespace nlohmann
Expand Down Expand Up @@ -522,10 +577,10 @@ template <typename T, typename = void>
struct is_iterator_traits : std::false_type {};

template <typename T>
struct is_iterator_traits<std::iterator_traits<T>>
struct is_iterator_traits<iterator_traits<T>>
{
private:
using traits = std::iterator_traits<T>;
using traits = iterator_traits<T>;

public:
static constexpr auto value =
Expand Down Expand Up @@ -642,7 +697,7 @@ struct is_compatible_array_type_impl <
// Therefore it is detected as a CompatibleArrayType.
// The real fix would be to have an Iterable concept.
not is_iterator_traits<
std::iterator_traits<CompatibleArrayType>>::value >>
iterator_traits<CompatibleArrayType>>::value >>
{
static constexpr bool value =
std::is_constructible<BasicJsonType,
Expand Down Expand Up @@ -679,7 +734,7 @@ struct is_constructible_array_type_impl <
// Therefore it is detected as a ConstructibleArrayType.
// The real fix would be to have an Iterable concept.
not is_iterator_traits <
std::iterator_traits<ConstructibleArrayType >>::value and
iterator_traits<ConstructibleArrayType >>::value and

(std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or
has_from_json<BasicJsonType,
Expand Down Expand Up @@ -2409,7 +2464,7 @@ class input_adapter
/// input adapter for iterator range with contiguous storage
template<class IteratorType,
typename std::enable_if<
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
int>::type = 0>
input_adapter(IteratorType first, IteratorType last)
{
Expand All @@ -2428,7 +2483,7 @@ class input_adapter

// assertion to check that each element is 1 byte long
static_assert(
sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
sizeof(typename iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");

const auto len = static_cast<size_t>(std::distance(first, last));
Expand All @@ -2452,7 +2507,7 @@ class input_adapter
/// input adapter for contiguous container
template<class ContiguousContainer, typename
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
std::is_base_of<std::random_access_iterator_tag, typename iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
int>::type = 0>
input_adapter(const ContiguousContainer& c)
: input_adapter(std::begin(c), std::end(c)) {}
Expand Down

0 comments on commit 4f270e3

Please sign in to comment.