[英]Initializing struct, using an array
我有幾個數組:
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鍵),其形式為:
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 ++來解決上述問題?
可能我沒有正確理解,但顯而易見的解決方案是將每個數組元素拆分為key
和value
,然后寫入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
后綴
這不應該太難。 你的第一個問題是你沒有固定大小的數組,所以你必須傳遞數組的大小,或者我更喜歡你使數組以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,它可以捕獲錯誤。
我寫了一些允許你初始化字段的小代碼,而不必過多擔心你的字段是否在初始化時出現故障。
以下是您在自己的代碼中使用它的方法:
/* clients using the above classes derive from lookable_fields */
struct predict_cache_key : private lookable_fields<predict_cache_key> {
predict_cache_key(std::vector<std::string> const& vec) {
for(std::vector<std::string>::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<std::string>(a, a + 4));
}
框架如下
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
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<CLASS>; \
private: \
static void init_fields_() { \
typedef CLASS parent_class;
#define FIELD(X) \
lookable_fields<parent_class>::entry_map[#X].set_ptr(&parent_class::X)
#define END_FIELDS() \
}
template<typename Derived>
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<Derived*>(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<Derived> entry_t;
typedef std::map<std::string, entry_t> 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<typename T>
std::map< std::string, entry<T> > lookable_fields<T>::entry_map;
template<typename T>
typename lookable_fields<T>::init_helper lookable_fields<T>::initializer;
它使用鮮為人知的數據成員指針,您可以使用語法&classname::member
從類中獲取它。
實際上,正如許多人所回答的那樣,需要將解析問題與對象構造問題分開。 工廠模式非常適合。
Boost.Spirit庫還以非常優雅的方式解決了parse-> function問題(使用EBNF表示法)。
我總是喜歡將“業務邏輯”與框架代碼分開。
您可以通過以非常方便的方式開始編寫“您想做什么”並從那里開始“如何做”來實現這一目標。
const CMemberSetter<predict_cache_key>* setters[] =
#define SETTER( tag, type, member ) new TSetter<predict_cache_key,type>( #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<predict_cache_key> 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<BaseClass> {
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<const CMemberSetter<BaseClass>*> aSetters;
template< size_t N >
PCKFactory( const CMemberSetter<BaseClass>* (&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 CMemberSetter<BaseClass>*>::const_iterator itSetter = aSetters.begin();
while( itSetter != aSetters.end() ) { // optimalization possible
if( (*itSetter)->try_set_value( pck, *pair ) )
break;
++itSetter;
}
}
return pck;
}
};
嘗試了你的想法,得到了一個
error: ISO C++ forbids declaration of ‘map’ with no type
在linux中ubuntu eclipse cdt。
我想通知一個應該在“* .h”文件中包含<map>
,以便在沒有此錯誤消息的情況下使用您的代碼。
#include <map>
// a framework
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
entry(R (T::*ptr)) {
等'等'......
問題是你沒有在運行時引用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)? 有一些算法可以讓你只填充你的關鍵字,並且功能出來了,幸運的是有人甚至對它們進行了編程:在你最喜歡的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
並在第一次搜索時構建哈希。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.