Commit 45c3a15d authored by Richard Schubert's avatar Richard Schubert

Added optional method chained interface

parent a5c55923
# Method Chained libconfig #
**Exception free + header only + method chained + config files**
Provides reading the configuration and defining the configuration specification at once.
### Features ###
* default values
* limits (min/max)
* mandatory/optional values
* help text output for expected config format on specification violation
* capturing and outputting expected configuration specification/defaults
While it is possible to write a config file with this extension using the configuration specification capturing feature, it is not intended to be used as a full fledged config writer.
### Example ###
```C++
#include <libconfig_chained.h>
using namespace std;
using namespace libconfig;
int main(int argc, char **argv)
{
Config cfg;
cfg.readFile("example.cfg");
ChainedSetting cs(cfg.getRoot());
string name = cs["name"].defaultValue("<name>").isMandatory();
string abstract = cs["abstract"].defaultValue("<unknown>");
double longitude = cs["longitude"].min(-180.0).max(180.0).isMandatory();
double latitude = cs["latitude"].min(-90.0).max(90.0).isMandatory();
if (cs.isAnyMandatorySettingMissing())
{
cerr << "Cannot proceed until all mandatory settings are set." << endl;
}
}
```
Console Output:
```sh
'longitude' setting is out of valid bounds (max: 180). Value was: 1200.35
Missing 'latitude' setting in configuration file.
Cannot proceed until all mandatory settings are set.
```
---
### How to integrate into your project ###
1. Link the libconfig++.[lib/la/a] library as usual (see standard use of libconfig++).
* Replace any includes of libconfig.h++ by libconfig_chained.h.
* Use method chained candy as displayed above.
---
Create an issue for any questions or suggestions. Alternatively email me at github [at) hemofektik.de
\ No newline at end of file
cmake_minimum_required(VERSION 2.8)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/")
project (ChainedLibconfigExample)
file(GLOB SOURCES *.cpp *.h ../*.h ../*.md)
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
else()
find_package(libconfig)
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
#set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
include_directories(
${CMAKE_SOURCE_DIR}/../../../lib/
)
if(MSVC)
link_libraries (
${CMAKE_SOURCE_DIR}/../../../Debug/libconfig++.lib
)
else()
link_libraries (
${LIBCONFIG_LIBRARY}
)
endif()
add_executable (
ChainedLibconfigExample
# WIN32 # Only if you don't want the DOS prompt to appear in the background in Windows
# MACOSX_BUNDLE
${SOURCES} # We could've listed the source files here directly instead of using a variable to store them
#${INCLUDES}
)
@echo off
rmdir sln /S/Q
mkdir sln
cd sln
cmake -G "Visual Studio 15 2017" ..
pause
\ No newline at end of file
### Running the examples ###
Expected Console Output:
```C++
<<< Example 1 >>>
'longitude' setting is out of valid bounds (max: 180). Value was: 1200.35
Missing 'latitude' setting in configuration file.
Cannot proceed until all mandatory settings are set.
<<< Example 2 >>>
TITLE AUTHOR PRICE QTY
Treasure Island Robert Louis Stevenson $ 29.99 5
Snow Crash Neal Stephenson $ 9.99 8
<<< Example 3 >>>
'longitude' setting is out of valid bounds (max: 180). Value was: 1200.35
Missing 'latitude' setting in configuration file.
Cannot proceed until all mandatory settings are set.
Expected Config Layout:
// -- begin --
name = "<name>";
abstract = "<unknown>";
longitude = 0.0;
latitude = 0.0;
inventory =
{
movies = ( );
books = (
{
title = "bookXYZ";
} );
};
// --- end ---
```
\ No newline at end of file
// An example configuration file that stores information about a store.
// Basic store information:
name = "Books, Movies & More";
longitude = 1200.345678;
// Store inventory:
inventory =
{
books = ( { title = "Treasure Island";
author = "Robert Louis Stevenson";
price = 29.99;
qty = 5; },
{ title = "Snow Crash";
author = "Neal Stephenson";
price = 9.99;
qty = 8; },
{ title = "Das Kapital";
price = 0.0;
qty = 100000; }
);
movies = ( { title = "Brazil";
media = "DVD";
price = 19.99;
qty = 11; },
{ title = "The City of Lost Children";
media = "DVD";
price = 18.99;
qty = 5; },
{ title = "Memento";
media = "Blu-Ray";
price = 24.99;
qty = 20;
},
{ title = "Howard the Duck"; }
);
};
// Store hours:
hours =
{
mon = { open = 9; close = 18; };
tue = { open = 9; close = 18; };
wed = { open = 9; close = 18; };
thu = { open = 9; close = 18; };
fri = { open = 9; close = 20; };
sat = { open = 9; close = 20; };
sun = { open = 11; close = 16; };
};
lost_bag = ( 1234.5678, "Napkin", [1, 2, 3] );
/* ----------------------------------------------------------------------------
libconfig - A library for processing structured configuration files
libconfig chained - Extension for reading the configuration and defining
the configuration specification at once.
Copyright (C) 2016 Richard Schubert
This file is part of libconfig contributions.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "../libconfig_chained.h"
using namespace std;
using namespace libconfig;
// This example reads basic information from config file
// and reacts on missing mandatory values.
void example1(Config& cfg)
{
ChainedSetting cs(cfg.getRoot());
string name = cs["name"].defaultValue("<name>").isMandatory();
string abstract = cs["abstract"].defaultValue("<unknown>");
double longitude = cs["longitude"].min(-180.0).max(180.0).isMandatory();
double latitude = cs["latitude"].min(-90.0).max(90.0).isMandatory();
if (cs.isAnyMandatorySettingMissing())
{
cerr << "Cannot proceed until all mandatory settings are set." << endl;
return;
}
// from here on all read config values are valid
}
/* ----------------------------------------------------------------------------
libconfig - A library for processing structured configuration files
libconfig chained - Extension for reading the configuration and defining
the configuration specification at once.
Copyright (C) 2016 Richard Schubert
This file is part of libconfig contributions.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "../libconfig_chained.h"
using namespace std;
using namespace libconfig;
// This example reads complex types from config file using method chains.
void example2(Config& cfg)
{
ChainedSetting cs(cfg.getRoot());
auto books = cs["inventory"]["books"];
if (!books.exists())
{
cerr << "No book section available." << endl;
return;
}
cout << setw(30) << left << "TITLE" << " "
<< setw(30) << left << "AUTHOR" << " "
<< setw(6) << left << "PRICE" << " "
<< "QTY"
<< endl;
const int count = books.getLength();
for(int i = 0; i < count; ++i)
{
auto book = books[i];
string title = book["title"];
string author = book["author"];
double price = book["price"].min(0.0);
int qty = book["qty"].min(0);
// Only output the record if all of the expected fields are present.
if(book.isAnySettingMissing()) continue;
cout << setw(30) << left << title << " "
<< setw(30) << left << author << " "
<< '$' << setw(6) << right << price << " "
<< qty
<< endl;
}
}
/* ----------------------------------------------------------------------------
libconfig - A library for processing structured configuration files
libconfig chained - Extension for reading the configuration and defining
the configuration specification at once.
Copyright (C) 2016 Richard Schubert
This file is part of libconfig contributions.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include "../libconfig_chained.h"
using namespace std;
using namespace libconfig;
// This example reads basic information from config file
// and prints out the expected configuration specification.
void example3(Config& cfg)
{
ChainedSetting cs(cfg.getRoot());
Config tmpCfgSpec;
tmpCfgSpec.setOptions(Config::OptionOpenBraceOnSeparateLine | Config::OptionSemicolonSeparators);
tmpCfgSpec.setTabWidth(3);
cs.captureExpectedSpecification(&tmpCfgSpec);
string name = cs["name"].defaultValue("<name>").isMandatory();
string abstract = cs["abstract"].defaultValue("<unknown>");
double longitude = cs["longitude"].min(-180.0).max(180.0).isMandatory();
double latitude = cs["latitude"].min(-90.0).max(90.0).isMandatory();
ChainedSetting movie0 = cs["inventory"]["movies"][0];
string book0tile = cs["inventory"]["books"][0]["title"].defaultValue("bookXYZ");
if (cs.isAnyMandatorySettingMissing())
{
cerr << "Cannot proceed until all mandatory settings are set." << endl << endl;
auto cfgSpec = cs.getCapturedSpecification("tmpCfgSpec.cfg");
cerr << "Expected Config Layout: " << endl << endl;
cerr << "// -- begin --" << endl;
cerr << cfgSpec;
cerr << "// --- end ---" << endl << endl;
return;
}
// from here on all read config values are valid
}
/* ----------------------------------------------------------------------------
libconfig - A library for processing structured configuration files
libconfig chained - Extension for reading the configuration and defining
the configuration specification at once.
Copyright (C) 2016 Richard Schubert
This file is part of libconfig contributions.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------
*/
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <libconfig.h++>
using namespace std;
using namespace libconfig;
// This is the main entry point for all examples.
// The examples will be called consecutively.
void example1(Config& cfg);
void example2(Config& cfg);
void example3(Config& cfg);
int main(int argc, char **argv)
{
Config cfg;
// Read the file. If there is an error, report it and exit.
try
{
cfg.readFile("../example.cfg");
}
catch(const FileIOException&)
{
std::cerr << "I/O error while reading file. " << std::endl;
return(EXIT_FAILURE);
}
catch(const ParseException& pex)
{
std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine()
<< " - " << pex.getError() << std::endl;
return(EXIT_FAILURE);
}
cout << endl << endl << "<<< Example 1 >>>" << endl << endl;
example1(cfg);
cout << endl << endl << "<<< Example 2 >>>" << endl << endl;
example2(cfg);
cout << endl << endl << "<<< Example 3 >>>" << endl << endl;
example3(cfg);
return(EXIT_SUCCESS);
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment