init-from-input-file
allows C and C++ variables to be initialized from an input text file, consequently eliminating the need to recompile source code merely due to a changed initialization value. For example, consider a simulation of an air-launched missile. Instead of writing ...
const double kAltitudeInitial = 10000.0; /* meters (m) */
where the right-hand side (i.e., floating constant / floating-point literal) must be updated for different scenarios, requiring recompilation of the source code after every update, write ...
double altitudeInitial = 0.0; /* meters (m) */
const bool kSuccessFlag = initFromInputFile("missile.inp", "double", "altInit", &altitudeInitial);
where src/init_from_input_file.h
must be #include
d. Breaking down the above, the input text file missile.inp
is searched for the key altInit
, and the value corresponding to that key is assigned to altitudeInitial
(passed by reference), which is a double
; and, assuming success, true
is returned by initFromInputFile
. A relevant snippet of missile.inp
could be ...
latInit 33.2385 # Initial latitude in degrees (deg)
lonInit -106.3464 # Initial longitude in degrees (deg)
altInit 10000.0 # Initial altitude in meters (m)
- The extension of the input text file is insignificant.
- Only one key-value pair is permitted per line.
- Each key must exactly match the
kKeyName
argument passed toinitFromInputFile
. - Each key must be left-aligned, i.e., must not be preceded by white space (unless the
kKeyName
argument passed toinitFromInputFile
contains the same white space, but don't do that). - Each key must be unique. Otherwise, only the first instance--with respect to reading from top to bottom, left to right--will be found by the parser.
- There must be at least one space or (horizontal) tab between each key and its corresponding value.
- Boolean values must be either
true
orfalse
. (1
and0
, respectively, are not supported.) - String values must be contained within double quotes and must not contain a double quotation mark. For example, ...
- For the string value
Hey, Bob!
, the file must list"Hey, Bob!"
after (and on the same line as) the corresponding key She said, 'Hey, Bob!'
(single quotes) is an acceptable string value, whereasShe said, "Hey, Bob!"
(double quotes) is not
- For the string value
- No specific character (sequence) denotes the start of a comment, as the parser simply searches for the specified key and then its corresponding value (starting from the character after the last one of the found key). Consequently, a comment can be anything--as long as, starting from the beginning of the line on which the comment begins, (part of) it doesn't match any of the
kKeyName
arguments passed toinitFromInputFile
. For example, ...
This is a comment unless one of the specified keys matches (part of) the text of this line, starting from the beginning--e.g., if "This is a comment" is a specified key. HOWEVER, it's best practice to start all comments with a consistent, non-letter (and non-number) identifier.
# This is almost certainly a comment (because no key should start with "#"; you CAN start a key with "#", but don't)
/* This is also almost certainly a comment (because no key should start with "/*"; again, you CAN start a key with "/*", but don't) */
The following table lists the supported kVarType
arguments that can be passed to initFromInputFile
and the variable type corresponding to each:
Argument | Type |
---|---|
"char" |
char |
"schar" |
signed char |
"uchar" |
unsigned char |
"short" |
(signed ) short (int ) |
"ushort" |
unsigned short (int ) |
"int" |
(signed ) int |
"uint" |
unsigned int |
"long" |
(signed ) long (int ) |
"ulong" |
unsigned long (int ) |
"float" |
float |
"double" |
double |
"ldouble" |
long double |
"bool" |
bool |
"string" |
char* |
- Download the source code from GitHub into the appropriate directory on your machine.
#include
the header filesrc/init_from_input_file.h
in the C and/or C++ source code you desire to use the aforementioned functionality in.- Call
initFromInputFile
--which is declared in the latter header file--with the following arguments, in order:const char* kFileName
: The relative or absolute path of the input text fileconst char* kVarType
: The type of the to-be-initialized variable (discussed in the previous section)const char* kKeyName
: The name of the key to search the input text file for, corresponding to the value to initialize the to-be-initialized variable withvoid* pVar
: The address of the to-be-initialized variable
- Incorporate the source files
src/init_from_input_file.c
andsrc/init_tools.c
into the build. (The former#include
ssrc/init_tools.h
.)
One of the primary objectives of this project was ensuring 1) compatibility with C and C++ and 2) backward compatibility. Hence, this tool is written in ANSI C and compatible with C++17.
Due to the nature of C, as well as various deliberate design decisions made for the sakes of both efficiency and simplicity, this tool grants the user power, which must be accompanied by responsibility. That being said, some noteworthy user responsibilities are as follows:
- Ensure the type of the to-be-initialized variable is capable of accurately storing the corresponding value listed in the input text file. For example, concerning C code, the value corresponding to an
int
must be inclusively betweenINT_MIN
andINT_MAX
, which are machine-dependent and#define
d inlimits.h
. (Otherwise, integer overflow--which may lead to undefined behavior--or loss of precision will occur.) - Regarding strings, ensure sufficient memory is allocated for the to-be-initialized variable based on the length of the corresponding value listed in the input text file. For example, the variable corresponding to the value
Hey, Bob!
must be able to hold (at least) tenchar
s--including the null terminator--and, therefore, be defined (in C code specifically) as eitherchar str[10]
orchar* str = malloc(10 * sizeof(char))
. [Otherwise, you'll end up "touching" memory you shouldn't and (hopefully!) be presented with an error.] - As previously mentioned, ensure string values listed in the input text file both start and end with a double quotation mark. [If the opening one isn't included, the first
char
of the value will be skipped; and, if the closing one isn't included, you'll likely end up "touching" memory you shouldn't and definitely encounter aninitFromInputFile
failure.] - Check
initFromInputFile
's return value; don't just assume it worked. There are many things--both machine and user faults (e.g., a memory shortage and an inconspicuous typo, respectively)--that can cause it to fail.
If the macro PRINT_ERRORS
is #define
d, then a message containing the following information is written to the output stream stderr
--which is normally directed to the screen by default--whenever an error is encountered:
- Name of the source file in which the error occurred
- Line number on which it occurred
kFileName
argument passed toinitFromInputFile
kKeyName
argument passed toinitFromInputFile
Detailed error messages greatly help with debugging. If you decide to not always #define PRINT_ERRORS
, you should absolutely do so if initFromInputFile
has returned false
.
Every function that's unique to this tool--i.e., not part of the standard library--has been thoroughly unit tested using GoogleTest (framework) paired with CMake (build system).
cd
into the test
directory and execute ./run_tests.sh
, which triggers all the run_test.sh
scripts located in the following subdirectories:
init_from_input_file/non_static_fxns
init_from_input_file/static_fxns
init_tools/non_static_fxns/other
init_tools/non_static_fxns/printError
init_tools/static_fxns
* Only works on Linux
- Add preprocessor directives that allow the tests to run with or without
PRINT_ERRORS
#define
d- Currently, all but one of the unit tests only work if
PRINT_ERRORS
is not#define
d init_tools_non_static_fxns_printError_test
inherently requiresPRINT_ERRORS
to be#define
d
- Currently, all but one of the unit tests only work if
- Try to use test fixtures to avoid code duplication
- Regarding the
run_test.sh
scripts, figure out how to get (or construct) the names of the.cpp
files so they don't have to be hard-coded