简体   繁体   中英

Boost::multi_index with map

I have a question about modifying elements in boost::multi_index container. What I have is the structure, containing some pre-defined parameters and a number of parameters, which are defined at run-time, and stored in a map. Here is a simplified version of the structure:

class Sdata{

    QMap<ParamName, Param> params; // parameters defined at run-time

public:

    int num;
    QString key;
    // more pre-defined parameters
    // methods to modify the map
    // as an example - mock version of a function to add the parameter
    // there are more functions operating on the QMAP<...>, which follow the same 
    // rule - return true if they operated successfully, false otherwise.
    bool add_param(ParamName name, Param value){
        if (params.contains(name)) return false;
        params.insert(name, value);
        return true;    
    }

}; 

Now, I want to iterate over different combinations of the pre-defined parameters of Sdata. To do this, I went for boost::multi_index:

typedef multi_index_container<Sdata,
indexed_by <
// by insertion order
    random_access<>,
//by key
    hashed_unique<
        tag<sdata_tags::byKey>,
        const_mem_fun<Sdata, SdataKey, &Sdata::get_key>
    >,
//by TS
    ordered_non_unique<
        tag<sdata_tags::byTS>,
        const_mem_fun<Sdata, TS, &Sdata::get_ts>
    >,

    /// more keys and composite-keys
>//end indexed by
> SdataDB;

And now, I want to access and modify the parameters inside the QMap<...> .

Q1 Do I get it correctly that to modify any field (even those unrelated to the index), one needs to use functors and do something as below?

Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
l.modify(it, Functor(...))

Q2 How to get the result of the method using the functor? Ie, I have a functor:

struct SdataRemoveParam : public std::unary_function<Sdata, void>{
    ParamName name;
    SdataRemoveParam(ParamName h): name(h){}
    void operator ()(Sdata &sdata){
        sdata.remove_param (name); // this returns false if there is no param
    }
};

How to know if the remove_param returned true or false in this example:

Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
l.modify(it, SdataRemoveParam("myname"));

What I've arrived to so far is to throw an exception, so that the modify method of boost::multi_index, when using with Rollback functor will return false :

struct SdataRemoveParam : public std::unary_function<Sdata, void>{
    ParamName name;
    SdataRemoveParam(ParamName h): name(h){}
    void operator ()(Sdata &sdata){
        if (!sdata.remove_param (name)) throw std::exception("Remove failed");
    }
};

// in some other place

Sdatas_byKey const &l = sdatas.get<sdata_tags::byKey>();
auto it = l.find(key);
bool res = l.modify(it, SdataRemoveParam("myname"), Rollback);

However, I do not like the decision, because it increases the risk of deleting the entry from the container.

Q3 are there any better solutions?

Q1 Do I get it correctly that to modify any field (even those unrelated to the index), one needs to use functors and do something as below?

Short answer is yes, use modify for safety. If you're absolutely sure that the data you modify does not belong to any index, then you can get by with an ugly cast:

const_cast<Sdata&>(*it).remove_param("myname");

but this is strongly discouraged. With C++11 (which you seem to be using), you can use lambdas rather than cumbersome user-defined functors:

Sdatas_byKey &l = sdatas.get<sdata_tags::byKey>(); // note, this can't be const
auto it = l.find(key);
l.modify(it, [](Sdata& s){
  s.remove_param("myname");
});

Q2 How to get the result of the method using the functor?

Again, with lambdas this is very simple:

bool res;
l.modify(it, [&](Sdata& s){
  res=s.remove_param("myname");
});

With functors you can do the same but it requires more boilerplate (basically, have SdataRemoveParam store a pointer to res ).

The following is just for fun: if you're using C++14 you can encapsulate the whole idiom very tersely like this (C++11 would be slightly harder):

template<typename Index,typename Iterator,typename F>
auto modify_inner_result(Index& i,Iterator it,F f)
{
  decltype(f(std::declval<typename Index::value_type&>())) res;
  i.modify(it,[&](auto& x){res=f(x);});
  return res;
}
...
bool res=modify_inner_result(l,it, [&](Sdata& s){
  return s.remove_param("myname");
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM