Unverified Commit 8a361a56 authored by Mark Lindner's avatar Mark Lindner Committed by GitHub

Merge pull request #95 from Hemofektik/master

Added optional method chained interface
parents a5c55923 45c3a15d
# 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);
}
/* ----------------------------------------------------------------------------
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/>.
----------------------------------------------------------------------------
*/
#pragma once
#ifndef _CHAINED_LIBCONFIG_H_
#define _CHAINED_LIBCONFIG_H_
#include <libconfig.h++>
#include <cassert>
#include <fstream>
#include <sstream>
#include <iostream>
namespace libconfig
{
class ChainedSetting
{
struct Variant
{
private:
bool isSet;
Setting::Type type;
bool value_bool;
int64_t value_int;
double value_float;
std::string value_string;
public:
Variant()
: isSet(false)
, type(Setting::TypeNone)
{
}
Variant(bool value)
{
value_bool = value;
isSet = true;
type = Setting::TypeBoolean;
}
Variant(int32_t value)
{
value_int = value;
isSet = true;
type = Setting::TypeInt;
}
Variant(int64_t value)
{
value_int = value;
isSet = true;
type = Setting::TypeInt64;
}
Variant(double value)
{
value_float = value;
isSet = true;
type = Setting::TypeFloat;
}
Variant(std::string& value)
{
value_string = value;
isSet = true;
type = Setting::TypeString;
}
Variant(const char* value)
{
value_string = value;
isSet = true;
type = Setting::TypeString;
}
operator bool() const { return value_bool; }
operator int() const { return (int)value_int; }
operator unsigned int() const { return (unsigned int)value_int; }
operator long() const { return (long)value_int; }
operator unsigned long() const { return (unsigned long)value_int; }
operator long long() const { return (long long)value_int; }
operator unsigned long long() const { return (unsigned long long)value_int; }
operator double() const { return value_float; }
operator float() const { return (float)value_float; }
operator std::string() const { return value_string; }
const bool IsSet() const
{
return isSet;
}
const Setting::Type GetType() const
{
return type;
}
};
public:
// Starting point for method chained libconfig.
// Pass a custom ostream to intercept any error messages (useful for Applications with UI).
ChainedSetting(Setting& setting, std::ostream& err = std::cerr)
: name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : ""))
, index(setting.getIndex())
, parent(NULL)
, setting(&setting)
, err(err)
, isSettingMandatory(false)
, anySettingIsMissing(false)
, anyMandatorySettingIsMissing(false)
, capturedSpecification(NULL)
, capturedSetting(NULL)
{
}
// Starts capturing any configuration readings into the temporary config object.
void captureExpectedSpecification(Config* temporaryConfigSpecification)
{
capturedSpecification = temporaryConfigSpecification;
capturedSetting = &capturedSpecification->getRoot();
}
// Returns the captured configuration specification,
// premised captureExpectedSpecification() was called earlier.
// The path parameter is needed to write the configuration
// to disk before it can be read into a usable string.
std::string getCapturedSpecification(const std::string& tempFilePath)
{
try
{
capturedSpecification->writeFile(tempFilePath.c_str());
}
catch (const FileIOException&)
{
err << "I/O error while writing temporary setting file: " << tempFilePath << std::endl;
return "";
}
std::ifstream t(tempFilePath);
if (!t.is_open())
{
err << "I/O error while reading temporary setting file: " << tempFilePath << std::endl;
return "";
}
std::stringstream buffer;
buffer << t.rdbuf();
capturedSpecification = NULL;
return buffer.str();
}
// Defines the default value for this setting if missing from config file.
template<typename T>
ChainedSetting& defaultValue(T defaultValue)
{
defaultVal = defaultValue;
return *this;
}
// Defines the inclusive minimum value for this setting.
// A lesser value set in a configuration file will be clamped to this limit.
template<typename T>
ChainedSetting& min(T min)
{
minVal = min;
return *this;
}
// Defines the inclusive maximum value for this setting.
// A greater value set in a configuration file will be clamped to this limit.
template<typename T>
ChainedSetting& max(T max)
{
maxVal = max;
return *this;
}
// Defines this setting to be mandatory.
// Any mandatory value missing in the configuration file will raise an error.
// Use isAnyMandatorySettingMissing() to check for any violations.
ChainedSetting& isMandatory()
{
isSettingMandatory = true;
if (parent) parent->isMandatory();
return *this;
}
template<typename T>
operator T()
{
auto requestedType = GetRequestedType<T>();
CheckType(defaultVal, requestedType);
CheckType(minVal, requestedType);
CheckType(maxVal, requestedType);
CaptureSetting<T>(requestedType);
if (!setting)
{
if (isSettingMandatory)
{
AlertMandatorySettingMissing<T>();
}
PropagateAnySettingIsMissing();
return GetDefaultValue<T>();
}
try
{
T value = *setting;
if (minVal.IsSet())
{
T min = minVal;
if (value < min)
{
err << "'" << setting->getPath() << "' setting is out of valid bounds (min: " << min << "). Value was: " << value << std::endl;
value = min;
}
}
if (maxVal.IsSet())
{
T max = maxVal;
if (value > max)
{
err << "'" << setting->getPath() << "' setting is out of valid bounds (max: " << max << "). Value was: " << value << std::endl;
value = max;
}
}
return value;
}
catch (const SettingTypeException& tex)
{
err << "'" << tex.getPath() << "' setting is of wrong type." << std::endl;
}
return GetDefaultValue<T>();
}
ChainedSetting operator[](const char *name)
{
CaptureSetting<Setting>(Setting::TypeGroup);
if (!setting)
{
return ChainedSetting(name, this);
}
if(setting->exists(name))
{
return ChainedSetting((*setting)[name], this);
}
else
{
return ChainedSetting(name, this);
}
}
inline ChainedSetting operator[](const std::string &name)
{
return(operator[](name.c_str()));
}
ChainedSetting operator[](int index)
{
// This could also be an TypeArray but we cannot be sure here.
// By using TypeList we ensure it will always work.
CaptureSetting<Setting>(Setting::TypeList);
if (!setting)
{
return ChainedSetting(index, this);
}
if (index >= 0 && index < setting->getLength())
{
return ChainedSetting((*setting)[index], this);
}
else
{
return ChainedSetting(index, this);
}
}
int getLength() const
{
return setting ? setting->getLength() : 0;
}
Setting::Type getType() const
{
return setting ? setting->getType() : Setting::TypeNone;
}
// Indicates whether this setting is present in the read configuration file.
bool exists() const
{
return setting != NULL;
}
bool isAnyMandatorySettingMissing() const
{
return anyMandatorySettingIsMissing;
}
bool isAnySettingMissing() const
{
return anySettingIsMissing;
}
void clearAnySettingMissingFlag()
{
anySettingIsMissing = false;
}
private:
ChainedSetting(Setting& setting, ChainedSetting* parent)
: name(setting.isRoot() ? "<root>" : (setting.getName() ? setting.getName() : ""))
, index(setting.getIndex())
, parent(parent)
, setting(&setting)
, err(parent->err)
, isSettingMandatory(false)
, anySettingIsMissing(false)
, anyMandatorySettingIsMissing(false)
, capturedSpecification(NULL)
, capturedSetting(NULL)
{
}
ChainedSetting(const std::string& name, ChainedSetting* parent)
: name(name)
, index(-1)
, parent(parent)
, setting(NULL)
, err(parent->err)
, isSettingMandatory(false)
, anySettingIsMissing(true)
, anyMandatorySettingIsMissing(false)
, capturedSpecification(NULL)
, capturedSetting(NULL)
{
}
ChainedSetting(int index, ChainedSetting* parent)
: name("")
, index(index)
, parent(parent)
, setting(NULL)
, err(parent->err)
, isSettingMandatory(false)
, anySettingIsMissing(true)
, anyMandatorySettingIsMissing(false)
, capturedSpecification(NULL)
, capturedSetting(NULL)
{
}
template<typename T>
void ConditionalSetCapturedDefaultValue()
{
*capturedSetting = GetDefaultValue<T>();
}
template<typename T>
void CaptureSetting(Setting::Type type)
{
if (!capturedSetting && parent && parent->capturedSetting)
{
if (name.length() > 0)
{
if (!parent->capturedSetting->exists(name))
{
capturedSetting = &parent->capturedSetting->add(name, type);
}
else
{
capturedSetting = &(*parent->capturedSetting)[name.c_str()];
}
}
else
{
if (index < parent->capturedSetting->getLength())
{
capturedSetting = &(*parent->capturedSetting)[0];
}
else
{
assert(index == parent->capturedSetting->getLength()); // you requested an index while omitting at least one of its previous siblings
capturedSetting = &parent->capturedSetting->add(type);
}
}
ConditionalSetCapturedDefaultValue<T>();
}
}
std::string GetPath() const
{
if (setting)
{
return setting->getPath();
}
std::string path = (name.length() > 0) ? name : "[" + std::to_string(index) + "]";
if (parent)
{
auto parentPath = parent->GetPath();
return (parentPath.length() > 0) ? (parentPath + ((name.length() == 0) ? "" : ".") + path) : path;
}
return path;
}
void PropagateAnySettingIsMissing()
{
anySettingIsMissing = true;
if (parent)
{
parent->PropagateAnySettingIsMissing();
}
}
void PropagateAnyMandatorySettingIsMissing()
{
anyMandatorySettingIsMissing = true;
if (parent)
{
parent->PropagateAnyMandatorySettingIsMissing();
}
}
template<typename T>
void AlertMandatorySettingMissing()
{
PropagateAnyMandatorySettingIsMissing();
err << "Missing '" << GetPath() << "' setting in configuration file." << std::endl;
}
template<typename T>
T GetUnsetDefaultValue() const
{
return (T)0;
}
template<typename T>
T GetDefaultValue() const
{
if (defaultVal.IsSet())
{
return (T)defaultVal;
}
return GetUnsetDefaultValue<T>();
}
template<typename T>
Setting::Type GetRequestedType() const
{
// TODO @ Hemofektik: Check whether the outcommented line is still needed. static_assert(false) is checked on compile time and, well, asserts :)
// static_assert(false, "should never happen, unless you requested an unsupported type");
return Setting::TypeNone;
}
void CheckType(const Variant& variant, Setting::Type expectedType) const
{
if (!variant.IsSet()) return;
if(expectedType != variant.GetType())
{
assert(false); // fix your code to match the whole chain of this setting to one single type!
err << "'" << GetPath() << "' setting limits or default value is of incompatible type." << std::endl;
}
}
std::string name;
int index;
ChainedSetting* parent;
Setting* setting;
std::ostream& err;
Variant defaultVal;
Variant minVal;
Variant maxVal;
bool isSettingMandatory;
bool anySettingIsMissing;
bool anyMandatorySettingIsMissing;
Config* capturedSpecification;
Setting* capturedSetting;
};
template<>
inline
void ChainedSetting::ConditionalSetCapturedDefaultValue<Setting>() { }
template<>
inline
std::string ChainedSetting::GetUnsetDefaultValue() const
{
return "";
}
template<> inline Setting::Type ChainedSetting::GetRequestedType<int8_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<uint8_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<int16_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<uint16_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<int32_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<uint32_t>() const { return Setting::TypeInt; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<int64_t>() const { return Setting::TypeInt64; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<uint64_t>() const { return Setting::TypeInt64; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<float>() const { return Setting::TypeFloat; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<double>() const { return Setting::TypeFloat; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<std::string>() const { return Setting::TypeString; }
template<> inline Setting::Type ChainedSetting::GetRequestedType<bool>() const { return Setting::TypeBoolean; }
}
#endif
\ No newline at end of file
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