My problem is that atoi is converting a string's hexadecimal memory address to decimal, instead of what's contained within the string. It is doing this during a macro. Why is it interpreting struct->member as a pointer, when the macro definition makes it an int? Pseudocode below:
if (struct.member == int)? struct.member = atoi(data) : struct.member = data;
The aim of this section of the program is to retrieve data from a .csv file containing information about a structs attributes. I can take in an "id" and store each cells string into an array of strings (csvRowSplit).
However, I then want to transfer the contents of the array to a structure that contains different data types (a method that I want to use to retrieve players saved attributes, attack methods, shop items etc.). It's easy to hard code:
opponent->att = atoi(csvSplitRow[0]);
opponent->= atoi(csvSplitRow[1]);
opponent->hitpoints = atoi(csvSplitRow[2]);
opponent->description = csvSplitRow[3]);
However this clutters with more struct members and is not very flexible or reproducible.
I have defined a macro to cycle through the elements of a structure, and pair up the csvSplitRow[] to the variables, converting if required with atoi.
#define X_FIELDS \
X(char*, description, "%s") \
X(int, xpreward, "%d") \
X(int, att, "%d") \
/* ... */
X(int, crit, "%d")
typedef struct
{
#define X(type, name, format) type name;
X_FIELDS
#undef
}
void update_opp(char** csvSplitRow, opp* opponent)
{
int i = 0;
#define X(type, name, format) \
if (strcmp(format, "%d") == 0) \ // if an int, convert it
opponent->name = atoi(csvSplitRow[i]); \
else \ // otherwise put it straight in
opponent->name = csvSplitRow[i]; \
i++;
X_FIELDS
#undef X
}
The assignment directly to the string members work (ie. no conversion), but atoi results in a conversion of the hexadecimal memory address to an integer, instead of the string it's pointing to.
// before conversion
csvRowSplit[1] == 0x501150 "20"
// practice
atoi(csvRowSplit[1]) == 20
// after conversion and storing in struct int member
opponent->xpreward = atoi(csvSplitRow[1]);
opponent->xpreward == 5247312 // the decimal equivalent of 0x501150
I don't know what I can do now, other than hard code each time I want to match up a parsed line of csv with a structure. Please help!
EDIT: I get a compile time error with -Werror:
error: assignment makes integer from pointer without a cast [-Werror]
Error is within the macro of the update_opp function. However, I know it's not a pointer because I defined it to be an int earlier? So why isn't it recognizing that? and I can't cast it, so what can I do?
Your problem is in this code:
#define X(type, name, format) \
if (strcmp(format, "%d") == 0) \ // if an int, convert it
opponent->name = atoi(csvSplitRow[i]); \
else \ // otherwise put it straight in
opponent->name = csvSplitRow[i]; \
i++;
For any given attribute name (att
say), you get:
if (strcmp("%d", "%d") == 0)
opponent->att = atoi(csvSplitRow[i]);
else
opponent->att = csvSplitRow[i];
i++;
Except that it is all on one line. But, the key point is that you either assign an int
(from atoi()
) to a string or you assign a string to an int
in every invocation, and one of those two is wrong. The code has to be correct before it is optimized.
How to fix? That's tricky. I think I'd probably use something like this:
#include <stdlib.h>
#define CVT_INT(str) atoi(str)
#define CVT_STR(str) str
#define X_FIELDS \
X(char*, description, "%s", CVT_STR) \
X(int, xpreward, "%d", CVT_INT) \
X(int, att, "%d", CVT_INT) \
/* ... */ \
X(int, crit, "%d", CVT_INT)
typedef struct opp
{
#define X(type, name, format, converter) type name;
X_FIELDS
#undef X
} opp;
extern void update_opp(char** csvSplitRow, opp* opponent);
void update_opp(char** csvSplitRow, opp* opponent)
{
int i = 0;
#define X(type, name, format, converter) \
opponent->name = converter(csvSplitRow[i++]);
X_FIELDS
#undef X
}
This compiles without warnings under very stringent compiler flags:
gcc -pedantic -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition -c xm.c
The CVT_INT
and CVT_STR
macros could be redefined when required to do other things.
An alternative version of the code exploits CVT_INT
and CVT_STR
(renamed to X_INT
and X_STR
) more extensively:
#include <stdlib.h>
#define X_FIELDS \
X(X_STR, description) \
X(X_INT, xpreward) \
X(X_INT, att) \
/* ... */ \
X(X_INT, crit)
typedef struct opp
{
#define X_INT char *
#define X_STR int
#define X(code, name) code name;
X_FIELDS
#undef X
#undef X_INT
#undef X_STR
} opp;
extern void update_opp(char** csvSplitRow, opp* opponent);
void update_opp(char** csvSplitRow, opp* opponent)
{
int i = 0;
#define X_INT(str) atoi(str)
#define X_STR(str) str
#define X(converter, name) \
opponent->name = converter(csvSplitRow[i++]);
X_FIELDS
#undef X
#undef X_INT
#undef X_STR
}
I'm not 100% convinced this is better because of the multiple #define
and #undef
operations, but it is more nearly minimal in some respects (it doesn't need the "%d"
vs "%s"
fields, for example — at least, not in the code shown).