Boost logo

Boost :

Subject: [boost] Picking overload with the most specific concept
From: Louis Dionne (louis.dionne92_at_[hidden])
Date: 2013-02-10 23:28:28


Hi,

I was playing around today and I achieved to do something I find quite cool.
I would like to know whether the technique/general principle is of any
interest. Basically, it allows to overload functions based on the modeling of
a concept by a type without having to write complicated enable_ifs. It does
come with some problems and it is not very general for the moment, but it's a
first shot. Here we go:

    #include <duck.hpp> // the library where I implemented that

    #include <forward_list>
    #include <utility>
    #include <vector>

    // Generates some boilerplate required to enable concept overloading for
    // a function named `my_distance`. Ignore this for now.
    DUCK_ENABLE_CONCEPT_OVERLOADING(
        my_distance,
        my_distance(std::declval<Iterator>(), std::declval<Iterator>(), State()),
        typename Iterator
    )

    struct random_access_version_picked { };
    struct forward_version_picked { };

    // The State template parameter will be used to carry information during
    // the resolution. tag::my_distance was defined by the above macro.
    template <typename Iterator, typename State = tag::my_distance>

    // This can be seen like an enable_if with some more functionality.
    typename duck::requires<

        // This overload is picked iff Iterator models RandomAccessIterator
        // and no other overload for a more specific concept exist.
        // Concepts are MPL metafunction classes, so there's no magic here.
        duck::model_of<duck::RandomAccessIterator, Iterator>

        // Could add more duck::model_of clauses here

      , duck::overload_resolution_state<State> // required for the mechanics
      , duck::template_parameters<Iterator> // ditto

    , random_access_version_picked>::type // return type
    my_distance(Iterator, Iterator, State = State()) {
        return random_access_version_picked();
    }

    // Same thing for the ForwardIterator implementation.
    template <typename Iterator, typename State = tag::my_distance>
    typename duck::requires<
        duck::model_of<duck::ForwardIterator, Iterator>

      , duck::overload_resolution_state<State>
      , duck::template_parameters<Iterator>

    , forward_version_picked>::type
    my_distance(Iterator, Iterator, State = State()) {
        return forward_version_picked();
    }

    int main() {
        std::forward_list<int> list;
        std::vector<int> vec;

        forward_version_picked forward = my_distance(begin(list), end(list));
        random_access_version_picked random = my_distance(begin(vec),end(vec));
    }

This example works with Clang 3.3 and GCC 4.8. For those interested in seeing
more, the full library is available at:
    http://github.com/ldionne/duck

Look for the `next` branch. The file involved with the above mechanics is
include/duck/requires.hpp. Also see the test in test/requires.cpp.

Regards,

Louis Dionne


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk