I'm writing a program that will simulate a randomized race between runners who are climbing up a mountain where dwarf orcs (dorcs) are coming down the mountain to attack the runners. It begins with two runners named harold and timmy at the bottom of the mountain. The runners make their way up the mountain in randomized moves where they may make progress forward up the mountain, or they may slide back down the mountain. Dorcs are randomly generated, and they inflict damage on a runner if they collide. The simulation ends when one of the runners reaches the top of the mountain, or when both runners are dead.
I'm struggling with a part where I have to implement the actual race loop. Once the race is initialized, the race loop will iterate until the race is over. This happens when either a winner has been declared, or when all runners are dead.
Every iteration of the race loop will do the following:
with 30% probability, dynamically allocate a new dorc as an EntityType structure, and initialize it as follows:
(a) a dorc’s avatar is always “d”
(b) each dorc begins the race at the top of the mountain, which is at row 2
(c) with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two
(d) add the new dorc to the race’s array of dorcs
(e) using the pthread_create() function, create a thread for the new dorc, and save the thread pointer in the dorc’s entity structure; the function that each dorc thread will execute is the void* goDorc(void*) function that you will implement in a later step; the parameter to the goDorc() function will be the EntityType pointer that corresponds to that dorc
I guess I'm confused with the logic of how to approach this. I decided to make a function called isOver() to indicate if the race is over, and then a separate function called addDorc() to initialize the Dorc elements and do all the requirements above.
In isOver(), I attempt to add a dorc object to the dorcs array by doing addDorc(race); with every iteration of the race loop/if the race hasn't ended or no one died. But I keep getting the error:
control.c:82:3: error: too few arguments to function ‘addDorc’
addDorc(race);
The problem is I don't think I can manually declare all the parameters in addDorc() because some elements like the "path" argument are based on probability. As mentioned above, with equal probability, the dorc may be placed either in the same column as timmy, or in the same column as the harold, or in the column exactly half-way between the two. The issue is I don't know how to factor this random value when calling addDorc() and would appreciate some help. I also don't know if I'm doing the "with 30% probability, dynamically allocate a new dorc as an EntityType structure" correctly and would be grateful for some input on that as well.
defs.h
typedef struct {
pthread_t thr;
char avatar[MAX_STR];
int currPos;
int path;
} EntityType;
typedef struct {
EntityType ent;
char name[MAX_STR];
int health;
int dead;
} RunnerType;
typedef struct {
int numRunners;
RunnerType *runners[MAX_RUNNERS];
int numDorcs;
EntityType *dorcs[MAX_DORCS];
char winner[MAX_STR];
int statusRow;
sem_t mutex;
} RaceInfoType;
void launch();
int addDorc(RaceInfoType*, char*, int, int);
int isOver(RaceInfoType*);
void initRunners(RaceInfoType*);
int addRunner(RaceInfoType*, char*, char*, int, int, int, int);
int randm(int);
void *goRunner(void*);
void *goDorc(void*);
RaceInfoType *race;
control.c
void launch(){
race = malloc(sizeof(RaceInfoType));
race->numRunners = 0;
initRunners(race);
if (sem_init(&race->mutex, 0, 1) < 0) {
printf("semaphore initialization error\n");
exit(1);
}
strcpy(race->winner, " ");
srand((unsigned)time(NULL));
int i;
for(i = 0; i < race->numRunners; ++i){
pthread_create(&(race->runners[i]->ent.thr), NULL, goRunner, " ");
}
race->numDorcs = 0;
}
int addDorc(RaceInfoType* race, char *avatar, int path, int currPos){
if(race->numDorcs == MAX_DORCS){
printf("Error: Maximum dorcs already reached. \n");
return 0;
}
race->dorcs[race->numDorcs] = malloc(sizeof(EntityType));
int timmysColumn = race->dorcs[race->numDorcs]->currPos;
int haroldsColumn = race->dorcs[race->numDorcs]->currPos;
int halfwayColumn = (timmysColumn+haroldsColumn)/2;
int r = rand()%100;
pthread_t dorc;
if(r <= 30){
strcpy(race->dorcs[race->numDorcs]->avatar, "d");
race->dorcs[race->numDorcs]->currPos = 2;
if(r <= 33){
race->dorcs[race->numDorcs]->path = timmysColumn;
}else if(r <= 66){
race->dorcs[race->numDorcs]->path = haroldsColumn;
}else{
race->dorcs[race->numDorcs]->path = halfwayColumn;
}
pthread_create(&dorc, NULL, goDorc, " ");
}
race->numRunners++;
}
int isOver(RaceInfoType* race){
int i;
for(i = 0; i < race->numRunners; ++i){
if((race->winner != " ") || (race->runners[race->numRunners]->dead = 1)){
return 1;
}
addDorc(race);
return 0;
}
}
void initRunners(RaceInfoType* r){
addRunner(r, "Timmy", "T", 10, 35, 50, 0);
addRunner(r, "Harold", "H", 14, 35, 50, 0);
}
int addRunner(RaceInfoType* race, char *name, char *avatar, int path, int currPos, int health, int dead){
if(race->numRunners == MAX_RUNNERS){
printf("Error: Maximum runners already reached. \n");
return 0;
}
race->runners[race->numRunners] = malloc(sizeof(RunnerType));
strcpy(race->runners[race->numRunners]->name, name);
strcpy(race->runners[race->numRunners]->ent.avatar, avatar);
race->runners[race->numRunners]->ent.path = path;
race->runners[race->numRunners]->ent.currPos = currPos;
race->runners[race->numRunners]->health = health;
race->runners[race->numRunners]->dead = dead;
race->numRunners++;
return 1;
}
Caveat: Because there's so much missing [unwritten] code, this isn't a complete solution.
But, I notice at least two bugs: the isOver bugs in my top comments. And, incrementing race->numRunners in addDorc.
isOver also has the return 0; misplaced [inside the loop]. That should go as the last statement in the function. If you had compiled with -Wall [which you should always do], that should have been flagged by the compiler (e.g. control reaches end of non-void function)
From that, only one "dorc" would get created (for the first eligible runner). That may be what you want, but [AFAICT] you want to try to create more dorcs (one more for each valid runner).
Also, the bug the compiler flagged is because you're calling addDorc(race); but addDorc takes more arguments.
It's very difficult to follow the code when you're doing (e.g.) race->dorcs[race->numDorcs]->whatever everywhere.
Better to do (e.g.):
EntityType *ent = &race->dorcs[race->numDorcs];
ent->whatever = ...;
Further, it's likely that your thread functions would like a pointer to their [respective] control structs (vs. just passing " ").
Anyway, I've refactored your code to incorporate these changes. I've only tried to fix the obvious/glaring bugs from simple code inspection, but I've not tried to recompile or address the correctness of your logic.
So, there's still more work to do, but the simplifications may help a bit.
void
launch(void)
{
race = malloc(sizeof(RaceInfoType));
race->numRunners = 0;
initRunners(race);
if (sem_init(&race->mutex,0,1) < 0) {
printf("semaphore initialization error\n");
exit(1);
}
strcpy(race->winner," ");
srand((unsigned)time(NULL));
int i;
for (i = 0; i < race->numRunners; ++i) {
RunnerType *run = &race->runners[i];
EntityType *ent = &run->ent;
pthread_create(&ent->thr,NULL,goRunner,ent);
}
race->numDorcs = 0;
}
int
addDorc(RaceInfoType* race,char *avatar,int path,int currPos)
{
if (race->numDorcs == MAX_DORCS) {
printf("Error: Maximum dorcs already reached. \n");
return 0;
}
EntityType *ent = malloc(sizeof(*ent));
race->dorcs[race->numDorcs] = ent;
int timmysColumn = ent->currPos;
int haroldsColumn = ent->currPos;
int halfwayColumn = (timmysColumn + haroldsColumn) / 2;
int r = rand()%100;
#if 0
pthread_t dorc;
#endif
if (r <= 30) {
strcpy(ent->avatar,"d");
ent->currPos = 2;
if (r <= 33) {
ent->path = timmysColumn;
} else if (r <= 66) {
ent->path = haroldsColumn;
} else {
ent->path = halfwayColumn;
}
pthread_create(&ent->thr,NULL,goDorc,ent);
}
#if 0
race->numRunners++;
#else
race->numDorcs += 1;
#endif
}
int
isOver(RaceInfoType* race)
{
int i;
for (i = 0; i < race->numRunners; ++i) {
#if 0
if ((race->winner != " ") ||
(race->runners[race->numRunners]->dead = 1))
return 1;
#else
RunnerType *run = &race->runners[i];
if ((race->winner != " ") || (run->dead == 1))
return 1;
#endif
addDorc(race);
#if 0
return 0;
#endif
}
#if 1
return 0;
#endif
}
void
initRunners(RaceInfoType* r)
{
addRunner(r,"Timmy","T",10,35,50,0);
addRunner(r,"Harold","H",14,35,50,0);
}
int
addRunner(RaceInfoType* race,char *name,char *avatar,int path,int currPos,
int health,int dead)
{
if (race->numRunners == MAX_RUNNERS) {
printf("Error: Maximum runners already reached. \n");
return 0;
}
RunnerType *run = malloc(sizeof(*run));
race->runners[race->numRunners] = run;
strcpy(run->name,name);
EntityType *ent = &run->ent;
strcpy(ent->avatar,avatar);
ent->path = path;
ent->currPos = currPos;
run->health = health;
run->dead = dead;
race->numRunners++;
return 1;
}
UPDATE:
I noticed in addDorc(), you put pthread_t dorc; in an if statement. I don't quite understand what my if statement is actually supposed to be checking though.
I forgot to mention/explain. I wrapped your/old code and my/new code with preprocessor conditionals (e.g.):
#if 0
// old code
#else
// new code
#endif
After the cpp stage, the compiler will only see the // new code stuff. Doing this was an instructional tool to show [where possible] what code you had vs what I replaced it with. This was done to show the changes vs. just rewriting completely.
If we never defined NEVERWAS with a #define NEVERWAS, then the above block would be equivalent to:
#ifdef NEVERWAS
// old code ...
#else
// new code
#endif
Would it still be under the if(r <= 30) part like I did in my original code?
Yes, hopefully now, it is more clear. #if is a cpp directive to include/exclude code (as if you had edited that way). But, a "real" if is an actual executable statement that is evaluated at runtime [as it was before], so no change needed.
My other concern is it doesn't look like dorc is used anywhere in the function because you write pthread_create(&ent->thr,NULL,goDorc,ent); which seems to use ent instead?
That is correct. It is not used/defined and the value goes to ent->thr. As you had it, the pthread_t value set by pthread_create would be lost [when dorc goes out of scope]. So, unless it's saved somewhere semi-permanent (e.g. in ent->thr), there would be no way to do a pthread_join call later.
I'm trying to use static structs as a buffer for incoming messages, in order to avoid checking the buffer on the MCP2515-external unit. An ISR enters the function with a can_message* value 255 to actually read new messages from my MCP2515.
Other applications register an ID in the message passed as argument, in order to check if the buffer holds any messages with the same value.
This returns wrong IDs, and the rest of the datafields are 0 and uninitialized. What is wrong?
can_message struct:
typedef struct
{
uint8_t id;
uint8_t datalength;
uint8_t data[8];
}can_message;
int CAN_message_receive(can_message* message)
{
static volatile can_message* buffers = (volatile can_message*)0x18FF;
static int birth = 1;
if(birth)
{
for (int i; i < CAN_MESSAGE_UNIQUE_IDS; i++)
{
//These structs gets addresses outside SRAM
buffers[i] = (can_message){0,0,0};
}
birth = 0;
}
if (message == CAN_UPDATE_MESSAGES)
{
/* Sorts messages <3 */
can_message currentMessage;
//These functions are working:
CAN_message_get_from_MCP_buf(¤tMessage, 0);
buffers[currentMessage.id] = currentMessage;
CAN_message_get_from_MCP_buf(¤tMessage, 1);
buffers[currentMessage.id] = currentMessage;
return 0; //returns nothing !
}
if(buffers[message->id].id != 0)
{
printf("test\n");
//This copy gives wrong id and data:
memcpy(message, &buffers[message->id], sizeof(can_message));
buffers[message->id].id = 0;
return 0;
}
return -1;
}
Edit 1:
I did however notice that any buffers[i]-struct gets a totally different address than expected. It does not use the addresses following 0x18FF on the SRAM. Is there any way to change this?
Edit 2:
This is my main-loop:
while (1) {
//printf("tx buf ready: %d\n", MCP2515_TX_buf_empty(0));
//CAN_Loopback_test();
_delay_ms(500);
value = USART_ReadByte(0);
CAN_message_receive(&msg);
printf("CAN_receive: ID: %d, datalength: %d, data: \n",msg.id);
for (int k; k < msg.datalength; k++)
{
printf("%d, ",msg.data[k]);
}
printf("\n");
}
Edit 3: Changing the buffer-pointer to array solved the issue. (It does no longer use the SRAM, but whatever floats my boat)
int CAN_message_receive(can_message* message)
{
static can_message buffers[CAN_MESSAGE_UNIQUE_IDS];
static int birth = 1;
if(birth)
{
for (int i; i < CAN_MESSAGE_UNIQUE_IDS*10; i++)
{
*(char*)(0x18FF+i) = 0;
printf("buffers: %X\n", &buffers[i]);
}
birth = 0;
}
Solved!
Pointer to buffers changed to buffer-array:
int CAN_message_receive(can_message* message)
{
static can_message buffers[CAN_MESSAGE_UNIQUE_IDS];
static int birth = 1;
if(birth)
{
for (int i; i < CAN_MESSAGE_UNIQUE_IDS*10; i++)
{
*(char*)(0x18FF+i) = 0;
printf("buffers: %X\n", &buffers[i]);
}
birth = 0;
}
I would strongly suggest to decouple the ISR logic with the programs own message cache logic. Also the initializing logic with the birth variable looks unnecessary.
I would setup some ring buffer that the ISR can write messages to and from that the main code reads the data into the ID-lookup-buffer.
This would ensure that message updates does not interfere with readouts (at least if you check the read/write indices to your ring buffer) and also eliminates the need to put Mutexes around your whole message buffer.
Currently it smells very badly because of missing read/write synchronization.
// global
#define CAN_MESSAGE_UNIQUE_IDS 50
static can_message g_can_messagebuffers[CAN_MESSAGE_UNIQUE_IDS];
#define MAX_RECEIVEBUFFER 8
static volatile can_message g_can_ringbuffer[MAX_RECEIVEBUFFER];
static volatile int g_can_ringbufferRead = 0;
static volatile int g_can_ringbufferWrite = 0;
// called from ISR
void GetNewMessages()
{
// todo: check ring buffer overflow
can_message currentMessage;
CAN_message_get_from_MCP_buf(&g_can_ringbuffer[g_can_ringbufferWrite], 0);
g_can_ringbufferWrite = (g_can_ringbufferWrite + 1) % MAX_RECEIVEBUFFER;
CAN_message_get_from_MCP_buf(&g_can_ringbuffer[g_can_ringbufferWrite], 1);
g_can_ringbufferWrite = (g_can_ringbufferWrite + 1) % MAX_RECEIVEBUFFER;
}
// called from main loop
void handleNewMessages()
{
while(g_can_ringbufferRead != g_can_ringbufferWrite){
const can_message* currentMessage = &g_can_ringbuffer[g_can_ringbufferRead];
if(currentMessage->id < CAN_MESSAGE_UNIQUE_IDS)
{
g_can_messagebuffers[currentMessage->id] = *currentMessage;
}
g_can_ringbufferRead = (g_can_ringbufferRead + 1) % MAX_RECEIVEBUFFER;
}
}
// called from whoever wants to know
// todo:
// really required a by value interface?
// would it not be sufficient to return a pointer and
// provide an additional interface to mark the message as used?
int getMsg(can_message* message)
{
if(buffers[message->id].id != 0)
{
printf("test\n");
*message = &g_can_messagebuffers[message->id];
g_can_messagebuffers[message->id].id = 0;
return 0;
}
return -1;
}
// alternative to above
const can_message* getMsg(int id)
{
if( (id < CAN_MESSAGE_UNIQUE_IDS)
&& (g_can_messagebuffers[id] != 0))
{
return &g_can_messagebuffers[id].id;
}
return NULL;
}
void invalidateMsg(int id)
{
if(id < CAN_MESSAGE_UNIQUE_IDS)
{
g_can_messagebuffers[id] = 0;
}
}
edit:
after your changes to an message array instead some strange pointer, there is also no need for the setup routine for this code.
edit:
if your micro controller already has a buffer for received messages, then may be it is unnecessary at all to register a ISR and you could empty it from the mainloop directly into your own id-lookup buffer (assuming the mainloop is fast enough)
Can someone please help me understand why my printf statement in the main loop prints nothing? If I add a regular hello world printf statement in the main loop it also does not print anything to the console. However, if I comment out the line that prints the returned data, this hello world print statement does work.
I have tried multiple methods of passing the string data between the two functions with no luck.
char * getADCValue()
{
uint8_t id;
int32_t adc;
int32_t volt;
uint8_t i;
uint8_t ch_num;
int32_t iTemp;
uint8_t buf[3];
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST );
bcm2835_spi_setDataMode(BCM2835_SPI_MODE1);
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_1024);
bcm2835_gpio_fsel(SPICS, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(SPICS, HIGH);
bcm2835_gpio_fsel(DRDY, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud(DRDY, BCM2835_GPIO_PUD_UP);
id = ADS1256_ReadChipID();
/*if (id != 3)
{
printf("Error, ASD1256 Chip ID = 0x%d", (int)id);
}
else
{
printf("Ok, ASD1256 Chip ID = 0x%d", (int)id);
}*/
ADS1256_CfgADC(ADS1256_GAIN_1, ADS1256_15SPS);
ADS1256_StartScan(0);
while((ADS1256_Scan() == 0));
adc = ADS1256_GetAdc(0);
volt = (adc * 100) / 167;
buf[0] = ((uint32_t)adc >> 16) & 0xFF;
buf[1] = ((uint32_t)adc >> 8) & 0xFF;
buf[2] = ((uint32_t)adc >> 0) & 0xFF;
iTemp = volt; /* uV */
char * return_data = malloc(1024);
if (!return_data)
return NULL;
sprintf(return_data, "%ld.%01ld V", iTemp /1000000, (iTemp%1000000)/100000);
return return_data;
}
int main()
{
char * returnValue = getADCValue();
printf("Voltage Value: %s", returnValue);
free(returnValue);
return 0;
}
Thanks for the suggestions guys. It ended up being the bcm library calls as suggested. Somehow lost my bcm2835 init call and this resulted in something going haywire with my file descriptors.
I am trying to program the logomatic by sparkfun, and yes I have used their forum with no responses, and having some issues. I am trying to send characters to the UART0 and I want the logomatic to respond with specific characters and not just an echo. For example, I send 'ID?' over the terminal (using RealTerm), and the logomatic sends back '1'. All it will so now is echo.
I am using c with programmers notepad with the WinARM toolchain. The following snippet is from the main.c file. I only included this, because I am fairly certain that this is where my problem lies
void Initialize(void)
{
rprintf_devopen(putc_serial0);
PINSEL0 = 0xCF351505;
PINSEL1 = 0x15441801;
IODIR0 |= 0x00000884;
IOSET0 = 0x00000080;
S0SPCR = 0x08; // SPI clk to be pclk/8
S0SPCR = 0x30; // master, msb, first clk edge, active high, no ints
}
Notice the rprintf_devopen function, below is from the rprintf.c file, and due to my mediocre skills, I do not understand this bit of code. If I comment out the rprintf_devopen in main, the chip never initializes correctly.
static int (*putcharfunc)(int c);
void rprintf_devopen( int(*put)(int) )
{
putcharfunc = put;
}
static void myputchar(unsigned char c)
{
if(c == '\n') putcharfunc('\r');
putcharfunc(c);
}
Now, below is from the serial.c file. So my thought was that I should be able to just call one of these putchar functions in main.c and that it would work, but it still just echoes.
int putchar_serial0 (int ch)
{
if (ch == '\n')
{
while (!(U0LSR & 0x20));
U0THR = CR; // output CR
}
while (!(U0LSR & 0x20));
return (U0THR = ch);
}
// Write character to Serial Port 0 without \n -> \r\n
int putc_serial0 (int ch)
{
while (!(U0LSR & 0x20));
return (U0THR = ch);
}
// Write character to Serial Port 1 without \n -> \r\n
int putc_serial1 (int ch)
{
while (!(U1LSR & 0x20));
return (U1THR = ch);
}
void putstring_serial0 (const char *string)
{
char ch;
while ((ch = *string))
{
putchar_serial0(ch);
string++;
}
}
I have tried calling the different putchar functions in main, also with the rprintf_devopen. Still just echoes. I have altered the putchar functions and still just echoes. I have tried just writing to the U0THR register in main.c and no luck. Keep in mind that I am still a student and my major is electrical engineering, so the only programming classes that I have taken are intro to c, and an intro to vhdl. I am more of a math and physics guy. I was working on this for an internship I was doing. The internship ended, but it just bugs me that I cannot figure this out. Honestly, working on this program taught me more that the c class that I took. Anyways, I appreciate any help that can be offered, and let me know if you want to see the entire code.
Below is an update to the question. This function is in main.c
static void UART0ISR(void)
{
char temp;
trig = 13; //This is where you set the trigger character in decimal, in this case a carriage return.
temp = U0RBR; //U0RBR is the receive buffer on the chip, refer to datasheet.
if(temp == query1[counter1]) //This segment looks for the characters "ID?" from the U0RBR
{ //query1 is defined at the top of the program
counter1++;
if(counter1 >= 3)
{
flag1 = 1; //This keeps track of whether or not query1 was found
counter1 = 0;
stat(1,ON);
delay_ms(50);
stat(1,OFF);
RX_in = 0;
temp = 0;
//rprintf("\n\rtransmission works\n");
putc_serial1(49);
}
}
if(temp == query2[counter2] && flag1 == 1) //This segment looks for "protov?" from the U0RBR, but only after query1 has been found
{
counter2++;
if(counter2 >= 7)
{
flag2 = 1; //This keeps track of whether or not query2 was found
counter2 = 0;
stat(1,ON);
delay_ms(50);
stat(1,OFF);
RX_in = 0;
temp = 0;
putc_serial1(49);
}
}
if(temp == stop[counter3]) //This if segment looks for certain characters in the receive buffer to stop logging
{
counter3++;
if(counter3 >= 2)
{
flagstop = 1; //This flagstop keeps track of whether or not stop was found. When the stop characters are found,
flag1 = 0; //the query1 and query2 flags will be reset. So, in order to log again these queries must be sent again
flag2 = 0; //this may seem obvious, but deserves mention.
counter3 = 0;
stat(1,ON);
delay_ms(500);
stat(1,OFF);
RX_in = 0;
temp = 0;
}
flagstop = 0; //Reset the stop flag in order to wait once again for the query 1&2
}
if(RX_in == 0)
{
memset (RX_array1, 0, 512); // This clears the RX_array to make way for new data
memset (RX_array2, 0, 512);
}
if(RX_in < 512 && flag1 == 1 && flag2 == 1) //We cannot log data until we see both flags 1 & 2 and after we see these flags,
{ //we must then see the trigger character "carriage return"
RX_array1[RX_in] = temp;
RX_in++;
if(temp == trig)
{
RX_array1[RX_in] = 10; // delimiters
log_array1 = 1;
RX_in = 0;
}
}
else if(RX_in >= 512 && flag1 == 1 && flag2 == 1) //This else if is here in case the RX_in is greater than 512 because the RX_arrays are defined to
{ //be of size 512. If this happens we don't want to lose data, so we must put the overflow into another register.
RX_array2[RX_in - 512] = temp;
RX_in++;
RX_array1[512] = 10; // delimiters
RX_array1[512 + 1] = 13;
log_array1 = 1;
if(RX_in == 1024 || temp == trig)
{
RX_array2[RX_in - 512] = 10; // delimiters
log_array2 = 1;
RX_in = 0;
}
}
temp = U0IIR; // have to read this to clear the interrupt
VICVectAddr = 0;
}
I want to configure (module A) to send certain data to (module B) at certain slots of time.
(Module B) should send these configuration to (module A) during initialization.
The data is:
struct _data
{
int temp;
int velocity;
int time;
}
For example, (module A) should send 'temp' at first slot, then 'temp & velocity' at second slot, then 'time' at third slot .... etc
I am thinking about making making "configuration flags" structure:
struct _configuration
{
int temp_flag;
int time_flag;
int velocity_flag;
}
Then making an array of this structures:
struct _configuration arr[NUMBER_OF_SLOTS];
and configure using this array:
arr[0].temp_flag = 1;
arr[0].velocity_flag = 0;
arr[0].time_flag = 0;
arr[1].temp_flag = 1;
arr[1].velocity_flag = 1;
arr[1].time_flag = 0;
arr[2].temp_flag = 0;
arr[2].velocity_flag = 0;
arr[2].time_flag = 1;
.... etc
But I am not very happy with this approach ... does anyone has a better way or algorithm to do this task ?
Many thanks in advance
One of the many possible solutions is bitmasks (very popular in computer graphics). As you know, every number can be represented as sequence of 0 and 1, so they can be the flags that mean something. It's quite simple to use them in such way because we have bitwise operations. And there is no need to create some configuration structures.
const int USE_TEMP = 1 << 0; // 01
const int USE_VELOCITY = 1 << 1; // 010
const int USE_TIME = 1 << 2; // 0100
// ...
// you have 32 free-to-use bits
const int USE_ALL = 0111; // just for fun
struct _data {
int temp;
int velocity;
int time;
}
And it bring us to
int arr[NUMBER_OF_SLOTS];
arr[0] = USE_TEMP;
arr[1] = USE_TEMP | USE_VELOCITY;
arr[2] = USE_TIME;
Looks better, isn't it?
Checking values
When you need to check whether some of the parameters included in configuration, it's very simple
if( arr[i] & USE_TEMP ) {
// do smth with temp, it's included
}
or this way
int expected_flags = USE_TIME | USE_VELOCITY;
if( arr[i] & expected_flags == expected_flags ) {
// time and velocity enabled
}
or even declare special function (or scary macro) to check whether some parameters are in your config
bool check(int config, int flags) {
return config & flags == flags;
}
Changing configurations
What if you need to delete/add parameters?
int some_conf = USE_TEMP | USE_VELOCITY;
Simple way
// delete
if( check(some_conf, USE_VELOCITY) )
some_conf -= USE_VELOCITY; // <--- dangerous without if( )
// add
if( !check(some_conf, USE_TIME) )
some_conf += USE_TIME; // <--- dangerous without if( )
Safety way
// delete
some_conf &= ~USE_VELOCITY;
// add
some_conf |= USE_TIME;