[multi_index] Indices interface type for overload resolution

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[multi_index] Indices interface type for overload resolution

Boost - Users mailing list
Hi,

I have currently

template <typename Key, typename Value>
std::vector<Key> keys(const std::map<Key, Value> &m) { ... }

I'd like to write an overload that works also with the indices interface of multi_index_container.

Suppose I have a multi_index_container with ordered unique indexes tagged "by_id" and "by_name".

struct by_id {};
struct by_name {};
using BMI = multi_index::multi_index_container<Value,
     multi_index::indexed_by<
         multi_index::ordered_unique<by_id, multi_index::key<&Value::id>>,
         multi_index::ordered_unique<by_name, multi_index::key<&Value::name>>
 >>;

BMI bmi;
auto k = keys(bmi.get<by_name>());

Based on the documentation, I tried

template< typename Tag,typename Value,typename IndexSpecifierList,typename Allocator >
auto keys(const typename multi_index::index< multi_index::multi_index_container<Value,IndexSpecifierList,Allocator>,Tag>::type& m)
{
     typedef decltype(c.key_extractor()(*c.begin())) key_type; // A better definition would be welcome
     std::vector<key_type> result;
     result.reserve(c.size());
     for (const auto &elem : c) {
         result.push_back(c.key_extractor()(elem));
     }
     return result;
}

But the overload resolution fails.
What should be the interface of keys in order to support indices interface of multi_index_container?

Thank you,

Christophe B

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [multi_index] Indices interface type for overload resolution

Boost - Users mailing list
On Wed, Nov 20, 2019 at 2:51 PM Christophe B via Boost-users <[hidden email]> wrote:
template <typename Key, typename Value>
std::vector<Key> keys(const std::map<Key, Value> &m) { ... }

I'd like to write an overload that works also with the indices interface of multi_index_container.

template <typename Key, typename Value>
std::vector<Key> keys(const std::map<Key, Value> &m) { ... }

I suspect that you only need to make your template a little more generic, something like (untested):

template <typename C>
std::vector<typename C::key_type> keys(const C& c) {
  std::vector<typename C::key_type> result;
  result.reserve(c.size());  
     for (const auto& entry : c) {
         result.emplace_back(entry.first);
     }
     return result;  

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [multi_index] Indices interface type for overload resolution

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
El 20/11/2019 a las 14:12, Christophe B via Boost-users escribió:

> Hi,
>
> I have currently
>
> template <typename Key, typename Value>
> std::vector<Key> keys(const std::map<Key, Value> &m) { ... }
>
> I'd like to write an overload that works also with the indices
> interface of multi_index_container.
>
> [...]
>
> template< typename Tag,typename Value,typename
> IndexSpecifierList,typename Allocator >
> auto keys(const typename multi_index::index<
> multi_index::multi_index_container<Value,IndexSpecifierList,Allocator>,Tag>::type&
> m)
> {
>     typedef decltype(c.key_extractor()(*c.begin())) key_type; // A
> better definition would be welcome
>     std::vector<key_type> result;
>     result.reserve(c.size());
>     for (const auto &elem : c) {
>         result.push_back(c.key_extractor()(elem));
>     }
>     return result;
> }
>
> But the overload resolution fails.

The second overload is not picked up because Tag, Value, etc. are being
used in a nondeduced context:

https://stackoverflow.com/questions/1268504/why-is-the-template-argument-deduction-not-working-here

So, this approach won't get you anywhere. You have two options:

1. You can use the name of the actual class implementing ordered indices:

     template<typename... Args>
     auto keys(const boost::multi_index::detail::ordered_index<Args...>&
c){...}

THIS IS STRONGLY DISCOURAGED, because this name is not documented.
Basically, you're relying on
an implementation detail. Rather use:

2. With SFINAE and a little ingenuity you can have something like:

     template<
       typename Index,
       std::enable_if_t<
         sizeof(typename Index::key_type)&&
         sizeof(std::declval<const Index>().key_extractor())
       >* =nullptr
     >
     auto keys(const Index& c)
     {
       using key_type=typename Index::key_type;
       std::vector<key_type> result;
       result.reserve(c.size());
       for(const auto &elem:c){
         result.push_back(c.key_extractor()(elem));
       }
       return result;
     }

Here, we're using SFINAE to activate the overload *only* when Index does
have a nested
key_type type and a key_extractor member function, which can be taken as
a very certain
bet that we're indeed dealing with a key-based Boost.MultiIndex index.
This is, if you wish,
poor man's pre-C++20 concepts. BTW, this overloads also picks hashed and
ranked indices.

Best,

Joaquín M López Muñoz

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users