Создание элементов данных класса из вектора

Пожалуйста, рассмотрите этот вопрос С++:

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

class ParseURL{
    private:
        int qualify; // 0 means we qualify
    public:
        string node;
        string service;
        bool primary;
        bool health;
        string epoch;
        // methods
        ParseURL(string URL);
        ~ParseURL();
};


ParseURL::ParseURL(string URL){
    vector<string> g2 = {"node", "service", "primary", "health", "epoch"};

    for (string tag : g2){
        auto found = URL.find(tag);
        if ( found != string::npos ){
            auto cut_from = found + 1 + tag.size() ;
            auto meh = URL.substr(cut_from, URL.substr(cut_from).find("&") );
            // is there a way we can avoid these lines below?
            if (tag.find("node",0) == 0){
                this->node = meh;
            } else if (tag.find("service",0) == 0 ){
                this->service = meh;
            } else if (tag.find("epoch",0) == 0) {
                this->epoch = meh;
            } else if (tag.find("health",0) == 0){
                this->health = (meh == "OK");
            } else if (tag.find("primary",0) == 0 ){
                this->primary == (this->node == meh);
            }
        }
    }

}

ParseURL::~ParseURL(){
    cout << "Tearing Down the class\n";
}
int main(){
    char req[] = "GET /register?node=hostrpi3&service=potatoservice&primary=node1&health=OK&epoch=1559345106 HTTP";
    string Request = req;
    Request = Request.substr(Request.find_first_of(" ") );
    Request = Request.substr(0, Request.find(" HTTP"));
    ParseURL *a = new ParseURL(Request);

    cout.width(12);
    cout << "a->node: " << a->node << endl;
    cout.width(12);
    cout << "a->service: " << a->service << endl;
    cout.width(12);
    cout << "a->epoch: " << a->epoch << endl;
    cout.width(12);
    cout << "a->health: " << a->health << endl;
    cout.width(12);
    cout << "a->primary: " << a->primary << endl;

    delete(a);

    return 0;
}

Мне нужно представить объект на основе строк запроса URL, нужные мне теги представлены в векторе g2, и, как вы можете видеть, я создаю элементы данных для ParseURL из этих тегов.

О/п выглядит так:

   a->node: hostrpi3
a->service: potatoservice
  a->epoch: 1559345106
 a->health: 1
a->primary: 0
Tearing Down the class

Хотя эта версия работает, кажется, что ее можно значительно улучшить. Несмотря на то, что все это есть в Векторе, я постоянно делаю tag.find для каждого из этих атрибутов. Должен быть лучший способ, я надеюсь. Не могли бы вы указать в правильном направлении? Спасибо!

Изменить 1

Спасибо, я обновил конструктор, следуя предложениям:

ParseURL::ParseURL(string URL){
    char tags[10][100] = {"node", "service", "primary", "health", "epoch"};

    int i = 0;
    for (auto tag : tags){
        auto found = URL.find(tag);
        if ( found != string::npos ){
            auto cut_from = found + 1 + strlen(tag);
            auto tag_info = URL.substr(cut_from, URL.substr(cut_from).find("&") );
            switch (i){
                case 0:
                    this->node = tag_info; 
                    break;
                case 1:
                    this->service = tag_info; 
                    break;
                case 2:
                    this->primary = (this->node == tag_info); 
                    break;
                case 3:
                    this->health = (tag_info == "OK"); 
                    break;
                case 4:
                    this->epoch = tag_info; 
                    break;
            }
        }
    i++;
    }
}

bash, например, предлагает косвенную ссылку на переменные, то есть одна переменная может содержать имя другой переменной:

$ cat indirect.sh 
#!/bin/bash 

values="value1 value2"
value1="this is value1"
value2="this is value2"

for i in $values; do 
    echo "$i has value ${!i}"
done

$ ./indirect.sh 
value1 has value this is value1
value2 has value this is value2

Я надеялся, что для таких случаев существует что-то подобное, тем не менее, я извлек здесь ценные уроки. Спасибо!


person struggling_learner    schedule 01.06.2019    source источник
comment
Разве весь цикл for с операторами if не контрпродуктивен? meh можно было бы назвать и получше.   -  person LogicStuff    schedule 01.06.2019
comment
Вместо этого if (tag.find("node",0) == 0) должно быть if (tag == "node"), и то же самое для других проверок тегов. Однако я бы предложил использовать switch в индексе обрабатываемого tag: for (size_t i = 0; i < g2.size(); ++i) { string tag = g2[i]; ... switch (i) { case 0: this->node = meh; ... } } . Или просто полностью избавьтесь от отдельных элементов данных класса и используйте один map<string, string> или map<string, variant>, который указан в tag, или массив string[5] или variant[5], который индексируется вектором tag.   -  person Remy Lebeau    schedule 01.06.2019
comment
Кстати, использование vector вместо g2 является излишним, когда достаточно статического string[5].   -  person Remy Lebeau    schedule 01.06.2019
comment
@RemyLebeau, спасибо. Почему вы говорите, что вектор - это излишество, а не строка - это племенное знание или существует часто задаваемые вопросы по соответствующему выбору контейнера?   -  person struggling_learner    schedule 03.06.2019
comment
@struggling_learner, вы используете фиксированный массив значений, но vector — это динамический массив. Вы можете настроить фиксированный массив статически во время компиляции вместо того, чтобы динамически выделять его во время выполнения. Вот почему я говорю, что использование vector в этой ситуации излишне. Кроме того, в вашем обновлении char[10][100] перераспределяется, char[5][8] будет достаточно. По крайней мере, 1-е измерение массива должно соответствовать количеству строк в массиве, поэтому 10 слишком много для всего 5 значений, особенно при использовании цикла for на основе диапазона.   -  person Remy Lebeau    schedule 03.06.2019


Ответы (3)


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

vector<pair<string,string ParseURL::*>> keys={{"node",&ParseURL::node},…};

Но здесь это не работает, потому что типы разные и обработка для каждого не одинакова. Вместо этого для такого рода почти-параллелизма синтаксически оптимальным выбором часто является лямбда:

const auto get=[&URL](const string &tag) {
  auto found = URL.find(tag);
  if(found == string::npos) return "";
  auto cut_from = found + 1 + tag.size() ;
  return URL.substr(cut_from, URL.substr(cut_from).find("&") );
};
node=get("node");
service=get("service");
epoch=get("epoch");
health=get("health")=="OK";
primary=get("primary")==node;

Преимущество этого стиля в том, что он однозначно инициализирует health и primary. При достаточном количестве искажений с закрытым конструктором его можно поместить в список инициализаторов членов, что даже лучше.

person Davis Herring    schedule 02.06.2019

Я не совсем понял, для чего вы хотите использовать эту косвенность в своем примере, но вот как это можно сделать:

vector<string> values{"value1", "value2"}
map<const string, const string> fields_and_values{{"value1", "this is value1"}, {"value2", "this is value2"}};
for (value: values)
     cout << value << " have value " << fields_and_values[value] << endl;

Этот код позволяет динамически добавлять дополнительные поля. Если у вас есть предопределенное количество полей, это излишество, и подойдет простой массив. то есть

array<const string, 2> values{"this is value1", "this is value2"};
for (int i = 0; i < 2; ++i)
    cout << "value" << i << " have value " << values[i] << endl;

Надеюсь это ответит на твой вопрос.

В общем о вашем коде:

Почему вы перешли от vector<string> к char[10][100]? (особенно когда у вас всего 5 строк, и ни одна из них не занимает 100 символов?) array<string, 5> - это то, что нужно для вашего случая.

Кроме того, не используйте new и delete. Нет причин делать выделение кучи, просто используйте ParseURL parse(request) (и даже если была веская причина - вместо этого используйте unique_ptr).

И, наконец, если вы не хотите многократно просматривать строку в поисках разных ключевых слов, вы можете использовать регулярное выражение.

person Mr.WorshipMe    schedule 01.06.2019
comment
Что касается косвенности, я теперь узнал, что интроспекция/gettattr (stackoverflow.com/questions/10400874/) не так просто в C++. for (auto tag: tags){ cout << "b." << tag << "is: " << b.tag <<endl; // this will not work } - person struggling_learner; 02.06.2019
comment
@struggling_learner Это не встроено в язык (хотя для С++ 20 предлагается статическая самоанализ, она называется отражением, и это уже здесь TS: open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4746.pdf) В python класс реализован как карта имя->поле, что является именно тем решением, которое я предложил для такого требования. Если вам нужен динамический класс, используйте карту, чтобы получить его. он может даже выполнять функции с небольшой модификацией. - person Mr.WorshipMe; 02.06.2019

Я считаю, что этот вопрос можно разложить:

  • Анализ URL, а затем
  • присвоение результатов элементам данных объекта.

регулярное выражение дает компактное и надежное решение для извлечения пар значений ключа URL (параметров) в карту:

#include <iostream>
#include <iterator>
#include <map>
#include <string>
#include <regex>

using namespace std;

int main()
{
    map<string, string> keyValues;
    string s = "GET /register?node=hostrpi3&service=potatoservice&primary=node1&health=OK&epoch=1559345106 HTTP";
    regex keyValueRegex("[?&](\\w+)=(\\w+)");
    auto pairsBegin = std::sregex_iterator(s.begin(), s.end(), keyValueRegex);
    auto pairsEnd = std::sregex_iterator();

    for (std::sregex_iterator i = pairsBegin; i != pairsEnd; ++i)
    {
        std::smatch match = *i;
        keyValues[match[1]] = match[2];
    }

    for (auto p : keyValues)
    {
        cout << p.first << " " << p.second << endl;
    }
}

дает вывод:

epoch 1559345106
health OK
node hostrpi3
primary node1
service potatoservice

Другой подход (для очень детального анализа URL-адресов) заключается в использовании библиотеки cpp-netlib (предлагается для включения в Boost).

person Vlad Novakovsky    schedule 02.06.2019