Reputation: 1063
I am creating a large application with a large amount of constant values that are used during the entire run of the application but that sometimes changed in between uses, sensor parameters and so on. In Python which I am quite comfortable with I simply use a separate file where I store all the variables, using all caps to indicate a constant value as follows (just and example):
IP = "169.254.98.220" # The ip used to connect to the LiDAR via Ethernet
SAMPLES_PER_SCAN = 2400 # number of scans collected in one revolution
SCAN_FREQUENCY = 35 # Hz, note that a high samples_per_scan can require a low scan_frequency
USE_FILTER = False # Uses a filter to average 5 incoming scans into one
However I want to know what the recommended way to store the same type of values in C. I could not find the answer, I'm sure the answer can be found somewhere but I can not find it, maybe I'm using the wrong search keywords.
Is the best way to do "the same" as I do in python by using a single file for all variables and then use a header file to share them between files?
Upvotes: 2
Views: 400
Reputation: 23218
"recommended way to store the same type of values in C. I could not find the answer,..."
Without knowing more about your immediate purpose/plans, the question does not have a right answer, only guesses. Following are some guiding suggestions based on scope of project assumptions.
Header file defined constants:
If this is a quick prototype or proof of concept, then using the header file approach is likely to be completely adequate. This probably depends on how large the “…large amount of constant values…” actually is. It’s all really up to you. Depending on how patient you are about editing all those values, and how meticulous you are when accuracy of value is important. If just 10-20 items, then okay, If much larger than that, I would choose to nix the header file idea and use either a configuration file, or database entry.
Disadvantage - This is not run-time adjustable. What is defined at compile time is the behavior you will get at run-time.
Advantage – simple to implement. (Using #defines
, extern
scoped variables, and macro
definitions is easy and well documented)
External initialization file/database:
If project is well defined, and fairly well along in development, and uses a large number of configuration values, then reading from external storage (whether it be file or database.) is probably best. Once implemented this will supports maximum run-time flexibility with minimal effort.
Disadvantage – This requires more time and rigor during implementation. The code to read in a file does not have to be complicated as long as the file is syntactically strict, and does not require frequent changes to field definitions, or count of fields.
Advantage – Maximum run-time flexibility. Can use run-time prompting of user to input which file/database table to use during run-time session, etc.
Below is a very simple concept for reading a simple file (such as the one your post describes) Even though this will compile and build, treat it as pseudo code and modify/expand as needs dictate.
enum {//enum values useful to use as indexes to struct values during initialization
IP,
SAMPS_PER_SCAN,
SCAN_FREQUENCY,
USE_FILTER,
MEMBERS_MAX
};
//Useful during file read to test record field name
//against coded field strings prior to parsing field
//data value into struct
const char members[][20] = {"IP","SCAN_FREQUENCY","SCAN_FREQUENCY","USE_FILTER"};
int get_member_index(const char *mem);//prototype helper function
typedef struct {//struct designed to match configuration dataset
char ipAddr[20];
int sPerScan;
double sfreq;
bool filter;
}params_s;
params_s params;//create instance of struct parameters
int main(void)
{
int index = 0;
char line[80] = {0}; //change size bases on known actual file contents
char *tok = NULL;
FILE *fp = fopen(".\\data.txt", "r");
if(fp)
{
while(fgets(line, sizeof(line), fp))
{
//This is where file is parse
index = get_member_index(line);
if(index < 0)
{
//error message to user that file is corrupt or incorrect
//exit program, or provide try again loop to enter another file name
}
else
{
switch(index) {//parse data according to type of each parameter
case IP:
//use strtok or scanf strchr combination to extract value from string
tok = strtok(line, "=\" ");//consume first part of line
tok = strtok(NULL, "=\" ");
strcpy(params.ipAddr, tok);
break;
case SAMPS_PER_SCAN:
tok = strtok(line, "= ");//consume first part of line
tok = strtok(NULL, "= ");//get value
params.sPerScan = atoi(tok);
break;
case SCAN_FREQUENCY:
//construt parse for double
break;
case USE_FILTER:
//construct parse for bool
break;
};
}
}
fclose(fp);//file no longer needed, close it
//use captured struct parameter data throughout code
//via passing as function argument.
}
return 0;
}
//helper function to get index of parameter in data line
int get_member_index(const char *mem)
{
for(int i=0;i<MEMBERS_MAX; i++)
{
if(strstr(mem, members[i]))
{
return i;
}
}
return -1;
}
Upvotes: 1
Reputation: 2114
I would add a .h
file containing defines that you would include where you want to use those constants.
For example, your constants.h would be:
#define IP "169.254.98.220" // The ip used to connect to the LiDAR via Ethernet
#define SAMPLES_PER_SCAN 2400 // number of scans collected in one revolution
#define SCAN_FREQUENCY 35 // Hz, note that a high samples_per_scan can require a low scan_frequency
#define USE_FILTER false // Uses a filter to average 5 incoming scans into one
And you would just add this line to your .c
file where you want to use them, assuming the files are located in the same folder:
#include "constants.h"
Defines are macros that are treated by the pre-compiler so it's basically the same as writing the values everywhere your are using the macro. More info here. And if you want to insert expression like:
#define A_NUMBER_PLUS_ANOTHER (42 + 66)
// Don't forget those : ^ ^
Don't forget to add parenthesis without which your operations with macros could lead to unexpected results.
Upvotes: 1