c++serializationstdboost-range

boost::has_range_const_iterator replacement with std


I have a big serialization library that rely on boost::has_range_const_iterator. The below code is part of a serialization library used in a game network.

template<typename T>
auto operator()(T const& vals) const -> typename std::enable_if<boost::has_range_const_iterator<T>::value>::type
{
    auto length = std::distance(std::begin(vals), std::end(vals));

    if (length > std::numeric_limits<uint16_t>::max())
        throw BadMessage("Length is too big to fit into length variable");

    (*this)(static_cast<uint16_t>(length));

    for (auto& val : vals)
        (*this)(val);
}

Is there some replacement in std for boost::has_range_const_iterator in the above context?

EDIT: What i have tried?

#include <iterator>
#include <list>
#include <vector>
#include <type_traits>
#include <iostream>

template <typename T>
struct has_const_iterator {
private:
    template <typename C>
    static std::true_type test(typename std::add_const<typename C::const_iterator>::type*);
    
    template <typename C>
    static std::false_type test(...);

public:
    static const bool value = decltype(test<T>(nullptr))::value;
};

int main() {
    // Example usage:
    std::vector<int> vec;
    if ( has_const_iterator<decltype(vec)>::value )
        std::cout << "Vector has a const iterator.";
    
    std::list<int> myList;
    if (!has_const_iterator<decltype(myList)>::value)
        std::cout << "List does not have a const iterator.";
    
    return 0;
}

it is good like this? Has anyone a better alternative ?


Solution

  • You could probably use the concept std::ranges::input_range instead:

    #include <ranges>
    
    auto operator()(std::ranges::input_range auto&& vals) const {
        auto length = std::distance(std::ranges::cbegin(vals),
                                    std::ranges::cend(vals));
    
        //...
    }
    

    You could also create a concept of your own to explicitly check if std::ranges::cbegin and cend are supported (although the standard range concept is most probably enough):

    template <class T>
    concept const_iterable = requires(T& t) {
        std::ranges::cbegin(t);
        std::ranges::cend(t);
    };
    

    You could also simplify your type trait somewhat:

    template <class, class = void>
    struct has_const_iterator : std::false_type {};
    
    template <class T>
    struct has_const_iterator<
        T, std::void_t<decltype(std::begin(std::declval<const T&>()))>>
        : std::true_type {};
    
    // helper variable:
    template <class T>
    inline constexpr bool has_const_iterator_v = has_const_iterator<T>::value;