2

This code compiles with visual studio 2015.

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/random_access_index.hpp>

#include <memory>

namespace bmi = boost::multi_index;

class Link {};

typedef std::shared_ptr<Link> Link_Ptr;


struct ByRnd {};
struct ByPtr {};

typedef boost::multi_index_container<Link_Ptr,
    bmi::indexed_by<
        bmi::random_access<
            bmi::tag<ByRnd>
        >,
        bmi::hashed_unique<
            bmi::tag<ByPtr>,
            bmi::const_mem_fun<Link_Ptr, Link*, &Link_Ptr::get>
        >
    >
> Store;
Store store;

int main() {}

However, on Ubuntu I am using GCC 6.2 and boost 1.62. And I get the following error:

error:   
could not convert template argument ‘&std::__shared_ptr<Link, (__gnu_cxx::_Lock_policy)2u>::get’ to ‘Link* (std::shared_ptr<Link>::*)() const’
    bmi::const_mem_fun<Link_Ptr, Link*, &Link_Ptr::get>

and clang 3.8:

error:  
non-type template argument of type 'Link *(std::__shared_ptr<Link, __gnu_cxx::_Lock_policy::_S_atomic>::*)() const noexcept' cannot be converted to a value of type
      'Link *(std::shared_ptr<Link>::*)() const'
                        bmi::const_mem_fun<Link_Ptr, Link*, &Link_Ptr::get>

If I use boost::shared_ptr instead, GCC compiles fine too.

sehe
  • 374,641
  • 47
  • 450
  • 633
ziv
  • 23
  • 4

1 Answers1

0

Looks like a QoI issue with GNU libstdc++. – sehe 1 hour ago

What does that mean? Is there anything I can do about it? – ziv 15 secs ago

It means "Quality of Implementation", meaning an unspecified detail in the library implementation is making life harder than you want it be (likely due to namespace versioning, but I'm guessing that a bit).

What can I do?

It looks like you just want to use the pointer identity, which is easier:

bmi::hashed_unique<bmi::tag<struct ByPtr>, bmi::identity<Link_Ptr> >

Live On Coliru

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index_container.hpp>

#include <memory>
#include <iostream>
#include <cassert>

namespace bmi = boost::multi_index;

class Link {};

typedef std::shared_ptr<Link> Link_Ptr;

typedef boost::multi_index_container<
    Link_Ptr,
    bmi::indexed_by<bmi::random_access<bmi::tag<struct ByRnd> >,
                    bmi::hashed_unique<bmi::tag<struct ByPtr>, bmi::identity<Link_Ptr> > > >
    Store;
Store store;

int main() {
    auto a = std::make_shared<Link>();
    auto b = a; // same

    auto& idx = store.get<ByPtr>();
    idx.insert(a);
    auto res = idx.insert(b);
    assert(!res.second);

    std::cout << "effective number of links: " << store.size() << "\n";
}

Alternatively

You could use bmi::global_fun:

template <typename T> static T *get_ptr(std::shared_ptr<T> const &p) 
{ return p.get(); }

Live On Coliru

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>

#include <cassert>
#include <iostream>
#include <memory>

namespace bmi = boost::multi_index;

class Link {};

typedef std::shared_ptr<Link> Link_Ptr;

template <typename T> static T *get_ptr(std::shared_ptr<T> const &p) { return p.get(); }

typedef boost::multi_index_container<
    Link_Ptr, bmi::indexed_by<bmi::random_access<bmi::tag<struct ByRnd> >,
                              bmi::hashed_unique<bmi::tag<struct ByPtr>,
                                                 bmi::global_fun<Link_Ptr const &, Link *, &get_ptr<Link> > > > >
    Store;
Store store;

int main() {
    auto a = std::make_shared<Link>();
    auto b = a; // same

    auto &idx = store.get<ByPtr>();
    idx.insert(a);
    auto res = idx.insert(b);
    assert(!res.second);

    std::cout << "effective number of links: " << store.size() << "\n";
}
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added a full demo of the alternative approach. Two working approaches tested with GCC and boost 1.6[12] – sehe Oct 27 '16 at 12:04
  • Thanks! The alternate approach does what I needed on Linux, however, it fails to compile with visual studio: test2.cpp(23): error C2971: 'boost::multi_index::global_fun': template parameter 'PtrToFunction': 'get_ptr': a variable with non-static storage duration cannot be used as a non-type argument C:\Dev\boost_1_62_0\boost/multi_index/global_fun.hpp(168): note: see declaration of 'boost::multi_index::global_fun' test2.cpp(18): note: see declaration of 'get_ptr'. Removing static from get_ptr fixes compile on windows, but breaks it on linux again. – ziv Oct 27 '16 at 12:36
  • PS. I'd suggest `identity<>` because it does what you want while expressing intent. Which is why I listed it first. – sehe Oct 27 '16 at 12:46
  • Re: "a variable with non-static storage duration cannot be used as a non-type argument" the error says it: apparently you're declaring the `get_ptr` not at global/namespace scope? Or maybe MSVC's message is confusingly worded and meant to say the symbol must have external linkage. That should work for GCC too, so just drop the static keyword: **[Coliru](http://coliru.stacked-crooked.com/a/06ac8fc643c39017)** – sehe Oct 27 '16 at 12:47
  • Re identity<>, find is being used on raw pointers in the code. Maybe it ought to work, but I don't understand the compiler error message, it is very long. – ziv Oct 27 '16 at 13:47
  • You didn't show any code that exercises this, nor the resultant error code. – sehe Oct 27 '16 at 13:48
  • I got this to compile on both platforms: [Coliru](http://coliru.stacked-crooked.com/a/ca2b16c9d3efe9c9). I'll know more tomorrow if I broke everything. – ziv Oct 27 '16 at 13:48
  • Yeah, sorry, there is a lot more code. I was hoping for a quick solution as I was under the impression that it should be pretty straight forward to replace boost shared pointers with standard – ziv Oct 27 '16 at 13:50
  • Yup. It should be, but the devil - as always - was in the details. You /might/ report it to libstdc++ developers, but I'm not convinced it's actually an error. Like I said, it seems a QoI issue mostly – sehe Oct 27 '16 at 13:51
  • @ziv I've made up what you meant with `find(T*)`, and [here's the way](http://coliru.stacked-crooked.com/a/e538942323bdb35e) you could solve that. Now to make it (way) more elegant, perhaps consider: [Live On Coliru](http://coliru.stacked-crooked.com/a/d742b58823671655) – sehe Oct 27 '16 at 14:33
  • I know this is very old but just passing the pointer to `identity` doesn't seem to do the same thing. It allows multiple instances of shared_ptr pointing to the same underlying memory whereas the other case does not. – George Reith Jul 06 '20 at 14:29
  • @GeorgeReith "does not seem to do the same thing" - as what? Also, I think you're wrong. The [sample shows](http://coliru.stacked-crooked.com/a/02ee7601d8dbd3ef) you cannot insert `b` after `a`. – sehe Jul 06 '20 at 15:05
  • If you mean **different** share-ptr control blocks still pointing to the same object, first off: that is usually an error. It's very hard(TM) to come up with a valid example of such a rogue `sthared_ptr` instance, but if you do, you can see that it also rejects the duplicate instance: **[Live On Coliru](http://coliru.stacked-crooked.com/a/548e71c4dce57570)** (watch the asserts for proof that it is factually [a separate shared_ptr control block](https://stackoverflow.com/a/12302176/85371)). – sehe Jul 06 '20 at 15:07
  • @sehe I was under the impression that the original would have rejected this: http://coliru.stacked-crooked.com/a/bab23d44159271a9 as I was playing with it locally. But I can no longer reproduce that. I most likely am wrong about that as I am pretty new to C++ so am lacking some of the concepts. Thanks for the info. – George Reith Jul 07 '20 at 10:32