I've been wondering if there's a way to do a function that saves game state but in transparent way. It's not a question about actual saving (I know how to save to file etc), it's about writing it in a clear, simple and understandable way.
So far I've came to three ideas (remember, I wish it could be a function or some easy thing like savegame()), none is perfect:
Put every variable I want to save into a struct like:
struct state{
int hp;
pos position;
int x;
int y;
...
};
...
if(savegame==1) {
state game;
game.hp=hp;
game.position=position;
game.x=x;
game.y=y;
...
savegame(game);
}
as you can see, it's not looking any good, it takes so many lines to do just a saving: what if I would like to save like 40 variables? What if I want to do saving in like 5-6 places in a code?
Make my function long as snake (this gives a problem with loading it back, so for example I also put a loadgame func):
void savegame(int hp, pos position, int x, int y,...) // too long
{ ... }
void loadgame(int &hp, pos &position, int &x, int &y,...) // too long
{ ... }
...
int main()
{
...
savegame(hp, pos, x, y, ...) // too long
...
loadgame(hp, pos, x, y, ...) // too long
}
again, if it would have 40 variables inside(and i also would like to call savegame or loadgame like 5-6 times), it would take like 4 lines of code each(i just skip an idea to put it in 1 line)
Start my program with struct containing game state, and make all calculations, actually all game on that struct
struct gamestate{
int hp;
pos position;
int x;
int y;
}
...
int main()
{
struct gamestate s;
s.x++;
...
}
but this leads to a problem where everytime I make calculations I have to add this 's' at beggining.
Conclusion:
My idea was to maybe change second option just to reduce every call of savegame(...); to simple savegame();, is there a way to do it? Without define?
Here is a dynamic way to do something similar to what you describe in your 1st option, i.e. track a game state using pointer to struct but that also stores and recovers that data to and from a binary file...
The following .c and .h files illustrate the idea that you can use a compound struct, i.e. one containing several other structs, that at any one time contain all of the values representing states in your program. This is very easily passed via a function(s) with very few arguments (prototyped eg. as char *GetState(state *in, const char *filespec); and char *SetState(state *out, const char *filespec); ) which in turn would write or read the contents of the struct into/out of a binary buffer. I have used this method to store and retrieve multiple sets of state data within files.
Note, the structs have random fields that of course you will modify as needed, but the idea is that you can pass a single pointer value that points to all the state data, in every function where state date is updated, or needs to be stored.
so_SaveGameState.c
#include <so_SaveGameState.h>
unsigned char *pByteA;
GAME_STATE game = {{{"jim", "C:\\ico1.ico", {10, 120, 3}}, {"joe", "C:\\ico2.ico", {80, 10, -5}},{"larry", "C:\\ico3.ico", {15, -45, -45}},{"sue", "C:\\ico4.ico", {-100, -45, 45}}}, ENVR_3};
GAME_STATE *pGame = NULL;
int main(void)
{
pGame = &game;//point to populated memory
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
//example function that changes game state
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
UpdatePlayerPosition(&pGame, 2);
printf("Player 3 position\nx = %d\ny = %d\nz = %d\n", game.plyr[2].pos.x, game.plyr[2].pos.y, game.plyr[2].pos.z);
//prepare an instance of game state for storeing
(const GAME_STATE *)pByteA = &game;
int len1 = sizeof(game);
BOOL status = storeState("C:\\tempextract\\binFileStruct.bin", pByteA, len1);
//recover a stored state
unsigned char *buf = recoverState("C:\\tempextract\\binFileStruct.bin");
GAME_STATE *game_2 = (GAME_STATE *)buf;
free(game_2);
return 0;
}
unsigned char * recoverState(const char *filespec)
{
size_t sz = 0;
int n = 0;
unsigned char *binBuf = NULL;
FILE *fp = fopen(filespec, "rb");
if(fp)
{
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
rewind(fp);
binBuf = calloc(sz, sizeof(*binBuf));
n = fread(binBuf, sizeof(unsigned char), sz, fp);
fclose(fp);
}
if(n == sz)
{
return binBuf;
}
else
{
return NULL;
}
}
int storeState(const char *filespec, const unsigned char *buf, size_t sz)
{
int count = 0;
FILE *fp = fopen(filespec, "wb");
if(fp)
{
count = fwrite(buf, sizeof(unsigned char), sz, fp);
fclose(fp);
}
return (count == sz) ? 1 : 0;
}
void UpdatePlayerPosition(GAME_STATE **game, int player)
{
static int x=0, y=0, z=0;
static BOOL toggle = TRUE;
toggle = (toggle == 1) ? -1 : 1;
srand(clock());
//using fake assignment here
//i.e. you would have other criteria to set actual position
x += toggle * rand()%300;
y += toggle * rand()%300;
z += toggle * rand()%300;
(*game)->plyr[player].pos.x = x;
(*game)->plyr[player].pos.y = y;
(*game)->plyr[player].pos.y = z;
}
so_StoreGameState.h
typedef enum {//environment
ENVR_1, //bad weather
ENVR_2, //hill
ENVR_3, //pit
ENVR_4, //angry birds
ENVR_5, //enemy guard
MAX_OBST
}ENVR_TYPE;
typedef struct {
int x;
int y;
int z;
}POS;
typedef struct {
ENVR_TYPE envir;
//...
}ENVIR;
typedef struct {
char name[20];
char iconFile[260];
POS pos;
//...
}PLAYER;
typedef struct {
PLAYER plyr[4];
ENVIR env;
//...
}GAME_STATE;
extern GAME_STATE game;
unsigned char * recoverState(const char *filespec);
int storeState(const char *filespec, const unsigned char *buf, size_t sz);
Related
So, I'm working with some C code. I've defined the following function:
int load_csv(size_t L, size_t W, CSV_DATA csv_data[L][W], char file[])
If i call this function, everything works as it should.
If, however, I change the order of the first two arguments, so that the function is defined as follows:
int load_csv(size_t W, size_t L, CSV_DATA csv_data[L][W], char file[])
I get a segfault when I try to save data to csv_data. Can anyone give any insight into the reasoning why the arguments defined before a struct parameter (csv_data in this case) has to be in the same order?
edit: as requested, here is the entire function:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
enum { is_int, is_float, is_char } type;
int ival;
char cval[10];
float fval;
}CSV_DATA;
int load_csv(size_t L, size_t W, CSV_DATA csv_data[L][W], char file[])
{
char buffer[1024] ;
char *record,*line;
int i = 0;
FILE *fstream = fopen("iris.csv","r");
if(fstream == NULL)
{
printf("\n file opening failed ");
return -1 ;
}
while((line=fgets(buffer,sizeof(buffer),fstream))!=NULL)
{
int j = 0;
record = strtok(line,",");
printf("%s", record);
while(record != NULL)
{
csv_data[i][j].type = is_char;
for (int k=0; k < strlen(record); k++){
csv_data[i][j].cval[k] = record[k];//record;
}
j++;
record = strtok(NULL,",");
//printf("%s", record);
}
++i;
}
return 0;
}
edit: here's the calling code. Eventhough..I'm not using L or W anywhere in the called function...
int main(){
int L = 500;
int W = 50;
CSV_DATA csv_data[500][50];
char file[10] = "iris.csv";
load_csv(L ,W , csv_data, file);
return 0;
}
If you swap the order of the parameters in the function definition, then you need to also swap them either in the array parameter declaration or in the function call. IOW, if you change
int load_csv(size_t L, size_t W, CSV_DATA csv_data[L][W], char file[])
to
int load_csv(size_t W, size_t L, CSV_DATA csv_data[L][W], char file[])
then you either need to change the declaration of csv_data as well:
int load_csv(size_t W, size_t L, CSV_DATA csv_data[W][L], char file[])
or you need to change the parameter order in the function call:
load_csv(L, W, csv_data, file);
to
load_csv(W, L, csv_data, file);
Otherwise, the dimensions of csv_data in load_csv will be reversed from the dimensions of csv_data in main, leading to out-of-range array accesses at some point.
The order shouldn't matter - its more likely what you are doing with the values inside the function that has changed - did you change the order of the values where the function is called?
Having a problem with a 2D variable array of structs.
Trying to parse a text file and populate the array with the following function:
void populateTable(int x; int y; Entry table[x][y],
int x, int y, char fileName[])
{
char currentLineStr[8192];
int yIndex = 0;
FILE *fileIn;
FILE *fileOut;
fileIn = fopen(fileName, "r");
if(fileIn == 0)
{
perror("Cannot open input file\n");
system("PAUSE");
exit(-1);
}
else
{
fgets(currentLineStr, 8192, fileIn);
while (currentLineStr[0] == '#')
fgets(currentLineStr, 8192, fileIn);
TokenizeLine(currentLineStr, table, yIndex, x, y);
yIndex++;
while(fgets(currentLineStr, 8192, fileIn) != NULL)
{
TokenizeLine(currentLineStr, table, yIndex, x, y);
yIndex++;
//printf("%i\n",yIndex);
}
fclose(fileIn);
}
printf("%s\n", table[0][0].str);
printf("%f\n", table[1][0].dVal);
}
The printf statements at the end of this return the values that they should. However immediately after the call of this function I do the same printf statements and they return the wrong information.
int main(int argc, char *argv[])
{
char stringToFind[256] = {"DWL_FaceMountTile"};
char fileName[64] = {"rtest.txt"};
char timeFile[64] = {"timefile.msgr"};
int numEntries = 0;
int x;
int y;
double time;
double temp;
int tTableXdim = 36;
int tTableYdim = 20;
x = NumColumns(fileName);
y = NumRows(fileName);
Entry eTable[x][y];
Entry tTable[x][y];
int idInstances[y];
int rowQuantity[y];
double clipLines[x][y];
populateTable(tTable,tTableXdim,tTableYdim,timeFile);// call for above code.
printf("%s\n", tTable[0][0].str);
printf("%f\n", tTable[1][0].dVal);// I call these any they return wrong
If anyone could shine some light on this issue I'd appreciate it greatly.
Edit (was suggested that I post my TokenizeLine function):
void TokenizeLine(int x; int y; char currentLineStr[], Entry table[x][y], int yIndex, int x, int y)
{
char *tokPtr;
char *current;
int xIndex = 0;
current = currentLineStr;
tokPtr = mystrsep(¤t, "|");
while(tokPtr != NULL)
{
if(IsDouble(tokPtr))
{
table[xIndex][yIndex].str = NULL;
table[xIndex][yIndex].dVal = atof(tokPtr);
}
else
{
table[xIndex][yIndex].str = malloc(strlen(tokPtr) + 1);
strcpy(table[xIndex][yIndex].str, tokPtr);
table[xIndex][yIndex].dVal = 0;
}
tokPtr = mystrsep(¤t, "|")
xIndex++;
}
}
As requested the Entry definition.
typedef struct Entry
{
char *str;
double dVal;
} Entry;
The output that I get within the populateTable function is.
DWL_FaceMountTile
1.000000
Which is completely correct and the correctness holds true for all the entries when accessed within populateTable
The below is what I get when accessing the table outside of populateTable after its been called.
the str values of the table all appear to be correct, however the dVal are all set to zero somehow as shown below.
DWL_FacMountTile
0.000000
Hi I'm having trouble trying to initializing each element of the struct array. When I try and assign the value ZERO to both 'bSize' and 'msgs', it doesn't work as it errors out when i get to malloc. In the printf statement it prints a -1852803823 number. Excuse the messy code as i'm playing around trying to figure it out.
struct message{
int *data;
int bSize;
int msgs;
};
int main(int argc, char *argv[]) {
.....
}
void getSchedFile (FILE *file, int **schd) {
struct message sMsg[nodeCount];
const int pakSize = 6;
// Iniitialise message buffer
for (int i=0; i<nodeCount; i++){
sMsg[i].bSize = 0;
sMsg[i].msgs = 0;
printf("bSize %d\n",sMsg[i].bSize);
}
/* Get the number of bytes */
fseek(file, 0L, SEEK_SET);
int time;
while((fscanf(file, "%d", &time)) != EOF){
int src;
fscanf(file, "%d", &src); // get source node id
// These are here for easier reading code
int aPos = sMsg[src].bSize;
int nMsg = sMsg[src].msgs;
printf("size %d\n", sMsg[src].bSize);
if (sMsg[src].bSize==0){
sMsg[src].data = malloc( pakSize * sizeof(int));
}else{
sMsg[src].data = realloc(sMsg[src].data, (aPos+pakSize)*sizeof(int));
}
Where is the nodeCount value coming from? Is it a global variable? You should be very careful with global variables, and avoid using them if possible.
Pass the nodeCount in the method parameter and as Charlie mentioned, check it for > 0
I'm currently trying to use msgpack in a project written in C. I'm using msgpack for the purpose of serializing the contents of a struct, which is then to be sent over the network, and deserialized back into a corresponding struct on the other side.
Condensed version of what I'm trying to do:
#include <stdio.h>
#include <msgpack.h>
#include <stdbool.h>
typedef someStruct{
uint32_t a;
uint32_t b;
float c;
} someStruct;
int main (void){
someStruct data;
/* ... Fill 'data' with some data for test purposes ...*/
msgpack_sbuffer* buff = msgpack_sbuffer_new();
msgpack_packer* pck = msgpack_packer_new(buff, msgpack_sbuffer_write);
someStruct* structs = malloc(sizeof(someStruct) * 10);
/* ... Fill 'structs' with members containing test data ... */
// Serialize
msgpack_pack_array (pck, 10);
int i;
for(i = 0 ; i < 10 ; i++){
msgpack_pack_array (pck, 3);
msgpack_pack_uint32 (pck, structs[i].a);
msgpack_pack_uint32 (pck, structs[i].b);
msgpack_pack_float (pck, structs[i].c);
}
free(structs);
msgpack_packer_free(pck);
// Deserialize
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
bool deserialize_success = msgpack_unpack_next
(&msg, buff->data, buff->size, NULL);
if(!deserialize_success) /* Error */
msgpack_object obj = msg.data;
msgpack_object_print(stdout,obj); // This seems to work perfectly, indicating serialize / deserialize works as intended...
someStruct deserialized_data;
/* Insert code to extract and cast deserialized data to 'deserialized_data */
// Clean
msgpack_sbuffer_free(buff);
msgpack_packer_free(pck);
return 0;
}
The code listed is more or less ripped straight from here, which seems to be one of very few resources on msgpack-c.
Can anyone point me in the right direction as to a way to 'recreate' the original struct on the other side of the wire? The only way I've found to actually utilize the deserialized data, is to use the msgpack_object_print() call to print from the messagepack_object. This does, however seem to work, so I'm certain the data is there.
Do I need to somehow loop through the serialized data and use msgpack_unpack_next() with an offset to retrieve each someStruct member? Using memcpy to a local byte buffer?
Any help is greatly appreciated!
Please find below a rewritten version that illustrates how to pack / unpack your data.
The whole idea is to pack each successive field of your struct, in a contiguous fashion, and apply (of course), the same logic at unpack time.
Right after pack, you are free to use the buffer the way you want (e.g send over the network, save on-disk, etc).
#include <stdio.h>
#include <assert.h>
#include <msgpack.h>
typedef struct some_struct {
uint32_t a;
uint32_t b;
float c;
} some_struct;
static char *pack(const some_struct *s, int num, int *size);
static some_struct *unpack(const void *ptr, int size, int *num);
/* Fixtures */
some_struct ary[] = {
{ 1234, 5678, 3.14f },
{ 4321, 8765, 4.13f },
{ 2143, 6587, 1.34f }
};
int main(void) {
/** PACK */
int size;
char *buf = pack(ary, sizeof(ary)/sizeof(ary[0]), &size);
printf("pack %zd struct(s): %d byte(s)\n", sizeof(ary)/sizeof(ary[0]), size);
/** UNPACK */
int num;
some_struct *s = unpack(buf, size, &num);
printf("unpack: %d struct(s)\n", num);
/** CHECK */
assert(num == (int) sizeof(ary)/sizeof(ary[0]));
for (int i = 0; i < num; i++) {
assert(s[i].a == ary[i].a);
assert(s[i].b == ary[i].b);
assert(s[i].c == ary[i].c);
}
printf("check ok. Exiting...\n");
free(buf);
free(s);
return 0;
}
static char *pack(const some_struct *s, int num, int *size) {
assert(num > 0);
char *buf = NULL;
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer pck;
msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
/* The array will store `num` contiguous blocks made of a, b, c attributes */
msgpack_pack_array(&pck, 3 * num);
for (int i = 0; i < num; ++i) {
msgpack_pack_uint32(&pck, s[i].a);
msgpack_pack_uint32(&pck, s[i].b);
msgpack_pack_float(&pck, s[i].c);
}
*size = sbuf.size;
buf = malloc(sbuf.size);
memcpy(buf, sbuf.data, sbuf.size);
msgpack_sbuffer_destroy(&sbuf);
return buf;
}
static some_struct *unpack(const void *ptr, int size, int *num) {
some_struct *s = NULL;
msgpack_unpacked msg;
msgpack_unpacked_init(&msg);
if (msgpack_unpack_next(&msg, ptr, size, NULL)) {
msgpack_object root = msg.data;
if (root.type == MSGPACK_OBJECT_ARRAY) {
assert(root.via.array.size % 3 == 0);
*num = root.via.array.size / 3;
s = malloc(root.via.array.size*sizeof(*s));
for (int i = 0, j = 0; i < root.via.array.size; i += 3, j++) {
s[j].a = root.via.array.ptr[i].via.u64;
s[j].b = root.via.array.ptr[i + 1].via.u64;
s[j].c = root.via.array.ptr[i + 2].via.dec;
}
}
}
msgpack_unpacked_destroy(&msg);
return s;
}
This question already has answers here:
How do I return multiple values from a function in C?
(8 answers)
Closed 3 years ago.
Can anyone tell me how to return multiple values from a function?
Please elaborate with some example?
Your choices here are to either return a struct with elements of your liking, or make the function to handle the arguments with pointers.
/* method 1 */
struct Bar{
int x;
int y;
};
struct Bar funct();
struct Bar funct(){
struct Bar result;
result.x = 1;
result.y = 2;
return result;
}
/* method 2 */
void funct2(int *x, int *y);
void funct2(int *x, int *y){
/* dereferencing and setting */
*x = 1;
*y = 2;
}
int main(int argc, char* argv[]) {
struct Bar dunno = funct();
int x,y;
funct2(&x, &y);
// dunno.x == x
// dunno.y == y
return 0;
}
You can't do that directly. Your options are to wrap multiple values into a struct, or to pass them in as pointer arguments to the function.
e.g.
typedef struct blah
{
int a;
float b;
} blah_t;
blah_t my_func()
{
blah_t blah;
blah.a = 1;
blah.b = 2.0f;
return blah;
}
or:
void my_func(int *p_a, float *p_b)
{
*p_a = 1;
*p_b = 2.0f;
}
First of all, take a step back and ask why you need to return multiple values. If those values aren't somehow related to each other (either functionally or operationally), then you need to stop and rethink what you're doing.
If the various data items are part of a larger, composite data type (such as a mailing address, or a line item in a sales order, or some other type described by multiple attributes), then define a struct type to represent a single value of that composite type:
struct addr { // struct type to represent mailing address
char *name;
int streetNumber;
char *streetName;
char *unitNumber;
char *city;
char state[3];
int ZIP;
};
struct addr getAddressFor(char *name) {...}
struct point2D {
int x;
int y;
};
struct polygon2D {
size_t numPoints;
struct point2D *points;
};
struct point2D getOrigin(struct polygon2D poly) {...}
Do not define a struct to collect random items that aren't somehow related to each other; that's just going to cause confusion for you and anyone who has to maintain your code down the road.
If the data items are not functionally related, but are somehow operationally related (e.g. data plus a status flag plus metadata about the operation or items as part of a single input operation), then use multiple writable parameters. The most obvious examples are the *scanf() functions in the standard library. There are also the strtod() and strtol() functions, which convert a string representation of a number; they return the converted value, but they also write the first character that was not converted to a separate parameter:
char *str = "3.14159";
double value;
char *chk;
value = strtod(str, &chk);
if (!isspace(*chk) && *chk != 0)
printf("Non-numeric character found in %s\n", str);
You can combine these approaches; here's an example inspired by some work I'm currently doing:
typedef enum {SUCCESS, REQ_GARBLED, NO_DATA_OF_TYPE, EMPTY, ERROR} Status;
typedef struct bounds {...} Bounds;
tyepdef struct metadata {
size_t bytesRead;
size_t elementsRead;
size_t rows;
size_t cols;
} Metadata;
typedef struct elevations {
size_t numValues;
short *elevations;
} Elevations;
Elevations elevs;
Metadata meta;
Bounds b = ...; // set up search boundary
Status stat = getElevationsFor(b, &elevs, &meta);
The service that I request elevation data from returns a 1-d sequence of values; the dimensions of the array are returned as part of the metadata.
You can do it using structures:
#include <stdio.h>
struct dont { int x; double y; };
struct dont fred(void)
{
struct dont b;
b.x = 1;
b.y = 91.99919;
return b;
}
int main(int argc, char **argv)
{
struct dont look = fred();
printf("look.x = %d, look.y = %lf\n", look.x, look.y);
return 0;
}
You cannot return multiple values from a C function.
You can either
Return a data structure with multiple values, like a struct or an array.
Pass pointers to the function and modify the values of the pointers inside the function. You need to pass x number of pointers where x is the number of return values you need
To return multiple values from a function we should use a pointer. Here is an example through which you can understand it better
int* twoSum(int* nums, int numsSize, int target) {
int i,j,*a;
a=(int*)malloc(2*sizeof(int));
for(i=0;i<numsSize;i++)
for(j=i+1;j<numsSize;j++)
if(nums[i]+nums[j]==target)
{
a[0]=i;
a[1]=j;
return a;
}
}
I´m a beginner in C, so I don´t have experience with array, pointer, structure. To get more than one value from my function I just used a global variable.
Here is my code:
#include <stdio.h>
double calculateCharges( double hourCharges );
// Global variable for totalCharges-function and main-function interaction
double totalCharges = 0;
int main ( void ) {
double car1 = 0;
double car2 = 0;
double car3 = 0;
double totalHours = 0;
printf( "%s", "Hours parked for Car #1: ");
scanf( "%lf", &car1 );
printf( "%s", "Hours parked for Car #2: ");
scanf( "%lf", &car2 );
printf( "%s", "Hours parked for Car #3: ");
scanf( "%lf", &car3 );
totalHours = car1 + car2 + car3;
printf( "%s", "Car\tHours\tCharge\n");
printf( "#1\t%.1f\t%.2f\n", car1, calculateCharges( car1 ));
printf( "#2\t%.1f\t%.2f\n", car2, calculateCharges( car2 ));
printf( "#3\t%.1f\t%.2f\n", car3, calculateCharges( car3 ));
printf( "TOTAL\t%.1f\t%.2f\n", totalHours, totalCharges);
}
double calculateCharges( double hourCharges ) {
double charges = 0;
if( hourCharges <= 3.0 ) {
charges = 2;
} else if ( hourCharges >= 24.0) {
charges = 10.00;
} else {
charges = ((hourCharges - 3.0)*0.5) + 2.0;
}
totalCharges += charges;
return charges;
}
Method 1 is using array
Method 2 is using pointer
Method 3 is using structure