【问题标题】:Generating class datamembers from a Vector从 Vector 生成类数据成员
【发布时间】:2019-06-01 16:07:04
【问题描述】:

请考虑这个 C++ 问题:

#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 的数据成员。

o/p 看起来像:

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

虽然这个版本有效,但感觉可以改进很多。尽管这一切都在 Vector 中,但我反复为每个属性执行 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

我希望在这样的案例中存在类似的情况,但是,我在这里学到了宝贵的经验。谢谢!

【问题讨论】:

  • 整个for 循环与if 语句不是适得其反吗? meh 可以更好地命名。
  • if (tag.find("node",0) == 0) 应该改为 if (tag == "node"),对于其他标签检查也是如此。但是,我建议在正在处理的tagindex 上使用switchfor (size_t i = 0; i &lt; g2.size(); ++i) { string tag = g2[i]; ... switch (i) { case 0: this-&gt;node = meh; ... } } 。或者,完全摆脱单个类数据成员并使用单个map&lt;string, string&gt;map&lt;string, variant&gt; 键控在tag,或string[5]variant[5]tag 索引的数组向量
  • 附带说明,当静态 string[5] 就足够时,使用 vector 代替 g2 是多余的
  • @RemyLebeau,谢谢。你为什么说向量与字符串相比是多余的 - 这是部落知识,还是在适当的容器选择上存在常见问题解答?
  • @struggling_learner 您使用的是固定值数组,但 vector 是动态数组。您可以在编译时静态设置固定数组,而不是在运行时动态分配它。这就是为什么我说在这种情况下使用vector 是多余的。此外,在您的更新中,char[10][100] 过度分配,char[5][8] 就足够了。至少,数组的第 1 维需要匹配数组中字符串的数量,所以 10 对于只有 5 个值来说太多了,尤其是在使用基于范围的for 循环时。

标签: c++ datamember


【解决方案1】:

假设您想要保留不同的类成员(而不是一个键值存储),您可以使用一个包含字符串和成员指针的静态表:

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

但这在这里行不通,因为类型不同,而且每种类型的处理也不相同。相反,对于这种几乎并行,语法上的最佳选择通常是 lambda

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;

这种风格的优点是绝对初始化healthprimary。使用私有构造函数进行足够的扭曲后,可以将其放入成员初始化器列表中,这是更好的做法。

【讨论】:

    【解决方案2】:

    我并不完全理解你想在你的例子中使用该间接的什么,但这是如何做到的:

    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&lt;string&gt; 转到char[10][100]? (尤其是当您只有 5 个字符串,并且没有一个字符串占用 100 个字符时?)array&lt;string, 5&gt; 是适合您的情况的方法。

    另外,不要使用 newdelete.. 没有理由进行堆分配,只需使用 ParseURL parse(request)(即使有充分的理由 - 请改用 unique_ptr)。

    最后,如果您不想多次遍历字符串以查找不同的关键字,则可以使用正则表达式。

    【讨论】:

    • 关于间接,我现在了解到 introspection/gettattr (stackoverflow.com/questions/10400874/…) 在 C++ 中并不是那么简单。 for (auto tag: tags){ cout &lt;&lt; "b." &lt;&lt; tag &lt;&lt; "is: " &lt;&lt; b.tag &lt;&lt;endl; // this will not work }
    • @struggling_learner 语言中没有内置(虽然c++20提出了静态自省,但叫反射,这里已经是TS了:open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4746.pdf)在python中,实现了一个类作为名称-> 字段的映射,这正是我针对此类要求提出的解决方案。如果您需要动态类,请使用映射来获取它。它甚至可以通过稍微修改来保存功能。
    【解决方案3】:

    我相信这个问题可以分解:

    • URL解析然后
    • 将结果分配给对象数据成员。

    regex 为将 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 包含)。

    【讨论】:

      猜你喜欢
      • 2012-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2015-05-03
      • 1970-01-01
      • 2012-10-19
      相关资源
      最近更新 更多