NukerDoggie
NukerDoggie

Reputation: 3

Is it possible to create Boost multi_index MEM_FUN key extractors for a container of Boost variant?

I am attempting to implement a multi_index container of Boost::variant objects. The variant consists of two derived classes of one common base object. I have implemented a virtual function in each derived class ("extractKey()") which returns a std::pair<char,char> to provide a suitable key value regardless of which derived object occupies the variant.

How do I use apply_visitor() (perhaps in a lambda expression?) to invoke the "extractKey()" function as a CONST_MEM_FUN key extractor in order to obtain the key value? I have not been able to get the syntax correct in order to accomplish this.

I am using Visual Studio 2019 and C++17.

Edit: While I already have a much more sane and conventional solution which simply uses a container of base object pointers and virtual functions in the derived objects (and no variants!) other scenarios arise where there's a need to store fundamentally different objects (not derived from a common base object) in a multi_index container. That's the real reason why I'm hoping to find a solution to the question posed here.

Upvotes: 0

Views: 127

Answers (1)

The best way to approach this is to provide your own user-defined key extractor. Note that the fact that the variant types are derived from a common base does not play any significant role here: in a scenario without inheritance, simply apply a regular visitor that takes care of all the types in the variant (possibly with a generic lambda if all the types conform to the same syntax for getting the key).

Live Coliru Demo

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/variant/variant.hpp>
#include <utility>

struct base
{
  virtual ~base()=default;
  virtual std::pair<char,char> extractKey()const=0;
};

struct derived1:base
{
  std::pair<char,char> extractKey()const override{return{1,0};};
};

struct derived2:base
{
  std::pair<char,char> extractKey()const override{return{0,1};};
};

using namespace boost::multi_index;

using variant=boost::variant<derived1,derived2>;

struct variant_key
{
  using result_type=std::pair<char,char>;
  
  auto operator()(const variant& x)const
  {
    return boost::apply_visitor(
      [](const base& b){return b.extractKey();},
      x
    );
  }
};

using container=multi_index_container<
  variant,
  indexed_by<
    ordered_non_unique<variant_key>
  >
>;

// testing

#include <iostream>

template<typename... Ts> struct overloaded:Ts...{using Ts::operator()...;};
template<typename... Ts> overloaded(Ts...)->overloaded<Ts...>;

int main()
{
  container c;
  for(int i=2;i--;){
    c.insert(variant(derived1()));
    c.insert(variant(derived2()));
  }
  
  for(const auto& x:c){
    boost::apply_visitor(
      overloaded{
        [](const derived1&){std::cout<<"derived1 ";},
        [](const derived2&){std::cout<<"derived2 ";}
      },
      x
    );
  }
}

Output

derived2 derived2 derived1 derived1 

Upvotes: 0

Related Questions