Tag: SFINAE

  • Multithreading in an expressive way with monadic expression

    Hi, there is a long time I have not written anything, sorry for that! I planned to write several articles during these few next weeks. This article will deal with multithreading.

    Introduction

    In a lot of articles, we can read that multithreading is not easy, and other things like that. In this article, we will learn how to write multi-threaded application in a simple and a fun way.
    The idea will be to write something like that

    int main() {
        auto test = when_all(
            [] {return 8; },    // return 8
            [] {return 25; }    // return 25
        ) // tuple(8, 25)
        .then(
            [](auto a, auto b) {return std::make_tuple(a, b); }, // return tuple(8, 25)
            [](auto a, auto b) {std::cout << a << "-" << b << " = "; }, // return nothing
            [](auto a, auto b) {return a - b; } // return - 17
        ) // tuple(tuple(8, 25), -17)
        .deferredExecutor();
    
        // Launch the chain of function in an asynchronous way
        test.execute(asynchronous_scheduler{});
        // Do some work very expensive
        auto result = test.waitAndGet();
        auto first = std::get<0>(result); // tuple(8, 25)
        auto second = std::get<1>(result); // -17
        std::cout << second << std::endl;
    
        return 0;
    }

    If we write a little scheme, it gives us that :

    Multithreading in action
    Multithreading in action

    Obviously, when the function f3 starts, functions f1 and f2 must have complete. The same thing happens for the getResult. It must wait for f3, f4, f5 to complete.

    Does it really exist?

    Actually, it does, and it will even be in the standard in C++20. However, the feature from the C++20 suffers from different problems that are explained in the Vittorio Romeo article. Actually, this article could be a continuation of the series of articles from Vittorio.

    Multithreading with Monadic Expression: Implementation

    I hope you really love read ununderstandable C++ code because the code is full of template and metaprogramming. I recommend to have some basis in really modern C++ or read the series from Vittorio I was talking before. We are going to see a lot of things both in multithreading and metaprogramming

    Why do we need a continuation of the series from Vittorio?

    Firstly, the code provided by Vittorio does not work on Visual Studio, and I really want to have my codes work on all compilers and all operating systems. Second, in its article, he only provides a version using a function waitAndGet that prevents us to insert code into the main thread in an easy way.
    The third issue is that the group of functions returns a std::tuple

    For example, you must have to write something like that

    when_all(
        []{return 8;},
        []{return 25;}
    ).then([](auto tuple) {auto [a, b] = tuple; return a - b;});
    
    // Instead of
    when_all(
        []{return 8;},
        []{return 25;}
    ).then([](auto a, auto b) {return a - b;});

    But, in reality, some people will prefer the first version, other will prefer the second version. What will happen if a function does not return any arguments? According to me, the second version is more natural.

    Forwarding

    Because we are going to use a lot of metaprogramming, we may need to perfect forward some arguments. Here are two macro that defines a perfect forwarding. There is one macro for a normal variable and another one for auto. I was not able to use the same one for both cases because sometimes, use the form ::std::forward<decltype(variable)>(variable); is too difficult for Visual Studio, that is why I provide these both declarations.

    #pragma once
    #include <utility>
    
    #define FWD(T, x) ::std::forward<T>(x)
    #define FWD_AUTO(x) ::std::forward<decltype(x)>(x)

    This code does not really need any explanations.

    A Latch

    A latch is useful when you want to know if a job is finished or not. Let’s say you have two thread, which one of them is waiting the result coming from the second one. The first one need to wait the second to finish.

    Latch
    Latch

    The following code shows how to implement a simple “bool_latch”

    #pragma once
    #include <mutex>
    #include <condition_variable>
    
    class bool_latch {
    public:
        /**
         * Function to call when the job is finished
         */
        void job_done() {
            {
                std::scoped_lock<std::mutex> lock(mMutex);
                mDone = true;
            }
            mConditionVariable.notify_one();
        }
    
        /**
         * Function to call when you want to wait for the job to be done
         */
        void wait_for_job_done() {
            std::unique_lock<std::mutex> lock(mMutex);
            mConditionVariable.wait(lock, [this] {return mDone; });
        }
    
    private:
        std::mutex mMutex;
        std::condition_variable mConditionVariable;
        bool mDone{ false };
    };
    

    The code is based on condition_variable, mutex, and a boolean variable

    The return type

    Each function may return one value. However, what returns a group of functions? The better choice is to return a std::tuple. Thus, if you have three functions that return an int at the same level (running in 3 parallels threads), the result will be a std::tuple<int, int, int>.

    Functions that return void.

    The simple way for this case is to create an empty type that we will name nothing. Its declaration is straightforward.

    struct nothing{};

    The function, instead to return nothing, must return the type nothing.
    Let’s say you have three functions, f1, f2, f3. f1 and f3 return an int and f2 returns nothing. You will get a std::tuple<int, nothing, int>
    How to return nothing instead of void? This function explains that in a straightforward way!

    namespace detail {
        template<typename F, typename ...Args>
        /**
         This function returns f(args...) when decltype(f(args...)) is not void. Returns nothing{} else.
         @param f - The function
         @param args... - Arguments to give to the function
         @result - returns f(args...) if possible, or nothing{} else.
        */
        inline decltype(auto) callThatReturnsNothingInsteadOfVoid(F &&f, Args &&...args) {
            if constexpr(std::is_void_v<std::invoke_result_t<F, Args...>>) {
                f(FWD(Args, args)...);
                return nothing{};
            }
    
            else
                return f(FWD(Args, args)...);
        }
    }

    Pass a tuple as an argument

    Okay, now say you have a std::tuple<int, double, std::string> and, you want to give it to one function with a prototype like returnType function(int, double, std::string).
    One way to do that is to use the function apply.

    Here there is no problem, but assume that there is a nothing in your tuple like std::tuple<int, nothing, int>.
    When you will apply this tuple to the function, you will also pass the nothing.

    To avoid such problem, you must filter all the arguments and store the arguments inside a lambda function.

    namespace detail {
        template<typename F>
        inline decltype(auto) callWithoutNothingAsArgument_Impl(F &&f) {
            return callThatReturnsNothingInsteadOfVoid(FWD(F, f));
        }
    
        template<typename F, typename T, typename ...Args>
        inline decltype(auto) callWithoutNothingAsArgument_Impl(F &&f, T &&t, Args &&...args) {
            return callWithoutNothingAsArgument_Impl([&f, &t](auto &&...xs) -> decltype(auto) {
                if constexpr(std::is_same_v < nothing, std::decay_t<T>>) {
                    return f(FWD_AUTO(xs)...);
                }
    
                else {
                    return f(FWD(T, t), FWD_AUTO(xs)...);
                }
            }, FWD(Args, args)...);
        }
    }
    
    template<typename F, typename ...Args>
    inline decltype(auto) callWithoutNothingAsArgument(F &&f, std::tuple<Args...> tuple) {
        return std::apply([&f](auto ...xs)->decltype(auto) {
            return detail::callWithoutNothingAsArgument_Impl(f, xs...);
        }, tuple);
    }

    Architecture

    This part will be the most difficult part of the article. We will see how the architecture work. It is an architecture with nodes. We will see three kinds of nodes. The root node that is the beginning of the chain of functions, the when_all node that can own one or several functions which will run in parallel and one node result_getter that represents the end of the chain of functions.

    Overview

    Overview of multithreading
    Overview of multithreading

    As you may have noticed, there is no type erasure. The type owned by the caller is the result_getter. However, the result_getter, when it executes all the functions, it must return to the root. And to do that, it must go through the when_all.

    Now go to explain whole the code!

    root

    #pragma once
    #include <tuple>
    #include "fwd.h"
    
    namespace detail {
        class root {
            template<typename Parent, typename... F>
            friend class when_all;
    
            // A node produce nothing
            using output_type = std::tuple<>;
    
        private:
            // Once you are at the root, you can call the execute function
            template<typename Scheduler, typename Child, typename ...GrandChildren>
            void goToRootAndExecute(Scheduler &&s, Child &directChild, GrandChildren &... grandChildren) & {
                execute(FWD(Scheduler, s), directChild, grandChildren...);
            }
    
            // You must use the scheduler
            template<typename Scheduler, typename Child, typename ...GrandChildren>
            void execute(Scheduler &&s, Child &directChild, GrandChildren &... grandChildren) & {
                s([&]() -> decltype(auto) {return directChild.execute(FWD(Scheduler, s), output_type{}, grandChildren...); });
            }
        };
    }

    There is nothing difficult here. The when_all is a friend. There are two functions, the first one is called by the children and its purpose it’s only to reach the top. After, you execute the child functions through the scheduler.

    when_all

    #pragma once
    #include 
    #include "enumerate_args.h"
    #include "result_getter.h"
    
    namespace detail {
        struct movable_atomic_size_t : std::atomic_size_t {
            using std::atomic_size_t::atomic;
            movable_atomic_size_t(movable_atomic_size_t &&v) : std::atomic_size_t(v.load(std::memory_order_acquire)) {}
        };
    
        template
        class when_all : Parent, Fs... {
            friend class root;
    
            template
            friend class ::detail::when_all;
    
            template
            friend class result_getter;
    
            using input_type = typename Parent::output_type;
            using output_type = std::tuple(), std::declval()))...>;
    
        public:
            /** There is SFINAE here because of something "kind of weird".
                Let's say you have to build something like Parent(std::move(parent)). Instead to try to reach the move constructor,
                it will try to instantiate this function with FsFWD = empty. However, Fs = {f1, f2, f3...};
                The SFINAE is to forbid this error, and so, Parent(parent) will reach the move constructor **/
            template 0)>>
            when_all(ParentFWD &&parent, FsFWD && ...f) : Parent(FWD(ParentFWD, parent)), Fs(FWD(FsFWD, f))... {}
    
            template
            decltype(auto) then(FFwd &&...ffwd) && {
                static_assert(sizeof...(FFwd) > 0, "Must have several functions objects");
                return make_when_all(std::move(*this), FWD(FFwd, ffwd)...);
            }
    
            decltype(auto) deferredExecutor() && {
                return make_result_getter(std::move(*this));
            }
    
            template
            decltype(auto) executeWaitAndGet(Scheduler &&s) && {
                auto f = deferredExecutor();
                f.execute(FWD(Scheduler, s));
                return f.waitAndGet();
            }
    
        private:
            Parent &getParent() {
                return static_cast(*this);
            }
    
            template
            void goToRootAndExecute(Scheduler &&s, Children&... children) & {
                // this is for ADL
                this->getParent().goToRootAndExecute(FWD(Scheduler, s), *this, children...);
            }
    
            template
            void execute(Scheduler &&s, input_type r, Child &directChild, GrandChildren &...grandChildren) & {
                auto exec = [&, r](auto i, auto &f) {
                    ::std::get < i >(mResult) = callWithoutNothingAsArgument(f, r);
    
                    if (mLeft.fetch_sub(1, std::memory_order::memory_order_release) == 1)
                        directChild.execute(FWD(Scheduler, s), std::move(mResult), grandChildren...);
                };
    
                auto executeOneFunction = [&s, &exec](auto i, auto f) {
                    if constexpr(i == sizeof...(Fs)-1)
                        exec(i, f);
    
                    else {
                        s([exec, i, f]() {
                            exec(i, f);
                        });
                    }
                };
    
                enumerate_args(executeOneFunction, static_cast(*this)...);
            }
    
        private:
            output_type mResult;
            movable_atomic_size_t mLeft{ sizeof...(Fs) };
        };
    
        template
        inline auto make_when_all(Parent &&parent, F&& ...f) {
            return when_all, std::decay_t...>{FWD(Parent, parent), FWD(F, f)...};
        }
    }
    
    template
    auto when_all(F&& ...f) {
        static_assert(sizeof...(F) > 0, "Must have several functions objects");
        return ::detail::make_when_all(detail::root{}, FWD(F, f)...);
    }

    This is the most difficult part.
    However, everything is not difficult. There are two things that are difficult. The output_type and the execute function.

    using input_type = typename Parent::output_type;
    using output_type = std::tuple<decltype(callWithoutNothingAsArgument(std::declval<Fs&>(), std::declval<input_type>()))...>;
    

    The input_type is straightforward, we take the output from the parent. The output_type is a bit tricky. The idea is to call all the function, and for each of them, add the result to a tuple.

    template
    void execute(Scheduler &&s, input_type r, Child &directChild, GrandChildren &...grandChildren) & {
        auto exec = [&, r](auto i, auto &f) {
            ::std::get < i >(mResult) = callWithoutNothingAsArgument(f, r);
    
            if (mLeft.fetch_sub(1, std::memory_order::memory_order_release) == 1)
                directChild.execute(FWD(Scheduler, s), std::move(mResult), grandChildren...);
        };
    
        auto executeOneFunction = [&s, &exec](auto i, auto f) {
            if constexpr(i == sizeof...(Fs)-1)
                exec(i, f);
    
            else {
                s([exec, i, f]() {
                    exec(i, f);
                });
            }
        };
        enumerate_args(executeOneFunction, static_cast(*this)...);
    }
    

    There are several things to think about.
    The exec lambda is the function that will be executed in a parallel thread. We store the value inside the good position of the tuple. If all functions have finished, we call the execute function for the directChild. This condition is checked by mLeft.

    The executeOneFunction lambda is the function that computes if the function must be launched through the scheduler or not. (If there is only one function, there is no need to launch it through the scheduler because the root already did that).

    The enumerate_args execute the function with each arguments, and give the index as an argument as well.

    The enumerate_args is following :

    #pragma once
    
    #include <type_traits>
    #include "fwd.h"
    
    namespace detail {
        template<typename F, std::size_t ...Is, typename ...Args>
        void enumerate_args_impl(F &&f, std::index_sequence<Is...>, Args && ...args) {
            using expander = int[];
            expander{ 0, ((void)f(std::integral_constant<std::size_t, Is>{}, FWD(Args, args)), 0)... };
        }
    }
    
    template<typename F, typename ...Args, typename Indices = std::index_sequence_for<Args...>>
    void enumerate_args(F &&f, Args &&...args) {
        detail::enumerate_args_impl(FWD(F, f), Indices{}, FWD(Args, args)...);
    }
    

    result_getter

    Once you are here, you may have already build your own result_getter

    #pragma once
    #include "bool_latch.h"
    #include "nothing.h"
    
    namespace detail {
    
        template<typename Parent>
        class result_getter : Parent {
            using input_type = typename Parent::output_type;
            using result_type = epurated_tuple_without_nothing_t<input_type>;
    
            template<typename, typename...>
            friend class when_all;
    
        public:
            template<typename ParentFWD>
            result_getter(ParentFWD &&parent) : Parent(std::move(parent)) {}
    
            template<typename Scheduler>
            void execute(Scheduler &&s) & {
                // this is for ADL
                this->getParent().goToRootAndExecute(FWD(Scheduler, s), *this);
            }
    
            result_type waitAndGet() & {
                mLatch.wait_for_job_done();
                return mResult;
            }
    
        private:
            Parent &getParent() {
                return static_cast<Parent&>(*this);
            }
    
            template<typename Scheduler>
            void execute(Scheduler &&, input_type r) & {
                auto setResult = [this](auto ...xs) {
                    if constexpr (sizeof...(xs) == 1)
                        mResult = result_type{ xs... };
    
                    else
                        mResult = std::make_tuple(xs...);
    
                    mLatch.job_done();
                };
    
                callWithoutNothingAsArgument(setResult, r);
            }
    
        private:
            result_type mResult;
            bool_latch mLatch;
        };
    
        // Ensure move semantic
        template<typename Parent, typename = std::enable_if_t<std::is_rvalue_reference_v<Parent&&>>>
        inline auto make_result_getter(Parent &&parent) {
            return result_getter<std::decay_t<Parent>>{std::move(parent)};
        }
    }
    

    The idea here is to execute the function and wait for the latch to return the result. All the part for epurated tuple is done here :

    namespace detail {
        template<typename...>
        struct tuple_without_nothing_impl;
    
        template<typename Tuple>
        struct tuple_without_nothing_impl<Tuple> {
            using type = Tuple;
        };
    
        template<typename ...PreviousArgs, typename T, typename ...NextArgs>
        struct tuple_without_nothing_impl<std::tuple<PreviousArgs...>, T, NextArgs...> {
            using type = std::conditional_t<
                std::is_same_v<T, nothing>,
                typename tuple_without_nothing_impl<std::tuple<PreviousArgs...>, NextArgs...>::type,
                typename tuple_without_nothing_impl<std::tuple<PreviousArgs..., T>, NextArgs...>::type>;
        };
    }
    
    template<typename Tuple>
    struct tuple_without_nothing;
    
    template<typename ...Args>
    struct tuple_without_nothing<std::tuple<Args...>> {
        using type = typename detail::tuple_without_nothing_impl<std::tuple<>, Args...>::type;
    };
    
    
    template<typename Tuple>
    using tuple_without_nothing_t = typename tuple_without_nothing<Tuple>::type;
    
    template<typename Tuple>
    struct epurated_tuple {
        using type = std::conditional_t<
            std::tuple_size_v<Tuple> == 1,
            std::tuple_element_t<0, Tuple>,
            tuple_without_nothing_t<Tuple>
        >;
    };
    
    template<>
    struct epurated_tuple<std::tuple<>> {
        using type = std::tuple<>;
    };
    
    template<typename Tuple>
    using epurated_tuple_without_nothing_t = typename epurated_tuple<tuple_without_nothing_t<Tuple>>::type;

    The idea is to remove the nothings from the tuple, and if the tuple owns only one type, the tuple is removed and we get only the type.

    Conclusion

    In this article, you learned how to write safe and easy to read multi-threaded functions. There is still a lot of things to do, but it is really usable and easy to use. And if someone else has to read your code, he should be able to do so.

    If you want to test it online, you can go on wandbox

    Reference

    Vittorio Romeo

  • Range : Be expressive using smart iterators with Range based containers

    Hi !
    Today I am not going to talk about rendering. This article will deal with expressiveness in C++. Expressiveness? Yes, but about containers, range, and iterators.
    If you want to know more about writing expressive code in C++, I advise you to go on fluentcpp.
    If you want to know more about range, I advise you to take a look at the Range V3 written by Eric Niebler.
    The code you will see may not be the most optimized, but it gives one idea behind what ranges are and how to implement it.

    Introduction

    How could we define a Range ?

    The objective

    Prior to defining what a Range is, we are going to see what Range let us do.

    int main()
    {
        std::list<int> list;
        std::vector<float> vector = {5.0, 4.0, 3.0, 2.0, 1.0, 0.0};
        list << 10 << 9 << 8 << 7 << 6 << vector;
        // list = 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
    
        auto listFiltered = list | superiorThan(4) | multiplyBy(3);
        // type of listFiltered = Range<You do not want to know lol>
        // listFiltered = [10, 9, 8, 7, 6, 5] -> 30, 27, 24, 21, 18, 15
    
        auto listSorted = Range::sort(listFiltered | superiorThan(23));
        // type of listSorted is vector, else use Range::sort<std::list>
        // listSorted = [30, 27, 24] -> 24, 27, 30
    
        std::cout << list << listFiltered << listSorted;
    
        return 0;
    }

    Isn’t it amazing to write things like that? Okay for direct operation inside the container, it could be better in two ways:

    1. It is not “easy” to read if you want to compose operation : (unique(sort(range)) is less readable than range | sort | unique in my opinion. But it is juste one “optimisation” to do :).
    2. It may be not optimized since sort returns a Container : here a vector, and build also.

    The overloading of operator<< is quite easy though:

    // writing
    template<template<typename, typename...> class Container, typename T, typename ...A>
    std::ostream &operator<<(std::ostream &stream, Container<T, A...> const &c) {
        for(auto const &e : c)
            stream << e << " ";
        stream << std::endl;
        return stream;
    }
    
    // Appending
    template<template<typename, typename...> class Container, typename T, typename ...A>
    Container<T, A...> &operator<<(Container<T, A...> &c, T const &v) {
        c.emplace_back(v);
        return c;
    }
    
    // Output must not be an ostream
    template<template<typename, typename> class Output, template<typename, typename> class Input,
             typename T1, typename A1, typename T2, typename A2>
    std::enable_if_t<!std::is_base_of<std::ostream, Output<T1, A1>>::value, Output<T1, A1>&>
    operator<<(Output<T1, A1> &o, Input<T2, A2> const &i) {
        std::copy(i.begin(), i.end(), std::back_inserter(o));
        return o;
    }
    
    template<template<typename, typename> class Output, template<typename> class Range,
             typename T1, typename A1, typename Iterator>
    std::enable_if_t<!std::is_base_of<std::ostream, Output<T1, A1>>::value, Output<T1, A1>&>
    operator<<(Output<T1, A1> &o, Range<Iterator> const &i) {
        std::copy(i.begin(), i.end(), std::back_inserter(o));
        return o;
    }

    Okay, there are a lot of templates. I hope you will not get an allergy to them. All the lines that follow will use and abuse of templates and SFINAE.

    The definition of a Range

    A range is a way to traverse a container. They are at one abstraction above of iterators. To be simple, a Range own the first iterator, and the final one. It comes with a begin and one end function.

    template<typename Iterator>
    class _Range {
    public:
        using __IS_RANGE = void; // helper To know if the type is a range or not
    public:
        using const_iterator = Iterator;
        using value_type = typename const_iterator::value_type;
        explicit _Range(const_iterator begin, const_iterator end) : mBegin(begin), mEnd(end){}
    
        const_iterator begin() const {return mBegin;}
        const_iterator end() const {return mEnd;}
    private:
        const_iterator mBegin;
        const_iterator mEnd;
    };
    
    template<typename T, typename = void>
    struct is_range : std::false_type{};
    
    template<typename T>
    struct is_range<T, typename T::__IS_RANGE> : std::true_type{};
    
    
    template<typename Iterator>
    auto Range(Iterator begin, Iterator end) {
        return _Range<Iterator>(begin, end);
    }
    
    template<typename Container>
    auto Range(Container const &c) {
        return Range(c.begin(), c.end());
    }

    Smart (Or proxy) iterator

    Okay, now things are becoming tricky. I hope that I lost no one.

    What exactly is an iterator ?

    To be simple, an iterator is an abstraction of a pointer.
    It exists several catetegories of iterators, to be simple, here is the list:

    1. input : They can be compared, incremented, and dereferenced as rvalue (output ones can be dereferenced as lvalue).
    2. Forward : not a lot of difference with the prior.
    3. Bidirectional: They can be decremented.
    4. Random Access : They support arithmetic operators + and -, inequality comparisons.

    Smart Iterator in details

    Lazy Initialization

    This statement tells us “If the result of the operation is not needed right now, there is no need to compute it”. To be simple, the operation will be done when we will need to get the result. With iterator, it could be done when you dereference it for instance.

    Different types of smart iterator

    Filter iterator

    This iterator will jump a lot of values that do not respect a predicate. For example, if you want only the odd values of your container, when you increment the iterator, it will advance until the next odd value and will skip all the even ones.

    Transform iterator

    This iterator will dereference the iterator, apply a function to the dereferenced value and returns the result.

    Implementation

    Basics

    Here we are going to implement our own iterator class. This class must be templated twice times.
    The first template argument is the iterator we want to iterate on. The second template argument is a tag that we use to perform a kind of tag dispatching.
    Moreover, this iterator must behave as … an iterator !

    So, we begin to write :

    template<class Iterator, class RangeIteratorTagStructure>
    class RangeIterator {
        Iterator mIt;
        RangeIteratorTagStructure mTag;
    public:
        using iterator_category = typename Iterator::iterator_category;
        using value_type = typename Iterator::value_type;
        using difference_type = typename Iterator::difference_type;
        using pointer = typename Iterator::pointer;
        using reference = typename Iterator::reference;

    One of above typename will fail if Iterator does not behave like one iterator.
    The Tag has a constructor and can own several data (function / functor / lambda), other iterators(the end of the range?) or other things like that.

    The iterator must respect the Open Closed Principle. That is why you must not implement the methods inside the class but outside (in a namespace detail for instance). We are going to see these methods later. To begin, we are going to stay focused on the RangeIterator class.

    Constructors

    We need 3 constructors.
    1. Default constructor
    2. Variadic templated constructor that build the tag
    3. Copy constructor

    And we need as well an assignment operator.

    RangeIterator() = default;
    
    template<typename ...Args>
    RangeIterator(Iterator begin, Args &&...tagArguments) :
        mIt(begin),
        mTag(std::forward<Args>(tagArguments)...) {
        detail::RangeIterator::construct(mIt, mTag);
    }
    
    RangeIterator(RangeIterator const &i) :
        mIt(i.mIt), mTag(i.mTag){}
    
    RangeIterator &operator=(RangeIterator f) {
        using std::swap;
        swap(f.mIt, this->mIt);
        swap(f.mTag, this->mTag);
        return *this;
    }

    There are no difficulties here.

    They also need comparison operators !

    And they are quite easy !

    bool operator !=(RangeIterator const &r) {
        return mIt != r.mIt;
    }
    
    bool operator ==(RangeIterator const &r) {
        return mIt == r.mIt;
    }

    Reference or value_type dereferencement

    I hesitate a lot between return either a reference or a copy. To make transform iterator easier, I make the return by copy.
    It means that you cannot dereference them as a lvalue :

    *it = something; // Does not work.

    The code is a bit tricky now because the dereferencement could not be the value you are waiting for. See the std::back_insert_iterator for instance.

    decltype(detail::RangeIterator::dereference(std::declval<Iterator>(), std::declval<RangeIteratorTagStructure>())) operator*() {
        return detail::RangeIterator::dereference(mIt, mTag);
    }
    
    decltype(detail::RangeIterator::dereference(std::declval<Iterator>(), std::declval<RangeIteratorTagStructure>())) operator->() {
        return detail::RangeIterator::dereference(mIt, mTag);
    }

    Forward iterator to go farther !

    Again, simple code !

    RangeIterator &operator++() {
        detail::RangeIterator::increment(mIt, mTag);
        return *this;
    }

    Backward iterator, to send you in hell !

    Okay, now as promised, we are going to see how beautiful C++ templates are. If you don’t want to be driven crazy, I advise you to stop to read here.
    So, we saw that not all iterators have the “backward” range. The idea is to enable this feature ONLY if the iterator (the first template argument) supports it also.
    It is the moment to reuse SFINAE (the first time was for the “is_range” structure we saw above).
    We are going to use the type_trait std::enable_if<Expr, type>.
    How to do that?

    template<class tag = iterator_category>
    std::enable_if_t<std::is_base_of<std::bidirectional_iterator_tag, tag>::value,
    RangeIterator>
    &operator--() {
        detail::RangeIterator::decrement(mIt, mTag);
        return *this;
    }

    You MUST template this function, else the compiler can not delete it !!!

    FYI : If you have C++17 enabled, you can use concepts (at least for GCC).

    Random iterator

    Now you can do it by yourself.
    But here some code to help you (because I am a nice guy :p)

    template<class tag = iterator_category>
    std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, tag>::value, RangeIterator>
    &operator+=(std::size_t n) {
        detail::RangeIterator::plusN(mIt, n, mTag);
        return *this;
    }
    
    template<class tag = iterator_category>
    std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, tag>::value, RangeIterator>
    operator+(std::size_t n) {
        auto tmp(*this);
        tmp += n;
        return tmp;
    }
    
    template<class tag = iterator_category>
    std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, tag>::value, difference_type>
    operator-(RangeIterator const &it) {
        return detail::RangeIterator::minusIterator(mIt, it.mIt, mTag);
    }
    
    template<class tag = iterator_category>
    std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, tag>::value, bool>
    operator<(RangeIterator const &f) {
        return mIt < f.mIt;
    }
    
    // Operator a + iterator
    template<template<typename, typename> class RIterator, typename iterator, typename tag, typename N>
    std::enable_if_t<std::is_base_of<std::random_access_iterator_tag, typename iterator::iterator_category>::value,
    RIterator<iterator, tag>> operator+(N n, RIterator<iterator, tag> const &it) {
        auto tmp(it);
        tmp += n;
        return tmp;
    }

    Details

    Okay, now we are going to see what is hiden by detail::RangeIterator.

    Normal iterators

    In this namespace, you MUST put the tag and the function on it.

    Here are the functions for normal iterator.

    /*********** NORMAL ************/
    template<typename Iterator, typename Tag>
    inline void construct(Iterator , Tag) {
    
    }
    
    template<typename Iterator, typename Tag>
    inline typename Iterator::value_type dereference(Iterator it, Tag) {
        return *it;
    }
    
    template<typename Iterator, typename Tag>
    inline void increment(Iterator &it, Tag) {
        ++it;
    }
    
    template<typename Iterator, typename Tag>
    inline void decrement(Iterator &it, Tag) {
        --it;
    }
    
    template<typename Iterator, typename Tag>
    inline void plusN(Iterator &it, std::size_t n, Tag) {
        it += n;
    }
    
    template<typename Iterator, typename Tag>
    inline void minusN(Iterator &it, std::size_t n, Tag) {
        it -= n;
    }
    
    template<typename Iterator, typename Tag>
    inline typename Iterator::difference_type minusIterator(Iterator i1, Iterator const &i2, Tag) {
        return i1 - i2;
    }

    It is simple, if it is a normal iterator, it behaves like a normal one.

    Transform iterator

    I will not talk about the filter iterator since it is not complicated to make it once we understand the ideas. Just be careful about the construct function…

    The tag

    So, what is a Transform iterator? It is simply one iterator that dereference the value, and apply a function to it.
    Here is the Tag structure.

    template<typename Iterator, typename Functor>
    struct Transform final {
        Transform() = default;
        Transform(Functor f) : f(f){}
        Transform(Transform const &f) : f(f.f){}
    
        std::function<typename Iterator::value_type(typename Iterator::value_type)> f;
    };

    It owns one std::function and that’s it.

    The usefulness of the transform iterator is when you dereference it. So you need to reimplement only the dereference function.

    template<typename Iterator, typename Functor>
    inline typename Iterator::value_type dereference(Iterator it, Transform<Iterator, Functor> f) {
        return f.f(*it);
    }

    Thanks to overloading via tag dispatching this function should (must??) be called without any issues (actually you hope :p).

    However, if you want to use several files (thing that I only can to advise you), you cannot do it by this way but specialize your templates. But you cannot partially specialize template function. The idea is to use functor!

    Here is a little example using dereference function.

    decltype(std::declval<detail::RangeIterator::dereference<Iterator, Tag>>()(std::declval<Iterator>(), std::declval<Tag>())) operator*() {
        return detail::RangeIterator::dereference<Iterator, Tag>()(mIt, mTag);
    }
    
    // Normal iterator
    template<typename Iterator, typename Tag>
    struct dereference {
        inline typename Iterator::value_type operator()(Iterator it, Tag) const {
            return *it;
        }
    };
    
    // Transform iterator
    template<typename Iterator, typename Functor>
    struct dereference<Iterator, Transform<Iterator, Functor>> {
        inline typename Iterator::value_type operator()(Iterator it, Transform<Iterator, Functor> f) {
            return f.f(*it);
        }
    };
    The builder : pipe operator (|)

    Okay, you have the iterator, you have the range class, you have your function, but now, how to gather them?

    What you want to write is something like that:

    auto range = vector | [](int v){return v * 2;};

    First, you need a function that Create one range that own two iterators.
    One that begins the set, and the other one that ends it.

    template<typename Container, typename Functor>
    auto buildTransformRange(Container const &c, Functor f) {
        using Iterator = RangeIterator<typename Container::const_iterator,
                                       detail::RangeIterator::Transform<typename Container::const_iterator, Functor>>;
        Iterator begin(c.begin(), f);
        Iterator end(c.end(), f);
        return Range(begin, end);
    }
    

    Once you have that, you want to overload the pipe operator that makes it simple :

    template<typename R, typename Functor>
    auto operator|(R const &r, Functor f) -> std::enable_if_t<std::is_same<std::result_of_t<Functor(typename R::value_type)>, typename R::value_type>::value, decltype(Range::buildTransformRange(r, f))> {
        return Range::buildTransformRange(r, f);
    }

    Warning : Don’t forget to take care about rvalue references to be easy to use !

    Conclusion

    So this article presents a new way to deal with containers. It allows more readable code and take a functional approach. There is a lot of things to learn about it, so don’t stop your learning here. Try to use one of the library below, try to develop yours. Try to learn a functional language and … Have fun !!!!

    I hope that you liked this article. It is my first article that discuss only C++. It may contains a lot of errors, if you find one or have any problems, do not forget to tell me!

    Reference

    Range V3 by Eric Niebler : His range library is really powerfull and I advice you to use it (and I hope that it will be a part of the C++20).
    Ranges: The STL to the Next Level : because of (thanks to?) him, I am doing a lot of modifications in all my projects… x).
    Range Library by me : I will do a lot of modifications : Performance, conveniance and others.