I have a couple of array's:
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"};
which i then need to parse out for '=' and then put the values in the struct. (the rc key maps to the fc key in the struct), which is in the form of:
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;
};
The problem I am encountering is that the array's are not in sequence or in the same sequence as the struct fields. So, I need to check each and then come up with a scheme to put the same into the struct.
Any help in using C or C++ to solve the above?
Indeed, like many answered, there is a need to separate the parsing problem from the object construction problem. The Factory pattern is suited well for that.
The Boost.Spirit library also solves the parse->function problem in a very elegant way (uses EBNF notation).
I always like to separate the 'business logic' from the framework code.
You can achieve this by start writing "what you want to do" in a very convenient way and work to "how do you do it" from there.
And the framework to achieve this:
Were I to do this in straight C, I wouldn't use the mother of all if's. Instead, I would do something like this:
This lets you look up the field by name at runtime with a compact collection of the struct layout. This is fairly easy to maintain, but I don't like the
sizeof(mumble)
in the actual usage code - that requires that all struct definitions get labeled with comments saying, "don't effing change the types or content without changing them in thet_fieldDef
array for this structure". There also needs to beNULL
checking.I'd also prefer that the lookup be either binary search or hash, but this is probably good enough for most cases. If I were to do hash, I'd put a pointer to a
NULL
hashtable into thet_structLayout
and on first search, build the hash.The problem is you dont have the metainformation to refer to the struct elements at run time (Something like structVar.$ElementName = ..., where $ElementName is not the element name but a (char?)variable containing the element name which should be used). My solution would be to add this metainformation. This should be an array with the offset of the elements in the struct.
Quick-n-Dirty solution: you add an array with the strings, the resulting code should look like this:
to enter something you would then something like this:
This loop for the inserts can get costly if you have bigger structures, but C doenst allow array indexing with strings. But the computer science found a solution for this problem: perfect hashes.
So it would afterwards look like this:
But how to obtain these perfect hash functions (I called it calc_perf_hash)? There exist algorithms for it where you just stuff your keywords in, and the functions comes out, and luckily someone even programmed them: look for the "gperf" tool/package in your faviourite OS/distribution. There you would just input the 6 element names and he outputs you the ready to use C code for a perfect hash function (in generates per default a function "hash" which returnes the hash, and an "in_word_set" function which decides if a given key is in the word list). Because the hash is in different order, you have of course to initilize the offsetof and size arrays in the order of the hashes.
Another problem you have (and which the other answers doesnt take into account) is the type conversion. The others make an assignment, I have (not better) memcopy. Here I would suggest you change the sizes array into another array:
Where each string describes the sscanf modifier to read it in. This way you can replace the assignment/copy by
Cf course you can vary here, by including the "element=" into the string or similar. So you can put the complete string into value and dont have to preprocess it, I think this depends strongly on the rest of you parse routine.
I've written some little code that allows you to initialize fields, without having to worry too much about whether your fields are going out of order with the initialization.
Here is how you use it in your own code:
The framework is below
It works using the lesser known data-member-pointers, which you can take from a class using the syntax
&classname::member
.This shouldn't be too hard. Your first problem is that you don't have a fixed sized array, so you'd have to pass the size of the array, or what I'd prefer you make the arrays NULL-terminated, e.g.
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", NULL};
Then I would write a (private) helper function that parse the string:
then you can do what qrdl has suggested.
in a simple for loop:
EDIT: you should probably use long instead of int and atol instead of atoi, because your prl_id is of the type long. Second if there could be wrong formated numbers after the '=', you should use strtol, which can catch errors.
tried your idea and got an
in linux ubuntu eclipse cdt.
I wish to notify that one should include
<map>
in the "*.h" file in order to use your code without this error message.etc' etc'......