Карта списка со структурами (содержащая список) — не может получить доступ к списку внутри структуры через карту

Я запустил поисковые системы, но не могу найти соответствующий ответ на мою проблему:

В основном я хочу иметь карту, каждая запись которой содержит список структур. Сама структура содержит 2 переменные std::string и переменную std::list<string>.

Все работает как положено, несмотря на доступ к списку внутри структуры.

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

В методе getRuleParams список с добавленным элементом корректно доступен напрямую через элемент структуры. В методе "sourrounding" список с добавленным элементом также корректно доступен напрямую через элемент структуры.

НО, если я хочу получить доступ к списку структур или, лучше, к элементам списка, тогда происходят странные вещи. Я понял, что адреса изменились (структура, список внутри структуры....)

Вы можете видеть, что выдает мой код (при условии, что он закодирован правильно).

Поскольку я новичок в C++, возможно, мне не хватает чего-то очень простого/важного. Связано ли это с некоторыми конструкторами копирования, которые вызываются, но почему это то же самое поведение только с использованием указателей на все «переменные»?

Я очень надеюсь, что кто-то здесь может просветить меня.

Я максимально упростил/изолировал свою проблему. (Подписи методов должны быть такими, например, void*, потому что я полагаюсь на другие функции)

Пожалуйста, смотрите код ниже и спасибо заранее:

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

person Johannes    schedule 10.01.2013    source источник
comment
Вы намеренно не используете итераторы и ссылки, где это уместно, или это просто потому, что вы не знакомы/не знакомы с ними?   -  person WhozCraig    schedule 10.01.2013
comment
Разве я не использую итератор через rule.params.begin(), указывающий на первый элемент?   -  person Johannes    schedule 10.01.2013
comment
Почему вы передаете почти все как пустые указатели?   -  person Platinum Azure    schedule 10.01.2013
comment
Определенно. Это итераторы, возвращаемые такими вещами, как все вызовы метода find(), о которых я говорю.   -  person WhozCraig    schedule 10.01.2013
comment
Я передаю все вокруг как пустые указатели, потому что я вынужден это делать. (по библиотеке)   -  person Johannes    schedule 10.01.2013


Ответы (1)


Это серьезная проблема сама по себе:

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

Хотя я не обязательно рекомендую эту модель, вы, вероятно, можете решить эту проблему следующим образом:

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

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

Но чтобы сделать это правильно, вы должны поместить пустое правило в список, а затем сослаться на этот объект правила (тот, что в списке) для инициализации:

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

Честно говоря, над самой парадигмой нужно немного поработать, здесь происходит много ненужного find()ing, и методичное использование ссылок и итераторов могло бы это немного почистить.


Пример переделки:

Следующее разделило все выходные предложения. На самом деле они труднее видеть проблемы. Пожалуйста, просмотрите следующее, чтобы, мы надеемся, дать вам некоторые идеи:

#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();
}
person WhozCraig    schedule 10.01.2013
comment
Я только что попробовал, и это работает, спасибо. Не могли бы вы объяснить мне, почему это имеет значение? Я не совсем понимаю, для меня это кажется равным, что, очевидно, не так. - person Johannes; 10.01.2013
comment
@Johannes Взгляните на обновленный пост с примером того, как это сделать со ссылками и итераторами. Причина, по которой ваш список правил был пуст, заключалась в том, что вы добавили в список новое правило по значению, а затем продолжили загрузку параметров только в локальную копию. Изменение, которое я показал вам, и только что опубликованный пример переделки, учитывают исправление этого. - person WhozCraig; 10.01.2013
comment
спасибо за ваши ответы и пояснения. Просто чтобы проверить, правильно ли я это понял: rules.push_back(rule); означает нажатие Rule по значению, а не по ссылке. Наоборот: если бы структура содержала ссылку на ParamList, то нажатие Rule произошло бы снова по значению, но с правильной ссылкой на ParamList. Ваш подход очень прямолинеен, чтобы я знал. Спасибо тебе за это! (извините за двойной комментарий -› истек срок редактирования) - person Johannes; 10.01.2013
comment
не совсем уверен, что следил за этим, но я думаю, что вы почти у цели. Правило создается локально, помещается в конец списка правил, затем мы инициализируем это правило, ссылаясь на него, используя ruleList.back() Поскольку ваша функция требует void *, нам нужно взять адрес этой ссылки, но это нормально; адрес ссылки является синонимом получения адреса базового ссылочного объекта. Оказавшись там, мы изменяем ParamList напрямую для данного объекта Rule, который в настоящее время находится в конце списка правил. Я надеюсь, что в этом есть смысл. Изучите немного больше ссылок. - person WhozCraig; 10.01.2013
comment
Хорошо, именно то, что я интерпретировал. Большое спасибо @WhozCraig, это мне очень помогло! Я подозревал, что это так, но сам не мог понять. Можете ли вы порекомендовать какую-либо онлайн/оффлайн литературу о рефах? - person Johannes; 10.01.2013
comment
честно говоря, я не могу делать какие-либо записи для книг, так как их так много, и так много настолько бесполезных, что я просто давно решил учиться, читая стандарты и множество статей в Интернете. У Херба Саттера есть несколько очень популярных книг, вы можете попробовать некоторые из его. Очень рад, что это имеет смысл, как мы обращаемся к вашему коду. Рад помочь. - person WhozCraig; 10.01.2013