使用数组初始化struct

我有几个数组:

const string a_strs[] = {"cr=1", "ag=2", "gnd=U", "prl=12", "av=123", "sz=345", "rc=6", "pc=12345"}; const string b_strs[] = {"cr=2", "sz=345", "ag=10", "gnd=M", "prl=11", "rc=6", "cp=34", "cv=54", "av=654", "ct=77", "pc=12345"}; 

然后我需要解析’=’然后将值放在结构中。 (rc键映射到结构中的fc键),其forms为:

 struct predict_cache_key { pck() : av_id(0), sz_id(0), cr_id(0), cp_id(0), cv_id(0), ct_id(0), fc(0), gnd(0), ag(0), pc(0), prl_id(0) { } int av_id; int sz_id; int cr_id; int cp_id; int cv_id; int ct_id; int fc; char gnd; int ag; int pc; long prl_id; }; 

我遇到的问题是数组不是与struct字段顺序或顺序相同。 所以,我需要检查每个,然后想出一个方案,将相同的结构放入结构中。

有什么帮助使用C或C ++来解决上述问题?

这不应该太难。 你的第一个问题是你没有固定大小的数组,所以你必须传递数组的大小,或者我更喜欢你使数组以NULL结尾,例如

const string a_strs[] = {"cr=1", "ag=2", "gnd=U", NULL};

然后我会编写一个解析字符串的(私有)辅助函数:

 bool parse_string(const string &str, char *buffer, size_t b_size, int *num) { char *ptr; strncpy(buffer, str.c_str(), b_size); buffer[b_size - 1] = 0; /* find the '=' */ ptr = strchr(buffer, '='); if (!ptr) return false; *ptr = '\0'; ptr++; *num = atoi(ptr); return true; } 

然后你可以做qrdl建议的。

在一个简单的for循环中:

 for (const string *cur_str = array; *cur_str; cur_str++) { char key[128]; int value = 0; if (!parse_string(*cur_string, key, sizeof(key), &value) continue; /* and here what qrdl suggested */ if (!strcmp(key, "cr")) cr_id = value; else if ... } 

编辑:你应该使用long而不是int和atol而不是atoi,因为你的prl_id属于long类型。 第二,如果在’=’之后可能存在错误的格式化数字,则应该使用strtol,它可以捕获错误。

可能我没有正确理解,但显而易见的解决方案是将每个数组元素拆分为keyvalue ,然后写入lo-o-ong if-else-if-else ...序列如

 if (!strcmp(key, "cr")) my_struct.cr = value; else if (!strcmp(key, "ag")) my_struct.ag = value; ... 

您可以在C预处理器的帮助下自动创建此类序列,例如

#define PROC_KEY_VALUE_PAIR(A) else if (!strcmp(key,#A)) my_struct.##A = value

因为领导else你用这种方式编写代码:

 if (0); PROC_KEY_VALUE_PAIR(cr); PROC_KEY_VALUE_PAIR(ag); ... 

你们中的一些结构字段唯一的问题是_id后缀 – 对于它们你需要创建一个不同的宏来粘贴_id后缀

我编写了一些允许您初始化字段的小代码,而不必过多担心您的字段是否因初始化而出现故障。

以下是您在自己的代码中使用它的方法:

 /* clients using the above classes derive from lookable_fields */ struct predict_cache_key : private lookable_fields { predict_cache_key(std::vector const& vec) { for(std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { std::size_t i = it->find('='); set_member(it->substr(0, i), it->substr(i + 1)); } } long get_prl() const { return prl_id; } private: /* ... and define the members that can be looked up. i've only * implemented int, char and long for this answer. */ BEGIN_FIELDS(predict_cache_key) FIELD(av_id); FIELD(sz_id); FIELD(gnd); FIELD(prl_id); END_FIELDS() int av_id; int sz_id; char gnd; long prl_id; /* ... */ }; int main() { std::string const a[] = { "av_id=10", "sz_id=10", "gnd=c", "prl_id=1192" }; predict_cache_key haha(std::vector(a, a + 4)); } 

框架如下

 template struct entry { enum type { tchar, tint, tlong } type_name; /* default ctor, so we can std::map it */ entry() { } template entry(R (T::*ptr)) { set_ptr(ptr); } void set_ptr(char (T::*ptr)) { type_name = tchar; charp = ptr; }; void set_ptr(int (T::*ptr)) { type_name = tint; intp = ptr; }; void set_ptr(long (T::*ptr)) { type_name = tlong; longp = ptr; }; union { char (T::*charp); int (T::*intp); long (T::*longp); }; }; #define BEGIN_FIELDS(CLASS) \ friend struct lookable_fields; \ private: \ static void init_fields_() { \ typedef CLASS parent_class; #define FIELD(X) \ lookable_fields::entry_map[#X].set_ptr(&parent_class::X) #define END_FIELDS() \ } template struct lookable_fields { protected: lookable_fields() { (void) &initializer; /* instantiate the object */ } void set_member(std::string const& member, std::string const& value) { typename entry_map_t::iterator it = entry_map.find(member); if(it == entry_map.end()) { std::ostringstream os; os << "member '" << member << "' not found"; throw std::invalid_argument(os.str()); } Derived * derived = static_cast(this); std::istringstream ss(value); switch(it->second.type_name) { case entry_t::tchar: { /* convert to char */ ss >> (derived->*it->second.charp); break; } case entry_t::tint: { /* convert to int */ ss >> (derived->*it->second.intp); break; } case entry_t::tlong: { /* convert to long */ ss >> (derived->*it->second.longp); break; } } } typedef entry entry_t; typedef std::map entry_map_t; static entry_map_t entry_map; private: struct init_helper { init_helper() { Derived::init_fields_(); } }; /* will call the derived class's static init function */ static init_helper initializer; }; template std::map< std::string, entry > lookable_fields::entry_map; template typename lookable_fields::init_helper lookable_fields::initializer; 

它使用鲜为人知的数据成员指针,您可以使用语法&classname::member从类中获取它。

实际上,正如许多人所回答的那样,需要将解析问题与对象构造问题分开。 工厂模式非常适合。

Boost.Spirit库还以非常优雅的方式解决了parse-> function问题(使用EBNF表示法)。

我总是喜欢将“业务逻辑”与框架代码分开。

您可以通过以非常方便的方式开始编写“您想做什么”并从那里开始“如何做”来实现这一目标。

  const CMemberSetter* setters[] = #define SETTER( tag, type, member ) new TSetter( #tag, &predict_cache_key::##member ) { SETTER( "av", int, av_id ) , SETTER( "sz", int, sz_id ) , SETTER( "cr", int, cr_id ) , SETTER( "cp", int, cp_id ) , SETTER( "cv", int, cv_id ) , SETTER( "ct", int, ct_id ) , SETTER( "fc", int, fc ) , SETTER( "gnd", char, gnd ) , SETTER( "ag", int, ag ) , SETTER( "pc", int, pc ) , SETTER( "prl", long, prl_id ) }; PCKFactory factory ( setters ); predict_cache_key a = factory.factor( a_strs ); predict_cache_key b = factory.factor( b_strs ); 

以及实现这一目标的框架:

  // conversion from key=value pair to "set the value of a member" // this class merely recognises a key and extracts the value part of the key=value string // template< typename BaseClass > struct CMemberSetter { const std::string key; CMemberSetter( const string& aKey ): key( aKey ){} bool try_set_value( BaseClass& p, const string& key_value ) const { if( key_value.find( key ) == 0 ) { size_t value_pos = key_value.find( "=" ) + 1; action( p, key_value.substr( value_pos ) ); return true; } else return false; } virtual void action( BaseClass& p, const string& value ) const = 0; }; // implementation of the action method // template< typename BaseClass, typename T > struct TSetter : public CMemberSetter { typedef T BaseClass::*TMember; TMember member; TSetter( const string& aKey, const TMember t ): CMemberSetter( aKey ), member(t){} virtual void action( BaseClass& p, const std::string& valuestring ) const { // get value T value (); stringstream ( valuestring ) >> value; (p.*member) = value; } }; template< typename BaseClass > struct PCKFactory { std::vector*> aSetters; template< size_t N > PCKFactory( const CMemberSetter* (&setters)[N] ) : aSetters( setters, setters+N ) {} template< size_t N > BaseClass factor( const string (&key_value_pairs) [N] ) const { BaseClass pck; // process each key=value pair for( const string* pair = key_value_pairs; pair != key_value_pairs + _countof( key_value_pairs); ++pair ) { std::vector*>::const_iterator itSetter = aSetters.begin(); while( itSetter != aSetters.end() ) { // optimalization possible if( (*itSetter)->try_set_value( pck, *pair ) ) break; ++itSetter; } } return pck; } }; 

问题是你没有在运行时引用struct元素的元信息(类似于structVar。$ ElementName = …,其中$ ElementName不是元素名称而是包含元素名称的(char?)变量使用)。 我的解决方案是添加这个元信息。 这应该是一个数组,其中包含结构中元素的偏移量。

Quick-n-Dirty解决方案:您添加一个包含字符串的数组,结果代码应如下所示:

 const char * wordlist[] = {"pc","gnd","ag","prl_id","fc"}; const int offsets[] = { offsetof(mystruct, pc), offsetof(mystruct, gnd), offsetof(mystruct, ag), offsetof(mystruct, prl_id), offsetof(mystruct, fc)}; const int sizes[] = { sizeof(mystruct.pc), sizeof(mystruct.gnd), sizeof(mystruct.ag), sizeof(mystruct.prl_id), sizeof(mystruct.fc)} 

输入一些你会喜欢的东西:

 index = 0; while (strcmp(wordlist[index], key) && index < 5) index++; if (index <5) memcpy(&mystructvar + offsets[index], &value, sizes[index]); else fprintf(stderr, "Key not valid\n"); 

如果你有更大的结构,插入的循环可能会变得昂贵,但C doenst允许使用字符串进行数组索引。 但计算机科学找到了解决这个问题的方法:完美的哈希。

所以事后看起来像这样:

 hash=calc_perf_hash(key); memcpy(&mystruct + offsets[hash], &value, sizes[hash]); 

但是如何获得这些完美的哈希函数(我称之为calc_perf_hash)? 有一些算法,你只需填写你的关键字,function就出来了,幸运的是有人甚至编程了它们:在你最喜欢的OS /发行版中寻找“gperf”工具/包。 在那里你只需要输入6个元素名称,然后输出你准备使用C代码的完美哈希函数(在默认情况下生成一个函数“hash”返回哈希值)和“in_word_set”函数决定是否给定键在单词列表中)。 因为散列的顺序不同,所以您当然要按散列的顺序初始化offsetof和size数组。

您遇到的另一个问题(以及其他答案未考虑的问题)是类型转换。 其他人做了一个任务,我有(不是更好)记忆。 在这里,我建议您将sizes数组更改为另一个数组:

 const char * modifier[]={"%i","%c", ... 

每个字符串都描述了sscanf修饰符以将其读入。这样您就可以替换赋值/复制

 sscanf(valueString, modifier[hash], &mystructVar + offsets(hash)); 

Cf当然,你可以在这里改变,通过将“element =”包含在字符串或类似字符串中。 因此,您可以将完整的字符串放入值中,而不必预处理它,我认为这很大程度上取决于您解析常规的其余部分。

如果我在直接C中这样做,我不会使用所有的母亲。 相反,我会做这样的事情:

 typedef struct { const char *fieldName; int structOffset; int fieldSize; } t_fieldDef; typedef struct { int fieldCount; t_fieldDef *defs; } t_structLayout; t_memberDef *GetFieldDefByName(const char *name, t_structLayout *layout) { t_fieldDef *defs = layout->defs; int count = layout->fieldCount; for (int i=0; i < count; i++) { if (strcmp(name, defs->fieldName) == 0) return defs; defs++; } return NULL; } /* meta-circular usage */ static t_fieldDef metaFieldDefs[] = { { "fieldName", offsetof(t_fieldDef, fieldName), sizeof(const char *) }, { "structOffset", offsetof(t_fieldDef, structOffset), sizeof(int) }, { "fieldSize", offsetof(t_fieldDef, fieldSize), sizeof(int) } }; static t_structLayout metaFieldDefLayout = { sizeof(metaFieldDefs) / sizeof(t_fieldDef), metaFieldDefs }; 

这使您可以在运行时使用结构布局的紧凑集合按名称查找字段。 这很容易维护,但我不喜欢实际使用代码中的sizeof(mumble) – 这要求所有结构定义都标有注释,“不要改变类型或内容而不改变它们这个结构的t_fieldDef数组“。 还需要进行NULL检查。

我也更喜欢查找是二进制搜索还是哈希,但对于大多数情况来说这可能已经足够了。 如果我要做哈希,我会将指向NULL哈希表的指针放入t_structLayout并在第一次搜索时构建哈希。

尝试了你的想法,得到了一个

 error: ISO C++ forbids declaration of 'map' with no type 

在linux ubuntu eclipse cdt。

我希望通知在“* .h”文件中应该包含

,以便在没有此错误消息的情况下使用您的代码。

 #include  // a framework template struct entry { enum type { tchar, tint, tlong } type_name; /* default ctor, so we can std::map it */ entry() { } template entry(R (T::*ptr)) { 

等’等’……