Boost::multi_index с картой

У меня вопрос об изменении элементов в контейнере boost::multi_index. У меня есть структура, содержащая некоторые предопределенные параметры и ряд параметров, которые определяются во время выполнения и хранятся в карте. Вот упрощенная версия структуры:

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;    
    }

}; 

Теперь я хочу перебрать различные комбинации предопределенных параметров Sdata. Для этого я пошел на 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;

И теперь я хочу получить доступ и изменить параметры внутри файла QMap<...>.

Q1 Правильно ли я понимаю, что для изменения любого поля (даже не связанного с индексом) нужно использовать функторы и делать что-то, как показано ниже?

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

Q2 Как получить результат метода с помощью функтора? То есть у меня есть функтор:

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
    }
};

Как узнать, вернул ли remove_param true или false в этом примере:

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

До сих пор я пришел к тому, чтобы генерировать исключение, чтобы метод modify boost::multi_index при использовании с функтором Rollback возвращал 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);

Однако мне это решение не нравится, потому что оно увеличивает риск удаления записи из контейнера.

Q3 есть ли решения получше?


person pausag    schedule 20.10.2015    source источник


Ответы (1)


Q1 Правильно ли я понимаю, что для изменения любого поля (даже не связанного с индексом) нужно использовать функторы и делать что-то, как показано ниже?

Короткий ответ: да, используйте modify для безопасности. Если вы абсолютно уверены, что изменяемые вами данные не принадлежат какому-либо индексу, вы можете обойтись безобразным приведением:

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

но это настоятельно не рекомендуется. С С++ 11 (который вы, кажется, используете) вы можете использовать лямбда-выражения, а не громоздкие пользовательские функторы:

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 Как получить результат метода с помощью функтора?

Опять же, с лямбдами это очень просто:

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

С функторами вы можете сделать то же самое, но для этого потребуется больше шаблонов (в основном, пусть SdataRemoveParam хранит указатель на res).

Следующее просто для удовольствия: если вы используете С++ 14, вы можете очень кратко инкапсулировать всю идиому (С ++ 11 будет немного сложнее):

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");
});
person Joaquín M López Muñoz    schedule 20.10.2015
comment
Спасибо! Вы спасли мой вечер)) Это именно то, что мне нужно. - person pausag; 20.10.2015