Skip to content
Diego Perini edited this page May 13, 2015 · 5 revisions

Maybe this is some way to construct our API

int main(int argc, char **argv)
{
  auto defaults = PropertyList{ { "time_to_run", "10" }, { "user_name", "Bob" } };

  using FILE_t = std::unique_ptr<FILE, std::function<int(FILE*)>>;
  FILE_t fromFile(fopen("myConfig.plist"), fclose);

  auto myProgramConfig = ProgramConfig::CreateProgramConfig().
                         CreateProgramConfig(defaults, CONFIG_SCHEMA).
                         CreateProgramConfig(fromFile, CONFIG_SCHEMA).
                         CreateProgramConfig(argc, argv, CMDLINE_SCHEMA);
}

My dream interface (which maybe exposes me as a Python fanboy):

int main(int argc, char **argv)
{
  // quick and dirty setup attribute addition
  auto my_schema = Schema().
                   add_attribute<bool>("debug", false); // second param: default
  // detailed version. Method names stolen from Python optparse 
  my_schema.add_attribute(Attribute<int>("timeout").set_help("Specify timeout in seconds").set_metavar("SECONDS"));
  // hierarchy encoded in string names
  my_schema.add_attribute<string>("network/smtp_server"); // no default: failure if not set
  // let library add its attributes into a sub-hierarchy, i.e. all attribute names get a "some_lib/" prefix
  my_schema.add_below("some_lib", some_lib::get_schema());
  
  // -- verbose way to get data --
  // if we want to re-load a config later we'll have to keep the original cmdline data separate

  // generate reduced schema, select only toplevel entries (don't want everything on commandline)
  auto cmdline_schema = my_schema.slice("*");
  ConfigData cmdline_data = cmdline_configdata(argc, argv, cmdline_schema);

  ConfigData conffile_data = inifile_configdata("some.conf");

  // default values are in schema (which makes sense for doc & keeps everything together)
  ConfigData final_data = fuse_configdata(my_schema, conffile_data, cmdline_data);
  
  // user defined functions to check e.g. parameter compatibility
  my_verify(final_data);
  // delegate checks to some_lib
  some_lib::verify_configdata(final_data.slice("some_lib/*"));

  // ... now final_data can be used. 
}

A useful approach I've been using for quite some time. (excuse my javascript)

Proposal from Diego Perini (see my pull request for implementation):

#include "confoost.hpp" 

// These can be moved to hpp as defaults
using schema = confoost_config<confoost_node>;
using configuration = confoost<schema>;

int main(int argc, char** argv) {
    auto config = configuration();

    // Our parsers are added but not executed
    auto xml_source = config.add_parser( xml_parser<schema>("config.xml") );
    auto json_source = config.add_parser( json_parser<schema>("config.json") );
    auto cli_source = config.add_parser( cli_parser<schema>(argc, argv) ) ;

    // When we retrieve a value, all parsers with invalid cache lazily loads
    auto a_value = config.retrieve<int>("some/path/to/some/int");

    // Accessing the value reads from cache
    a_value = config.retrieve<int>("some/path/to/some/int");

    // We can invalidate our cache anywhere
    xml_source().invalidate();

    // Invalidated sources are reparsed
    a_value = config.retrieve<int>("some/path/to/some/int");

    return 0;
}
Clone this wiki locally