Map of list with structs (containing a list) — cannot access list within struct via map

StackOverflow https://stackoverflow.com/questions/14250528

  •  14-01-2022
  •  | 
  •  

Вопрос

I fired up the search engines but cannot find a corresponding answer to my problem:

Basically I want to have a map, each entry containing a list of structs. A struct itself contains 2 std::string variables and a std::list<string>.

Everything works as expected, despite accessing the list within the struct.

One method (here: getRules) creates a map entry if necessary and attach a list to it and add one struct (here: Rule) element to it. Within this method another method (here: getRuleParams) is called which should take care of adding elements to the list within the struct.

Within the getRuleParams method the list with added element is directly accessible through the struct element correctly. Within the "sourrounding" method the list with added element is directly accessible through the struct element correctly, as well.

BUT, if I want to access the struct's list, or better it's list elements, then weird stuff happens. I figured out, that the addresses changed (struct, list within struct....)

You can see that in the output my code produces (assuming it's coded correctly).

Since I am new to C++, maybe I am missing something very easy/essential. Does it have to do with some copy constructors which get called, but why is it the same behaviour with only using pointers to all "variables"?

I really hope someone here can enlighten me.

I simplified/isolated my problem as much as I could. (The signatures of the methods have to be that way e.g. void*, because I rely on other functions)

Please see code below and thanks in advance:

#include <list>
#include <map>
#include <string>
#include <iostream>

using namespace std;

struct Rule {
    string target;
    string action;
    list<string> params;
};

static int getRuleParams(void *prule) {
    Rule* rule = (Rule*) (prule);
    string param = "Entry!";

    //Fill struct with ruleparams
    rule->params.push_back(param);
    cout << "Method getRuleParams()\n" << "parameter: "
    << *(rule->params.begin()) << " \nadress of rule:\t\t\t" << rule
    << " \nadress of rule.params:\t\t" << &(rule->params) << std::endl
    << std::endl;
    return 0;
}

static int getRules(void *dbpolicies) {
    string policy = "FileIO";
    string target = "TARGET";
    string action = "DENY";

    std::map<std::string, std::list<Rule> >* policies = (std::map<std::string,
                                                         std::list<Rule> >*) (dbpolicies);

    //Create std::list<DBRule> (list) for Policy Map-Entry, if not existing
    if ((*policies).find(policy) == (*policies).end())
        (*policies)[policy] = std::list<Rule>();

    //Fill struct
    Rule rule = { target, action };
    (*policies).find(policy)->second.push_back(rule);

    //call Method which manipulates params in struct
    getRuleParams(&rule);

    cout << "Method getRulesforPackage() (access rule directly):";
    cout << "\nparameter = " << *(rule.params.begin())
    << "\naddress of rule:\t\t" << &rule;
    cout << "\nadress of rule.params:\t\t" << &(rule.params) << std::endl
    << std::endl;

    cout << "Method getRulesforPackage() access rule via map:" << std::endl;

    //READ params from map entry -> EVIL
    std::list<Rule> dbrules = (*policies).find(policy)->second;
    Rule firstrule = *dbrules.begin();
    std::list<std::string> dbruleparams = firstrule.params;
    string ruleparam = *(firstrule.params.begin()); //EVIL!
    //  string ruleparam = *(dbruleparams.begin()); // <- REALLY EVIL! (program crashes)

    //Variant 2: pointers only
    std::list<Rule>* dbrules2 = &(*policies).find(policy)->second;
    Rule* firstrule2 = &*(dbrules2->begin());
    std::list<std::string>* dbruleparams2 = &(firstrule2->params);


    cout << "rule.target = " << firstrule.target << "\nrule.action = "
    << firstrule.action << std::endl;
    cout << "address of rule:\t\t" << &firstrule << std::endl;
    cout << "address of rule (pointer):\t" << firstrule2 << std::endl;

    //  string ruleparam2 = *(dbruleparams2->begin()); //REALLY EVIL! (program crashes)

    //  cout << "parameter: " << ruleparam << std::endl;
    //  cout << "parameter (pointer): " << ruleparam2 << std::endl;


    return 0;
}

static std::map<std::string, std::list<Rule> > getPolicies() {
    std::map<std::string, std::list<Rule> > policies;
    getRules(&policies);
    return policies;

}

int main() {
    std::map<std::string, std::list<Rule> > policies = getPolicies();
}
Это было полезно?

Решение

This is a significant issue all by itself:

Rule rule = { target, action };
(*policies).find(policy)->second.push_back(rule);

//call Method which manipulates params in struct
// **NOTE**: Fills the local variable "rule", not the one just pushed into the list
getRuleParams(&rule); 

Though I don't necessarily advise this model, you can probably address this by:

Rule rule = { target, action };
getRuleParams(&rule); // fill here, before the push

// then push.
(*policies).find(policy)->second.push_back(rule);

But to do it properly, you should push a blank rule into the list, then reference that rule object (the one in the list) for initialization:

Rule rule = { target, action };
(*policies)[policy].push_back(rule);
getRuleParams(&(*policies)[policy].back());

To be honest, the paradigm itself needs a bit of work, there is a lot of unnecessary find()ing going on in here, and methodical use of references and iterators could clean this up quite a bit.


Rework Example:

The following has stripped all the output clauses. They were actually making it harder to see problems. Please review the following to hopefully give you some ideas:

#include <list>
#include <map>
#include <string>
#include <iostream>

using namespace std;

// basic rule with strin parameter list
typedef std::list<std::string> ParamList;
struct Rule
{
    string target;
    string action;
    ParamList params;
};

typedef std::list<Rule> RuleList;
typedef std::map<std::string, RuleList> MapStringToRuleList;

static int getRuleParams(void *prule)
{
    Rule* rule = (Rule*) (prule);
    rule->params.push_back("Entry!");
    return 0;
}

static int getRules(void *dbpolicies) {

    string policy = "FileIO";
    string target = "TARGET";
    string action = "DENY";

    MapStringToRuleList* policies = (MapStringToRuleList*)(dbpolicies);

    // push a new rule into the list.
    Rule rule = {target, action};
    RuleList& rules = (*policies)[ policy ];
    rules.push_back(rule);

    //call Method which manipulates params in struct
    getRuleParams(&rules.back());

    // for each rule in our policy's rule list...
    for (RuleList::const_iterator rit = rules.begin(); rit != rules.end(); rit++)
    {
        cout << "Rule: " << policy << endl;

        // for each rule in our curent rule's params list
        const ParamList& params = rit->params;
        for (ParamList::const_iterator pit = params.begin(); pit != params.end(); pit++)
        {
            cout << "  Rule Param: " << *pit << endl;
            // TODO: use current param for something.
        }            
    }
    return 0;
}

static MapStringToRuleList getPolicies()
{
    MapStringToRuleList policies;
    getRules(&policies);
    return policies;
}

int main()
{
    MapStringToRuleList policies = getPolicies();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top