получить и установить значения вложенной структуры, используя внешние функции - PullRequest
0 голосов
/ 03 января 2019

Я столкнулся со следующей проблемой. У меня есть защищенная структура данных a map из <ID , Object>. Существует только одна структура данных карты, и нескольким потокам может потребоваться set и get значения из карты с использованием ID. Object может быть вложенным, как показано в следующем примере кода.

#include <iostream>
#include <map>
#include <algorithm>
#include <mutex>
using namespace std;
struct C {
    int r;
    char s;
};
struct B {
    int p;
    char q;
    C c;
};
struct A {
    int x;
    int y;
    B b;
    char z;
};
/*
    A
    L__ x
    |
    L__ y
    |
    L__ B
        L__p
        |
        L__q
        |
        L__C
            L__r
            |
            L__s

*/
/*  the follwing is the
    data structure to store objects of of type A 
    corresponding to an ID (int)
*/
map<int,A> Map;
mutex m;

/*  Follwing are the desired 
    api's to set and get values from the Map 
    Locks are also required to handle concurrency
*/
void getValueFromMap(int id,/* what to pass ?? */) {
    m.lock();

    /* code to get the value of the specified varible */

    m.unlock();
}
void setValueInMap(int id,/* what to pass ?? */) {
    m.lock();

    /* code to set the new value to the specified variable */

    m.unlock(); 
}
int main() {
    /* lets suppose Map has some A type objects already */
    int id = 4;
    /* assume I want to change the information (value of p) of  id = 4*/
    /* instead of doing the following */
    m.lock();
    Map[id].b.p = 12; /*update to some value */
    m.unlock();
    /* what I need the follwing */
    setValueInMap(4,/* what_to_change , what_is_the_new_value etc .. */);

    /*similarly */

    /*if I want get the value of s */
    int s;
    m.lock();
    getValueFromMap(id,/* what_to_get,placeholder (for example &s) */);
    m.unlock();
}

Я хочу иметь api вызовы (например, setValueInMap с фиксированным числом аргументов) для set и get значений map с использованием вызовов функций, и вся работа мьютекса будет происходить внутри этих api только звонки. Хотя я показал только одну универсальную функцию set для установки всех типов переменных-членов struct A, это не обязательно (больше вызовов API-функций не вызывает проблем).

Как это можно реализовать?

спасибо!

Ответы [ 4 ]

0 голосов
/ 03 января 2019

Один из способов, с помощью которого вы можете легко сгенерировать лямбда-выражения или функции для доступа к вложенным элементам, - написать функцию или скрипт для генерации их на языке сценариев.

# Ruby
def dig_lambda(*members)
  dig = "Map[id].#{members.join '.'}"  
  puts "[&]() -> decltype(#{dig}) {return #{dig};}" 
end

# prints [&]() -> decltype(Map[id].b.c.r) {return Map[id].b.c.r;}
dig_lambda %w[b c r]
0 голосов
/ 03 января 2019

Основной вопрос заключается в том, как вы хотите закодировать то, что вы называете "заполнителем", в систему типов C ++.На выбор:

  • Простые индексы (0 -> A.x, 3 -> B.q и т. Д.) И некоторые жестко запрограммированные операторы переключения (очень хрупкие, не рекомендуется).

  • То же самое, но с индексами вместо перечислений.Не намного лучше, чем выше.

  • Указатели переменных-членов.Это будет очень неудобно, особенно из-за вложенной структуры .

  • Функции.Вы можете написать функции доступа для каждого (вложенного) члена и передать указатели на функции (или функторы, например лямбда-выражения).Примерно так:

    void setBp(A& a, int value) { a.b.p = value; }
    // etc.
    
    template<typename T, typename SetterFunc>
    setValueInMap(int key, SetterFunc setterFunc, T value) {
      m.lock();
      setterFunc(Map[id], value);
      m.unlock();
    }
    
    // Usage
    setValueInMap(4, setBp, 12);
    

Обратите внимание, что ни один из этих подходов не очень красив.Совсем.Вместо этого я бы посмотрел на изменение базового дизайна, именно вложенная структура действительно является корнем этой проблемы.

0 голосов
/ 03 января 2019

Как насчет создания универсальной функции, действующей на карте:

std::map<int, A> Map;
std::mutex m;

template <typename F>
auto ActOnMap(F&& f) -> decltype(f(Map)) {
    std::lock_guard<std::mutex> locker(m);
    return f(Map);
}

(Примечание: вам, вероятно, следует создать класс для структурирования и скрыть прямой доступ к map и mutex.)

А потом:

/* lets suppose Map has some A type objects already */
int id = 4;
/* assume I want to change the information (value of p) of  id = 4*/
ActOnMap([id](std::map<int, A>&m){ m[id].b.p = 12;});

/*similarly */

/*if I want get the value of s */
int s = ActOnMap([id](std::map<int, A>&m){ return m[id].b.c.s;});
0 голосов
/ 03 января 2019

Возможно, вы могли бы использовать подход RapidJson или подход, основанный на принципах «если» (менее элегантный):

(...)

void* getValueFromMap(int id, char structure, string item_name) {
    m.lock();

    if(structure == 'C') {
         if(item_name.compare("r") == 0) {
              return (void*) Map[id].B.C.r;
         } else if(...

    } else if(...

    m.unlock();
}
void setValueInMap(int id, char structure, string item_name, void* item) {
    m.lock();

    if(structure == 'C') {
         if(item_name.compare("r") == 0) {
              Map[id].B.C.r = (int) item;
         } else if(...

    } else if(...


    m.unlock(); 
}

int main() {
    int r;
    r = (int) getValueFromMap(4,'C',"r");

    setValueInMap(5,'C',"r",(void*) r);
}

Отказ от ответственности: Я не компилировал этот код, поэтому он может иметьошибки.

...