2
votes

obtenir et définir les valeurs d'une structure imbriquée à l'aide de fonctions externes

Je suis confronté au problème suivant. J'ai une structure de données protégée une carte de . Il n'y a qu'une seule structure de données de carte et plusieurs threads peuvent souhaiter définir et obtenir les valeurs de la carte à l'aide d'un ID . L ' Object peut être imbriqué comme indiqué dans l'exemple de code suivant.

#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();
}

Je souhaite avoir des appels api (par exemple setValueInMap avec un nombre fixe d'arguments) pour set et obtenir les valeurs de la map en utilisant des appels de fonction et tout le travail mutex sera se produisent uniquement dans ces appels api . Bien que je n'ai montré qu'une seule fonction d'ensemble générique pour définir tous les types de variables membres de struct A , cela n'a pas besoin de l'être (plus d'appels de fonction api ne posent aucun problème).

Comment cela peut-il être mis en œuvre?

Merci !


1 commentaires

Qu'en est-il de la carte existante sans verrou thread-safe, elle est généralement basée sur la structure de données de la liste à sauter. libcdc ont une très bonne implémentation.


4 Réponses :


1
votes

Peut-être que vous pourriez opter pour une approche RapidJson ou une approche si basée (moins élégante):

(...)

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

Clause de non-responsabilité: Je n'ai pas compilé ce code, donc il peut contenir des erreurs.


4 commentaires

Cela ne règle pas le problème. La question est de définir uniquement des membres spécifiques de chaque élément.


Merci! mais pendant que définissant les valeurs, je pourrais ne pas avoir l'objet complet. De même, je ne souhaite peut-être pas copier la structure entière dans une copie locale à l'extérieur. Parce qu'avoir une copie locale de A dans un thread n'est pas une bonne idée pour la concurrence IMHO.


Désolé, je n'ai pas tout à fait compris au début. J'ai édité la réponse et j'espère qu'elle y répondra mieux maintenant.


Bien entendu, cette approche peut prendre beaucoup de temps pour des structures très grandes et imbriquées.



3
votes

La question principale est de savoir comment vous voulez encoder ce que vous appelez «l'espace réservé» dans le système de type C ++. Au choix:

  • Indices simples ( 0 -> Ax , 3 -> Bq , etc.) et une instruction de commutation codée en dur (très fragile, non recommandée).

  • / p>

  • La même chose, mais avec une énumération au lieu d'indices. Pas beaucoup mieux que ci-dessus.

  • Pointeurs de variable de membre. Cela deviendra très gênant, en particulier en raison de la structure imbriquée . P >

  • Fonctions. Vous pouvez écrire des fonctions d'accès pour chaque membre (imbriqué) et leur transmettre des pointeurs de fonction (ou des foncteurs, par exemple lambdas). Quelque chose comme ça:

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

Notez qu'aucune de ces approches n'est très jolie. Du tout. Je voudrais plutôt changer la conception sous-jacente, la structure imbriquée est vraiment la racine de ce problème.


6 commentaires

Oui merci. J'ai également pensé à avoir une table de pointeur de fonction pointant vers les fonctions getter et setter pour chaque variable membre. Je ne savais pas trop comment faire.


La structure imbriquée est vraiment à l'origine de ce problème. Pourquoi pourrait-elle être une meilleure option?


Éventuellement avec des pointeurs de variable membre et un modèle variadique: setValueInMap (42, 12, & A :: b, & B :: p)


@Debashish: quelque chose comme qui


@Max Langhof pourquoi la méthode du boîtier de commutation est "très fragile, déconseillée"?


@Debashish Si des membres sont ajoutés, supprimés, réorganisés ou modifiés de toute autre manière, vous devez mettre à jour chaque instruction switch avec soin. Si vous faites une erreur avec les indices, le compilateur n'a aucune chance de vous avertir.



5
votes

Que diriez-vous de créer une fonction générique pour agir sur la carte:

/* 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;});

(Remarque: vous devriez probablement créer une classe pour structurer cela et masquer l'accès direct à carte et mutex.)

Et puis:

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


0 commentaires

1
votes

Une façon de générer facilement des lambdas ou des fonctions pour accéder aux membres imbriqués consiste à écrire une fonction ou un script pour les générer dans un langage de script.

# 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 commentaires