Skip to content
ikhatri edited this page Mar 6, 2019 · 31 revisions

Welcome to the ConfigurationReader wiki!

Table of Contents

Usage

Importing parameters

In order to use the configuration reader, you'll first need a Lua file with the appropriate variables. This file can have any extension you'd like as long as it obeys Lua formatting. For example:

testString = "This is a string!"
tree = {
  stree = {
    number = 42;
  };
};

Additionally, you'll need to create a header file of your choosing that defines the variables the configuration reader is responsible for reading and updating. You should #include "reader.h" at the top of this header file. Within your header file you can then use the macros to declare variables of the correct type with the name you'd like them to have and their location in the Lua tree.

The macros take the format CONFIG_TYPE(name, key) where name is plaintext that is the name you will use to refer to the variable elsewhere in your program and key is the location of your parameter in the Lua file as a string. The supported types are as follows:

  • Integer - CONFIG_INT(name, key)
  • Unsigned Integer - CONFIG_UINT(name, key)
  • Double - CONFIG_DOUBLE(name, key)
  • Float - CONFIG_FLOAT(name, key)
  • String - CONFIG_STRING(name, key)
  • Eigen::Vector2f - CONFIG_VECTOR2F(name, key)

So for the above file, you could import testString by adding CONFIG_STRING(someString, "testString") to your header file. You can add these macros in any namespace you'd like and the variables will be created in that namespace. Just keep in mind that the macros themselves are defined in the configuration_reader namespace.

If the key has more than one subtree, you should include all of them in the key string separated by .'s. For example CFG_INT(someNum, "tree.stree.number")

For an example file, please see my_vars.h.

Using parameters

To retrieve the value of a specific parameter simply use the global variable that has been created for you. The variable will have the name you specified in the macro CONFIG_TYPE(name, key) with the characters CFG_ appended to it. Below you can see a small code snippet that puts it all together. Additional detail can be found in the example main.cpp file.

// In my_vars.h
#include "reader.h"
CFG_STRING(someString, "testString");

// In your main.cc
#include "my_vars.h"
int main(){
  std::vector<string> files;
  files.push_back("myfile.cfg");
  configuration_reader::CreateDaemon(files); // Spawns a new thread to read in the values in the background
  // You can do something else here and the thread will continue to watch and update the value of CONFIG_someString
  std::cout << configuration_reader::CFG_someString << std::endl; // Will print the value of testString from the lua file
  configuration_reader::Stop(); // To end the daemon thread
  return 0;
}

Implementation Overview

The Configuration Reader program consists of a variety of parts, each of which will have their own section. Here we'll list them and provide a brief description.

  • config_interface - Each file specified in the config_types folder is a class that stores the variable of the specified type. All of them inherit from the config_interface class which ensures that each child type contains functions to retrieve it's key and type.
  • lua_script - The lua_script.cc and lua_script.h files contain a binding between the Lua intrepreter and C++.
  • reader - The reader.cc and reader.h files contain the implementation of the configuration reader functions.
  • main - This file contains an example usage of the configuration reader
  • config.lua - The config.lua and config2.lua files are example files used for testing the program with the example main.cc file.

Implementation Specifics

Reader

The reader.h and reader.cc files contain the main logic of the configuration reader program. A short description of each function is listed below:

reader.h

  • const std::string kDefaultFileName - A constant that stores the default file to be read in if none is provided. Example behavior that utilizes this constant can be see in the main.cc file.
  • CONFIG_TYPE(name, key) - All of the macros of the form CONFIG_TYPE(name, key) are used to initialize a configuration variable. The name argument is for the name that you wish to use to access the variable within the program. It is expanded into a variable name of the type CFG_name. The key is the full path to the variable within the lua file. For usage details, see the importing parameters section.

reader.cc

  • const type& InitType(std::string key) - All the functions of the generic signature InitType take in a key as a string (from the macros defined in reader.h) and create an entry in the global unordered_map that corresponds to the key, with a pointer to a new ConfigInterface object as the value. The function then reads in the value from the Lua file and returns a const reference to the private value data within the child class of the ConfigInterface.
  • void LuaRead(const std::vector<std::string>& files) - The LuaRead() function takes in a list of file names as a vector of strings and imports them into a new Lua state using the LuaScript constructor. It then loops through the keys in the unordered_map and uses the GetType() function to dynamically determine the correct type of each value. Depending on the type, the function will cast the ConfigInterface pointer returned from the unordered_map and call the correct SetVal() function (which loads in the proper value from the Lua state).
  • void HelpText() - A simple function that returns text on how to use the program from the command line. See an example that uses the function in the main.cc file.
  • void InitDaemon(const std::vector<std::string>& files) - The InitDaemon() function contains all the logic for a background thread that calls the LuaRead() every time any of the files passed in as arguments have are modified with an IN_MODIFY event from the inotify API. The function loops indefinitely until the program is ended.
  • void CreateDaemon(const std::vector<std::string>& files) - The CreateDaemon() function spawns and detaches a new daemon thread that executes the InitDaemon() function in the background.

Configuration Interface

The configuration_interface class is a parent type for all the other configuration types. It contains the following functions & data:

  • enum ConfigType - The ConfigType enum is used as a "runtime" typecheck in the LuaRead() function (which is in the file reader.cc). It allows for the pointers to ConfigInterface objecs to be properly cast to their appropiate types at runtime.
  • virtual ConfigType GetType() - The GetType() function returns the type of the object. The ConfigInterface returns a null type. All the classes that inherit from the ConfigInterface provide their own implementation of this function that returns their specified type.
  • std::string GetKey() - The GetKey() function returns a string that corresponds to the key used to hash the value in the unordered_map used by the reader.cc file. The key is also the full path to the variable in the Lua tables (because it should be a unique identifier for each variable).

Configuration Types

The configuration types can all be found in the folder config_types and each class is a wrapper for a specific type that we wish to read in from our Lua file. Every class also inherits from the ConfigInterface type. Each configuration type has the following functions:

  • ConfigType(std::string key_name) - A constructor for the type. Takes in a string for the key as a parameter. Sets the protected std::string key_ variable to key_name.
  • ConfigType(std::string key_name, type upper_bound, type lower_bound) - An overridden constructor for the type. This constructor only exists for numeric types. An upper and lower bound can be supplied and the respective protected data will be initialized.
  • Type& GetVal() - The GetType() function returns a reference to the Type val_ protected data of the class.
  • bool SetVal(LuaScript* script) - This function calls the appropriate Lua binding functions and sets the value of the Type val_ protected variable from the provided Lua state. The function will return false if the value was unable to be read for any reason. If bounds exist, this function will also check that the value retrieved from the Lua state is within those bounds and return false if it is not acceptable.