C++: Determine the class of a pointer type in the use of Boost MultiIndex

送分小仙女□ 提交于 2019-12-24 00:44:31

问题


The answer to this question has general application, but I will motivate it with the following example:

I have the following template class:

template <typename V>
class Collection
{
public:
    struct id{};
    struct name{};
    // A collection parameterized by type V that is indexed by a string 'name'
    // and a integer 'id'
    // The type V must implement 'get_id()' and a 'get_name()' functions
    typedef multi_index_container <
        V,
        indexed_by<
            ordered_unique<
                tag<id>, const_mem_fun<V, unsigned int, &V::get_id> >,
            ordered_unique<
                tag<name>, const_mem_fun<V, std::string, &V::get_name> >
        >
    > ni_collection;
>

I want to modify this template so that I can create a collection with the objects, their pointers or their references: Collection<Obj>, Collection<std::unique_ptr<Obj>> or Collection<Obj *>.

How would I modify my template to achieve this?

--- UPDATE --- I had posted a related question here: Computing The Type Of A Function Pointer

Synthesizing the excellent answers in both places, I have finally achieved my original goal. Here are the details of my current implementation:

template <typename V>
class Collection
{
private:
// A type-level function that returns the undecorated type of the object
// So unrwap_object_type<Widget *> = Widget
//    unwrap_object_type<std::unique_ptr<Widget>> = Widget
//    unwrap_object_type<Widget> = Widget
template<typename T, typename = void>
struct unwrap_object_type { typedef T type; };

template <typename T>
struct unwrap_object_type<T *, void> { typedef T type; };

template<typename T>
struct unwrap_object_type<T,
    typename std::conditional<false,
        typename T::element_type, void>::type>
{
  typedef typename T::element_type type;
};
////

// So that QHNETO_COLLECTION<Widget>, QHNETO_COLLECTION<Widet *>,
// and QHNETO_COLLECTION<std::unique_ptr<Widget>> are valid
typedef typename unwrap_object_type<V>::type W;

// Tags for the two indices (id and name) of the collection
struct id;
struct name;

// A collection parameterized by type V that is indexed by a string 'name'
// and a integer 'id'
// The type V must implement 'get_id()' and a 'get_name()' functions
typedef multi_index_container <
    V,
    indexed_by<
        ordered_unique<
            tag<id>,
            const_mem_fun<W, unsigned int, &W::get_id> >,
        ordered_unique<
            tag<name>,
            const_mem_fun<W, std::string, &W::get_name> >
    >
> ni_collection;

ni_collection m_collection;
};

回答1:


Elaborating on @sehe's answer: Boost.MultiIndex predefined key extractors handle dereferencing automatically (for instance, const_mem_fun<foo,bar,&foo::bar> can be used as is with a multi_index_container of foo*s). You can take advantage of this capability and write the following (without any user-provided key extractor):

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <memory>

namespace bmi = boost::multi_index;

template<typename T>
struct remove_pointer{using type=T;};

template<typename T>
struct remove_pointer<T*>{using type=T;};

template<typename T>
struct remove_pointer<std::shared_ptr<T>>{using type=T;};

template <typename V> class Collection {
  public:
    struct id;
    struct name;

    using W=typename remove_pointer<V>::type;

    typedef boost::multi_index_container<
        V,
        bmi::indexed_by<
            bmi::ordered_unique<
                bmi::tag<id>,
                bmi::const_mem_fun<W, unsigned int, &W::get_id>
            >,
            bmi::ordered_unique<
                bmi::tag<name>,
                bmi::const_mem_fun<W,const std::string&, &W::get_name> 
            > 
        >
    > ni_collection;
};

struct Demo {
    unsigned _id;
    std::string _name;

    Demo(unsigned _id,const std::string& _name):_id(_id),_name(_name){}

    unsigned           get_id() const { return _id; }
    std::string const& get_name() const { return _name; }
};

int main() {
    Collection<Demo>::ni_collection works{ { 42, "LTUAE" }, { 4, "PI" } };
    Collection<Demo *>::ni_collection also_works{ new Demo{ 42, "LTUAE" }, new Demo{ 4, "PI" } };
    Collection<std::shared_ptr<Demo>>::ni_collection this_too{ std::make_shared<Demo>( 42, "LTUAE" ), std::make_shared<Demo>( 4, "PI" ) };
}

The only tricky part is that const_mem_fun uses W=std::remove_pointer<V>::type (i.e. V if V is a plain type or the type it points to if it's a pointer).

Edited: Instead of std::remove_pointer<V>, the updated code uses a handcrafted remove_pointer template class partially specialized to understand T* and std::shared_ptr<T>; you can extend this to cover, for instance, std::unique_ptr<T> or any other smart pointer class you need to cater to.




回答2:


You can use a custom key extractor. E.g: Advanced features of Boost.MultiIndex key extractors

For reference elements, consider boost::reference_wrapper or std::reference_wrapper

Live On Coliru

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>

namespace bmi = boost::multi_index;

template <typename V> class Collection {
    struct id_extractor {
        typedef unsigned result_type;
        template <typename U> result_type operator()(U const&e) const { return e.get_id(); }
        template <typename U> result_type operator()(U*e)       const { return e->get_id(); }
    };
    struct name_extractor {
        typedef std::string result_type;
        template <typename U> result_type const& operator()(U const&e) const { return e.get_name(); }
        template <typename U> result_type const& operator()(U*e)       const { return e->get_name(); }
    };
  public:
    struct id;
    struct name;

    // A collection parameterized by type V that is indexed by a string 'name'
    // and a integer 'id'
    // The type V must implement 'get_id()' and a 'get_name()' functions
    typedef boost::multi_index_container<
        V, bmi::indexed_by<bmi::ordered_unique<bmi::tag<id>, name_extractor >,
                           bmi::ordered_unique<bmi::tag<name>, id_extractor > > >
        ni_collection;
};

struct Demo {
    unsigned _id;
    std::string _name;

    unsigned           get_id() const { return _id; }
    std::string const& get_name() const { return _name; }
};

int main() {
    Collection<Demo>::ni_collection works{ { 42, "LTUAE" }, { 4, "PI" } };
    Collection<Demo *>::ni_collection also_works{ new Demo{ 42, "LTUAE" }, new Demo{ 4, "PI" } };
}


来源:https://stackoverflow.com/questions/34688926/c-determine-the-class-of-a-pointer-type-in-the-use-of-boost-multiindex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!