Skip to content

Simple/clean means to read and access configuration from C++.

Notifications You must be signed in to change notification settings

pbannister/pod_ini

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

POD configuration

Aim of this exercise is to ingest configuration files in (Windows-like) .ini-style (or Java-style .properties) format. In past, I have written code to ingest either (interchangably) using reflection to map into native Java (or Javascript, or Python) data structures. Using C/C++ does not allow the same.

Flip this around.

Contents of configuration files is only meaningful if ingested by source code. So source code defines what should be in a configuration file. Configuration read into POD (simple direct C++ data structure) is simplest to access.

Put differently, the aim here:

  1. Manually create an example INI (or properties) file with default values.
  2. Generate C++ code to read that INI/properties file into a POD.
  3. Access to settings is then simple POD (plain-old-data) structure fields.

Given an example INI file:

[foo]
bar=1 
zot=abc

[panic]
when=sometimes

Configuration data becomes a simple class:

#pragma once
// Generated date: Sun Apr 27 21:52:36 2025
// Do not edit this file.

extern struct configuration_pod_t {
    ~configuration_pod_t();
    struct context_o;
    struct context_o* p_context;
    bool pod_load(const char*);
    bool pod_save(const char*);
    struct configuration_pod_foo_t {
        const char* v_bar;
        const char* v_zot;
    } s_foo;
    struct configuration_pod_panic_t {
        const char* v_when;
    } s_panic;
} configuration_pod;

Access to settings becomes:

// Once
configuration_pod.pod_load("settings.ini");
// Later
::printf("When should we panic: %s\n", configuration_pod.s_panic.v_when);

Structured storage and common representation

INI files are grouped by [section] with associated key=value assignments.

PROPERTIES files are both more and less structured, with just us.bannister.key=value assignments. Keys are structured like (reversed) Internet host names (a Java convention). In languages that support reflection, those dotted-names can be mapped into native data structures.

You can unify both formats by treating INI files as yielding section.key=value.

Conversely, PROPERTIES can become [us.bannister] and key=value. So a single reader can accept both formats.

POD racer

(Yeh. I just liked the name.)

Ingests structured configuration files (either/both ini or properties). Ingests the entire configuration file in a single read(), then optimally splits key/values out of that block.

End is to generate C++ structures, then code to read into those structures.

Note there is no conversion from strings to other types. This is intentional.

POD valid

Configuration files are just named bundles of strings. Validation is needed to ensure a string represents an acceptable value. The temptation is to bundle validation with configuration. This is a mistake. Validation is an independent concern.

The provided "pod/pod_valid.h" include with pod_valid::validator_o class is meant as an example. This is meant as an example pattern - which you extend to suit your application. (Note you might follow a similar pattern to validate incoming parameters on a web API.)

Usage is simple and clean.

auto v = pod_valid::validator_o(configuration_pod.s_panic.v_when).as_integer().in_range(1, 10).limit_range(2, 6);
::printf("in: %s value: %li valid: %u\n", configuration_pod.s_panic.v_when, v.value, v.valid);

You can and should extend the above pattern to suit your use.

Structured naming

As an aside, my approach to naming might be unfamiliar.

With the usual CamelCase naming (and yes, I date before that was new), in a smart IDE, the grouping of members is a bit random. This bothered me.

Over time, I noticed that classes tended to have facets. When those facets were separable, they might turn into interfaces. But rather often the facets of an object were more dependent, and not truly separable from the class. (Should mention "mixins" from the Lisp world, but pre-web so links are scarce.)

You might also note that I use a minimalist version of "Hungarian" notation. (And yes, I spent time in Win16, where this got a bit extreme, of need.)

The minimalist end is:

  • Method names with the most significant bit first. Something like: facet_subfacet_operation()
  • Class names end with "_o".
  • Object names start with "o_" (sometimes).
  • Pointer names start with "p_".
  • Index names start with "i_".
  • Count names start with "n_".
  • String names start with "s_" (mostly).
  • Array names start with "a_" (sometimes).

Not remotely religious about any of the above, but works for me.

About

Simple/clean means to read and access configuration from C++.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published