Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Raspberry-Pi

A Light-weight, Thread Safe Properties Class for C++ Embedded Projects

4.60/5 (7 votes)
16 Dec 2015CPOL4 min read 15.6K   38  
INI-style properties for C++ Embedded/Applications on Linux

Introduction

Embedded and simple applications need a convenient place to store properties, and INI style files are great for this.

Background

This is a common problem with embedded applications; there are many ways one can store discrete pieces of configuration information, but code fragmentation is a constant worry of mine. After writing several projects that required non system-specific methods of storing configuration information for the application, I landed on INI files; those old Windows 3.1 files that were so easy to edit and configure in Notepad if you needed to do that. And just like those old ini files, I wanted to give the framework the ability to retain comments in those files.

Comments are important, I don't need to go into detail on that point. And the formatting of the file itself shouldn't be touched. Also, there's really no reason for the framework to be complicated. Beyond thread safety and the STL, there's really no reason for additional libraries, third party tools, etc. All that is required is that the project employ C++ 11 and the STL. This can be an issue in some cases, not all embedded projects use C++, preferring plain C, but in those cases where C++ is appropriate, which I do a lot of, this class might be the way to go.

Using the Code

I usually start with a properties file, I put in it the information I would like to see processed; what exactly does my application need? Here's a sample.

C++
# a comment
; also a comment

entry1=value
entry2="a string"
random=1234

In detail, what we have here are a couple of character prefixes (# & ;) that denote comments follow, in the case of an existing properties file, they should be retained, but ignored, including the separating line feed. Then, follow a trio of key - value pairs. One is a simple string with no spaces. Then a string with spacing, to keep things simple for the class, I decided that a string with spaces needed to be surrounded by quotes. I don't think this should be too much of a hardship for an integrator. Then a numeric value, can be a floating point value as well. This pretty much encompasses the data requirements I had for this class. The file can contain any number of linefeeds, comments, values. But this class is for small configurations, files megabytes in size will work in theory, but I would consider a beefier solution than this one.

Comments can be "nested", for lack of a better term, you also certainly can add separating lines of comment characters like this:

C++
########################################################

...if that's your thing.

Getting and Setting key - value Pairs

Now we get to the code. After checking for the existence of the file and creating it if necessary (this is covered in the code sample), we can look up a key like this:

C++
float f;
try {
    unique_ptr<properties> p(new Properties(MODE::PRP_READWRITE));
    // the next statement, if enabled, would throw a "Key doesn't exist" exception
    // because we didn't specify a properties file. No file, no properties db.
    // cout << "The value of pi is approximately " << p->Get("p1", 4.0) << endl;
    p->OpenPropFile(PRPFILE);
    // now we can look for a key in the file
    f = p->Get("pi", static_cast<float>(4.0));

First (using a C++ 11 smart pointer), we instantiate the class passing a read/write parameter. The class can be instantiated with a read/write mode, read-only may be useful in some cases within the process. Next, we open the file using plain file system semantics.

I've also including a custom exception class particular to this class, again, see the implementation for details. Possible expect ions include properties file not found, mal-formed key value pairs, duplicate key, just the problems one might encounter in such a class.

Then we look for a key, in this example a float. One thing to note here is I love the idea of a default value in the case that the key isn't found, in this case the key is present, so we have the expected output:

The value of pi is approximately 3.14159

See the driver class (main.cc) for details. All values are represented as plain text so we use a cast template to set the type of the data.

Setting a value is as simple:

C++
p->Set("anewvalue", static_cast<unsigned int>(4049860985));
p->dump();

That dump function simply echos all the keys with their values to stdout, a convenience for debugging.

Lastly, a few notes: The make file contains the particulars of how the class is generated. std=c++11 is necessary, and if you are incorporating the class in a multi-threaded application, be sure to pass the "_REENTRANT macro to the compiler (also shown in the makefile.) ABOUT that make file, it's a recursive one, I use that make pattern whenever I develop new code, I certainly don't suggest it for a production project. Since this by no means is a stand-alone application, I don't see the harm. You'll certainly want to use the traditional autoconf tool chain or cmake in your own application.

Another thing I'd change is I'd make the library more re-entrant by adding method access to the hash table, directly accessing the table and simply surrounding it with locks probably isn't the way to go here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)