pointer to member variable in nested struct

753 views Asked by At

For the below class, how do I represent the pointer to the member variable qux of one of the instances of Bar?

struct Foo {
  struct Bar {
    int qux;
  } bar1, bar2;
};

This is needed when I use the boost::multi_index container and need to use qux as the key, which is needed in the key extractor

template<class Class,typename Type,Type Class::*PtrToMember>
struct boost::multi_index::member
3

There are 3 answers

0
Pradhan On BEST ANSWER

I am assuming the intention is to create a boost multi_index container of Foos and use qux as the key. Though qux isn't a member of Foo, you can achieve this by indexing on the member Foo::bar1 and providing a custom comparison predicate for the ordered index. For example, if you are trying to create an ordered_unique key, you would write it like this:

ordered_unique<member<Foo, Foo::Bar, &Foo::bar1>, compare_foo_bar>

where compare_foo_bar is a friend of Foo::Bar and is defined as need:

struct compare_foo_bar
{
bool operator()(const Foo::Bar& lhs, const Foo::Bar& rhs)
{
  return lhs.qux < rhs.qux;
}
};
0
Joaquín M López Muñoz On

You can resort to user-defined key extractors:

struct Foobar1qux
{
  typedef int result_type;

  int operator()(const Foo &x)const{return x.bar1.qux;}
};

struct Foobar2qux
{
  typedef int result_type;

  int operator()(const Foo &x)const{return x.bar2.qux;}
};

typedef multi_index_container<
  Foo,
  indexed_by<
    ordered_non_unique<Foobar1qux>,
    ordered_non_unique<Foobar2qux>
  >
> multi_t1;

A more generic approach is to cascade key extractors as shown in one of the examples of Boost.MultiIndex documentation:

template<class KeyExtractor1,class KeyExtractor2>
struct key_from_key
{
public:
  typedef typename KeyExtractor1::result_type result_type;

  key_from_key(
    const KeyExtractor1& key1_=KeyExtractor1(),
    const KeyExtractor2& key2_=KeyExtractor2()):
    key1(key1_),key2(key2_)
  {}

  template<typename Arg>
  result_type operator()(Arg& arg)const
  {
    return key1(key2(arg));
  }

private:
  KeyExtractor1 key1;
  KeyExtractor2 key2;
};

typedef multi_index_container<
  Foo,
  indexed_by<
    ordered_non_unique<
      key_from_key<
        member<Foo::Bar,int,&Foo::Bar::qux>,
        member<Foo,Foo::Bar,&Foo::bar1>
      >
    >,
    ordered_non_unique<
      key_from_key<
        member<Foo::Bar,int,&Foo::Bar::qux>,
        member<Foo,Foo::Bar,&Foo::bar2>
      >
    >
  >
> multi_t2;
2
Praetorian On

In your case you have Class = Foo::Bar, Type = int and PtrToMember = &Foo::Bar::qux, so this should work

boost::multi_index::member<Foo::Bar, int, &Foo::Bar::qux>

Based on your comment below, I modified the basic example from the Boost.MultiIndex tutorial to match your use case. The original example contains the following struct

/* an employee record holds its ID, name and age */
struct employee
{
  int         id;
  std::string name;
  int         age;

  employee(int id_,std::string name_,int age_):id(id_),name(name_),age(age_){}
  // ...
};

And the multi_index container is defined as

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<
      tag<id>,  BOOST_MULTI_INDEX_MEMBER(employee,int,id)>,
    ordered_non_unique<
      tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name)>,
    ordered_non_unique<
      tag<age>, BOOST_MULTI_INDEX_MEMBER(employee,int,age)> >
> employee_set;

Now, let us modify employee so that employee::name is actually a member of a nested struct Bar, and say employee contains two instances of Bar.

struct employee
{
  int           id;
  struct Bar
  {
    Bar(std::string name) : name(name) {}

    std::string name;
  } bar1, bar2;
  int           age;

  // ...
};

But you can't modify this declaration

ordered_non_unique<
      tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name)>

to indicate the nested struct's data member directly. Instead, you need to modify the declaration to

ordered_non_unique<
  tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,employee::Bar,bar1)>

We need a way to order the employee::Bar objects, so add a comparison operator to its definition

struct Bar
{
  // ...
  bool operator<(Bar const& other) const { return name < other.name; }
};

With these changes, if you index into the container using the tag name, you'll be ordering it based on the bar1.name data member.

Here's a complete working example.

I also initialized bar2.name to contain the reverse sequence of characters as that contained in bar1.name, and added the option to index based on that using the tag name2.