Add *Property methods to replace *Value methods, simplify options helper

This commit is contained in:
Alexey Rybalchenko 2019-05-14 14:43:53 +02:00 committed by Dennis Klein
parent 5646d531f3
commit fe241fe9ee
4 changed files with 200 additions and 262 deletions

View File

@ -14,25 +14,123 @@
#include "FairMQLogger.h" #include "FairMQLogger.h"
#include "FairMQProgOptions.h" #include "FairMQProgOptions.h"
#include "FairProgOptionsHelper.h"
#include "FairMQParser.h" #include "FairMQParser.h"
#include "FairMQSuboptParser.h" #include "FairMQSuboptParser.h"
#include "tools/Unique.h" #include "tools/Unique.h"
#include <boost/filesystem.hpp>
#include <boost/any.hpp>
#include <boost/algorithm/string.hpp> // join/split #include <boost/algorithm/string.hpp> // join/split
#include <boost/core/demangle.hpp>
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <exception> #include <exception>
#include <typeinfo>
using namespace std; using namespace std;
using namespace fair::mq; using namespace fair::mq;
using boost::any_cast;
namespace po = boost::program_options; namespace po = boost::program_options;
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
for (unsigned int i = 0; i < v.size(); ++i) {
os << v[i];
if (i != v.size() - 1) {
os << ", ";
}
}
return os;
}
template<typename T>
pair<string, string> getString(const boost::any& v, const string& label)
{
return { to_string(any_cast<T>(v)), label };
}
template<typename T>
pair<string, string> getStringPair(const boost::any& v, const string& label)
{
stringstream ss;
ss << any_cast<T>(v);
return { ss.str(), label };
}
unordered_map<type_index, pair<string, string>(*)(const boost::any&)> FairMQProgOptions::fValInfos = {
{ type_index(typeid(string)), [](const boost::any& v) { return pair<string, string>{ any_cast<string>(v), "<string>" }; } },
{ type_index(typeid(int)), [](const boost::any& v) { return getString<int>(v, "<int>"); } },
{ type_index(typeid(size_t)), [](const boost::any& v) { return getString<size_t>(v, "<size_t>"); } },
{ type_index(typeid(uint32_t)), [](const boost::any& v) { return getString<uint32_t>(v, "<uint32_t>"); } },
{ type_index(typeid(uint64_t)), [](const boost::any& v) { return getString<uint64_t>(v, "<uint64_t>"); } },
{ type_index(typeid(long)), [](const boost::any& v) { return getString<long>(v, "<long>"); } },
{ type_index(typeid(long long)), [](const boost::any& v) { return getString<long long>(v, "<long long>"); } },
{ type_index(typeid(unsigned)), [](const boost::any& v) { return getString<unsigned>(v, "<unsigned>"); } },
{ type_index(typeid(unsigned long)), [](const boost::any& v) { return getString<unsigned long>(v, "<unsigned long>"); } },
{ type_index(typeid(unsigned long long)), [](const boost::any& v) { return getString<unsigned long long>(v, "<unsigned long long>"); } },
{ type_index(typeid(float)), [](const boost::any& v) { return getString<float>(v, "<float>"); } },
{ type_index(typeid(double)), [](const boost::any& v) { return getString<double>(v, "<double>"); } },
{ type_index(typeid(long double)), [](const boost::any& v) { return getString<long double>(v, "<long double>"); } },
{ type_index(typeid(bool)), [](const boost::any& v) { stringstream ss; ss << boolalpha << any_cast<bool>(v); return pair<string, string>{ ss.str(), "<bool>" }; } },
{ type_index(typeid(vector<bool>)), [](const boost::any& v) { stringstream ss; ss << boolalpha << any_cast<vector<bool>>(v); return pair<string, string>{ ss.str(), "<vector<bool>>" }; } },
{ type_index(typeid(boost::filesystem::path)), [](const boost::any& v) { return getStringPair<boost::filesystem::path>(v, "<boost::filesystem::path>"); } },
{ type_index(typeid(vector<string>)), [](const boost::any& v) { return getStringPair<vector<string>>(v, "<vector<string>>"); } },
{ type_index(typeid(vector<int>)), [](const boost::any& v) { return getStringPair<vector<int>>(v, "<vector<int>>"); } },
{ type_index(typeid(vector<size_t>)), [](const boost::any& v) { return getStringPair<vector<size_t>>(v, "<vector<size_t>>"); } },
{ type_index(typeid(vector<uint32_t>)), [](const boost::any& v) { return getStringPair<vector<uint32_t>>(v, "<vector<uint32_t>>"); } },
{ type_index(typeid(vector<uint64_t>)), [](const boost::any& v) { return getStringPair<vector<uint64_t>>(v, "<vector<uint64_t>>"); } },
{ type_index(typeid(vector<long>)), [](const boost::any& v) { return getStringPair<vector<long>>(v, "<vector<long>>"); } },
{ type_index(typeid(vector<long long>)), [](const boost::any& v) { return getStringPair<vector<long long>>(v, "<vector<long long>>"); } },
{ type_index(typeid(vector<unsigned>)), [](const boost::any& v) { return getStringPair<vector<unsigned>>(v, "<vector<unsigned>>"); } },
{ type_index(typeid(vector<unsigned long>)), [](const boost::any& v) { return getStringPair<vector<unsigned long>>(v, "<vector<unsigned long>>"); } },
{ type_index(typeid(vector<unsigned long long>)), [](const boost::any& v) { return getStringPair<vector<unsigned long long>>(v, "<vector<unsigned long long>>"); } },
{ type_index(typeid(vector<float>)), [](const boost::any& v) { return getStringPair<vector<float>>(v, "<vector<float>>"); } },
{ type_index(typeid(vector<double>)), [](const boost::any& v) { return getStringPair<vector<double>>(v, "<vector<double>>"); } },
{ type_index(typeid(vector<long double>)), [](const boost::any& v) { return getStringPair<vector<long double>>(v, "<vector<long double>>"); } },
{ type_index(typeid(vector<boost::filesystem::path>)), [](const boost::any& v) { return getStringPair<vector<boost::filesystem::path>>(v, "<vector<boost::filesystem::path>>"); } },
};
namespace fair
{
namespace mq
{
ValInfo ConvertVarValToValInfo(const po::variable_value& v)
{
string origin;
if (v.defaulted()) {
origin = "[default]";
} else if (v.empty()) {
origin = "[empty]";
} else {
origin = "[provided]";
}
try {
pair<string, string> info = FairMQProgOptions::fValInfos.at(v.value().type())(v.value());
return {info.first, info.second, origin};
} catch (out_of_range& oor)
{
return {string("[unidentified]"), string("[unidentified]"), origin};
}
};
string ConvertVarValToString(const po::variable_value& v)
{
return ConvertVarValToValInfo(v).value;
}
} // namespace mq
} // namespace fair
FairMQProgOptions::FairMQProgOptions() FairMQProgOptions::FairMQProgOptions()
: fVarMap() : fVarMap()
, fFairMQChannelMap() , fFairMQChannelMap()
@ -100,6 +198,8 @@ int FairMQProgOptions::ParseAll(const int argc, char const* const* argv, bool al
{ {
ParseCmdLine(argc, argv, allowUnregistered); ParseCmdLine(argc, argv, allowUnregistered);
UpdateVarMap<string>("blubblub", "yarhar");
// if this option is provided, handle them and return stop value // if this option is provided, handle them and return stop value
if (fVarMap.count("help")) { if (fVarMap.count("help")) {
cout << fAllOptions << endl; cout << fAllOptions << endl;
@ -382,7 +482,7 @@ int FairMQProgOptions::PrintOptions()
// string, int, float, double, short, boost::filesystem::path // string, int, float, double, short, boost::filesystem::path
// vector<string>, vector<int>, vector<float>, vector<double>, vector<short> // vector<string>, vector<int>, vector<float>, vector<double>, vector<short>
map<string, VarValInfo> mapinfo; map<string, ValInfo> mapinfo;
// get string length for formatting and convert varmap values into string // get string length for formatting and convert varmap values into string
int maxLenKey = 0; int maxLenKey = 0;
@ -393,15 +493,14 @@ int FairMQProgOptions::PrintOptions()
for (const auto& m : fVarMap) { for (const auto& m : fVarMap) {
maxLenKey = max(maxLenKey, static_cast<int>(m.first.length())); maxLenKey = max(maxLenKey, static_cast<int>(m.first.length()));
VarValInfo valinfo = ConvertVariableValue<options::ToVarValInfo>()((m.second)); ValInfo valinfo = ConvertVarValToValInfo(m.second);
mapinfo[m.first] = valinfo; mapinfo[m.first] = valinfo;
maxLenValue = max(maxLenValue, static_cast<int>(valinfo.value.length())); maxLenValue = max(maxLenValue, static_cast<int>(valinfo.value.length()));
maxLenType = max(maxLenType, static_cast<int>(valinfo.type.length())); maxLenType = max(maxLenType, static_cast<int>(valinfo.type.length()));
maxLenDefault = max(maxLenDefault, static_cast<int>(valinfo.defaulted.length())); maxLenDefault = max(maxLenDefault, static_cast<int>(valinfo.origin.length()));
} }
// TODO : limit the value len field in a better way
if (maxLenValue > 100) { if (maxLenValue > 100) {
maxLenValue = 100; maxLenValue = 100;
} }
@ -418,7 +517,7 @@ int FairMQProgOptions::PrintOptions()
<< setw(maxLenKey) << p.first << " = " << setw(maxLenKey) << p.first << " = "
<< setw(maxLenValue) << p.second.value << " " << setw(maxLenValue) << p.second.value << " "
<< setw(maxLenType) << p.second.type << setw(maxLenType) << p.second.type
<< setw(maxLenDefault) << p.second.defaulted << setw(maxLenDefault) << p.second.origin
<< "\n"; << "\n";
} }
@ -432,9 +531,9 @@ int FairMQProgOptions::PrintOptionsRaw()
const vector<boost::shared_ptr<po::option_description>>& options = fAllOptions.options(); const vector<boost::shared_ptr<po::option_description>>& options = fAllOptions.options();
for (const auto& o : options) { for (const auto& o : options) {
VarValInfo value; ValInfo value;
if (fVarMap.count(o->canonical_display_name())) { if (fVarMap.count(o->canonical_display_name())) {
value = ConvertVariableValue<options::ToVarValInfo>()((fVarMap[o->canonical_display_name()])); value = ConvertVarValToValInfo(fVarMap[o->canonical_display_name()]);
} }
string description = o->description(); string description = o->description();
@ -454,7 +553,7 @@ string FairMQProgOptions::GetStringValue(const string& key)
string valueStr; string valueStr;
try { try {
if (fVarMap.count(key)) { if (fVarMap.count(key)) {
valueStr = ConvertVariableValue<options::ToString>()(fVarMap.at(key)); valueStr = ConvertVarValToString(fVarMap.at(key));
} }
} catch (exception& e) { } catch (exception& e) {
LOG(error) << "Exception thrown for the key '" << key << "'"; LOG(error) << "Exception thrown for the key '" << key << "'";

View File

@ -24,6 +24,8 @@
#include <mutex> #include <mutex>
#include <regex> #include <regex>
#include <sstream> #include <sstream>
#include <typeindex>
#include <stdexcept>
namespace fair namespace fair
{ {
@ -33,6 +35,13 @@ namespace mq
struct PropertyChange : Event<std::string> {}; struct PropertyChange : Event<std::string> {};
struct PropertyChangeAsString : Event<std::string> {}; struct PropertyChangeAsString : Event<std::string> {};
struct ValInfo
{
std::string value;
std::string type;
std::string origin;
};
} /* namespace mq */ } /* namespace mq */
} /* namespace fair */ } /* namespace fair */
@ -45,6 +54,8 @@ class FairMQProgOptions
FairMQProgOptions(); FairMQProgOptions();
virtual ~FairMQProgOptions(); virtual ~FairMQProgOptions();
struct PropertyNotFoundException : std::runtime_error { using std::runtime_error::runtime_error; };
int ParseAll(const std::vector<std::string>& cmdLineArgs, bool allowUnregistered); int ParseAll(const std::vector<std::string>& cmdLineArgs, bool allowUnregistered);
// parse command line. // parse command line.
// default parser for the mq-configuration file (JSON) is called if command line key mq-config is called // default parser for the mq-configuration file (JSON) is called if command line key mq-config is called
@ -52,80 +63,36 @@ class FairMQProgOptions
FairMQChannelMap GetFairMQMap() const; FairMQChannelMap GetFairMQMap() const;
std::unordered_map<std::string, int> GetChannelInfo() const; std::unordered_map<std::string, int> GetChannelInfo() const;
template<typename T>
int SetValue(const std::string& key, T val)
{
std::unique_lock<std::mutex> lock(fMtx);
// update variable map
UpdateVarMap<typename std::decay<T>::type>(key, val);
if (key == "channel-config") {
ParseChannelsFromCmdLine();
} else if (fChannelKeyMap.count(key)) {
UpdateChannelValue(fChannelKeyMap.at(key).channel, fChannelKeyMap.at(key).index, fChannelKeyMap.at(key).member, val);
}
lock.unlock();
//if (std::is_same<T, int>::value || std::is_same<T, std::string>::value)//if one wants to restrict type
fEvents.Emit<fair::mq::PropertyChange, typename std::decay<T>::type>(key, val);
fEvents.Emit<fair::mq::PropertyChangeAsString, std::string>(key, GetStringValue(key));
return 0;
}
template <typename T>
void Subscribe(const std::string& subscriber, std::function<void(typename fair::mq::PropertyChange::KeyType, T)> func)
{
std::lock_guard<std::mutex> lock(fMtx);
static_assert(!std::is_same<T,const char*>::value || !std::is_same<T, char*>::value,
"In template member FairMQProgOptions::Subscribe<T>(key,Lambda) the types const char* or char* for the calback signatures are not supported.");
fEvents.Subscribe<fair::mq::PropertyChange, T>(subscriber, func);
}
template <typename T>
void Unsubscribe(const std::string& subscriber)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Unsubscribe<fair::mq::PropertyChange, T>(subscriber);
}
void SubscribeAsString(const std::string& subscriber, std::function<void(typename fair::mq::PropertyChange::KeyType, std::string)> func)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Subscribe<fair::mq::PropertyChangeAsString, std::string>(subscriber, func);
}
void UnsubscribeAsString(const std::string& subscriber)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Unsubscribe<fair::mq::PropertyChangeAsString, std::string>(subscriber);
}
std::vector<std::string> GetPropertyKeys() const; std::vector<std::string> GetPropertyKeys() const;
// get value corresponding to the key
template<typename T> template<typename T>
T GetValue(const std::string& key) const T GetProperty(const std::string& key) const
{ {
std::lock_guard<std::mutex> lock(fMtx); std::lock_guard<std::mutex> lock(fMtx);
T val = T();
if (fVarMap.count(key)) { if (fVarMap.count(key)) {
val = fVarMap[key].as<T>(); return fVarMap[key].as<T>();
} else {
LOG(warn) << "Config has no key: " << key << ". Returning default constructed object.";
} }
return val; throw PropertyNotFoundException(fair::mq::tools::ToString("Config has no key: ", key));
}
template<typename T>
T GetProperty(const std::string& key, const T& ifNotFound) const
{
std::lock_guard<std::mutex> lock(fMtx);
if (fVarMap.count(key)) {
return fVarMap[key].as<T>();
}
return ifNotFound;
}
template<typename T>
T GetValue(const std::string& key) const // TODO: deprecate this
{
return GetProperty<T>(key);
} }
std::map<std::string, boost::any> GetProperties(const std::string& q) std::map<std::string, boost::any> GetProperties(const std::string& q)
@ -145,6 +112,64 @@ class FairMQProgOptions
// Given a key, convert the variable value to string // Given a key, convert the variable value to string
std::string GetStringValue(const std::string& key); std::string GetStringValue(const std::string& key);
template<typename T>
void SetProperty(const std::string& key, T val)
{
std::unique_lock<std::mutex> lock(fMtx);
// update variable map
UpdateVarMap<typename std::decay<T>::type>(key, val);
if (key == "channel-config") {
ParseChannelsFromCmdLine();
} else if (fChannelKeyMap.count(key)) {
UpdateChannelValue(fChannelKeyMap.at(key).channel, fChannelKeyMap.at(key).index, fChannelKeyMap.at(key).member, val);
}
lock.unlock();
//if (std::is_same<T, int>::value || std::is_same<T, std::string>::value)//if one wants to restrict type
fEvents.Emit<fair::mq::PropertyChange, typename std::decay<T>::type>(key, val);
fEvents.Emit<fair::mq::PropertyChangeAsString, std::string>(key, GetStringValue(key));
}
template<typename T>
int SetValue(const std::string& key, T val) // TODO: deprecate this
{
SetProperty(key, val);
return 0;
}
template <typename T>
void Subscribe(const std::string& subscriber, std::function<void(typename fair::mq::PropertyChange::KeyType, T)> func)
{
std::lock_guard<std::mutex> lock(fMtx);
static_assert(!std::is_same<T,const char*>::value || !std::is_same<T, char*>::value,
"In template member FairMQProgOptions::Subscribe<T>(key,Lambda) the types const char* or char* for the calback signatures are not supported.");
fEvents.Subscribe<fair::mq::PropertyChange, T>(subscriber, func);
}
template <typename T>
void Unsubscribe(const std::string& subscriber)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Unsubscribe<fair::mq::PropertyChange, T>(subscriber);
}
void SubscribeAsString(const std::string& subscriber, std::function<void(typename fair::mq::PropertyChange::KeyType, std::string)> func)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Subscribe<fair::mq::PropertyChangeAsString, std::string>(subscriber, func);
}
void UnsubscribeAsString(const std::string& subscriber)
{
std::lock_guard<std::mutex> lock(fMtx);
fEvents.Unsubscribe<fair::mq::PropertyChangeAsString, std::string>(subscriber);
}
int Count(const std::string& key) const; int Count(const std::string& key) const;
// add options_description // add options_description
@ -159,6 +184,8 @@ class FairMQProgOptions
fFairMQChannelMap[channelName].push_back(channel); fFairMQChannelMap[channelName].push_back(channel);
} }
static std::unordered_map<std::type_index, std::pair<std::string, std::string>(*)(const boost::any&)> fValInfos;
private: private:
struct ChannelKey struct ChannelKey
{ {

View File

@ -1 +0,0 @@
#warning "This header file is deprecated. Use FairMQProgOptions class directly which now contains all FairProgOptions functionality. Note, that FairMQProgOptions is also available if you include FairMQDevice."

View File

@ -1,187 +0,0 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/*
* File: FairProgOptionsHelper.h
* Author: winckler
*
* Created on March 11, 2015, 5:38 PM
*/
#ifndef FAIRPROGOPTIONSHELPER_H
#define FAIRPROGOPTIONSHELPER_H
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <boost/spirit/home/support/detail/hold_any.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <ostream>
#include <iterator>
#include <typeinfo>
namespace fair
{
namespace mq
{
template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
{
for (const auto& i : v) {
os << i << " ";
}
return os;
}
struct VarValInfo
{
std::string value;
std::string type;
std::string defaulted;
};
template<typename T>
std::string ConvertVariableValueToString(const boost::program_options::variable_value& varVal)
{
std::ostringstream oss;
if (auto q = boost::any_cast<T>(&varVal.value())) {
oss << *q;
}
return oss.str();
}
namespace options
{
// policy to convert boost variable value into string
struct ToString
{
using returned_type = std::string;
template<typename T>
std::string Value(const boost::program_options::variable_value& varVal, const std::string&, const std::string&)
{
return ConvertVariableValueToString<T>(varVal);
}
returned_type DefaultValue(const std::string&)
{
return std::string("[unidentified]");
}
};
// policy to convert variable value content into VarValInfo
struct ToVarValInfo
{
using returned_type = VarValInfo;
template<typename T>
returned_type Value(const boost::program_options::variable_value& varVal, const std::string& type, const std::string& defaulted)
{
return VarValInfo{ConvertVariableValueToString<T>(varVal), type, defaulted};
}
returned_type DefaultValue(const std::string& defaulted)
{
return VarValInfo{std::string("[unidentified]"), std::string("[unidentified]"), defaulted};
}
};
} // namespace options
// host class that take one of the two policy defined above
template<typename T>
struct ConvertVariableValue : T
{
auto operator()(const boost::program_options::variable_value& varVal) -> typename T::returned_type
{
std::string defaulted;
if (varVal.defaulted()) {
defaulted = " [default]";
} else {
defaulted = " [provided]";
}
if (typeid(std::string) == varVal.value().type())
return T::template Value<std::string>(varVal, std::string("<string>"), defaulted);
if (typeid(std::vector<std::string>) == varVal.value().type())
return T::template Value<std::vector<std::string>>(varVal, std::string("<vector<string>>"), defaulted);
if (typeid(int) == varVal.value().type())
return T::template Value<int>(varVal, std::string("<int>"), defaulted);
if (typeid(std::vector<int>) == varVal.value().type())
return T::template Value<std::vector<int>>(varVal, std::string("<vector<int>>"), defaulted);
if (typeid(float) == varVal.value().type())
return T::template Value<float>(varVal, std::string("<float>"), defaulted);
if (typeid(std::vector<float>) == varVal.value().type())
return T::template Value<std::vector<float>>(varVal, std::string("<vector<float>>"), defaulted);
if (typeid(double) == varVal.value().type())
return T::template Value<double>(varVal, std::string("<double>"), defaulted);
if (typeid(std::vector<double>) == varVal.value().type())
return T::template Value<std::vector<double>>(varVal, std::string("<vector<double>>"), defaulted);
if (typeid(short) == varVal.value().type())
return T::template Value<short>(varVal, std::string("<short>"), defaulted);
if (typeid(std::vector<short>) == varVal.value().type())
return T::template Value<std::vector<short>>(varVal, std::string("<vector<short>>"), defaulted);
if (typeid(long) == varVal.value().type())
return T::template Value<long>(varVal, std::string("<long>"), defaulted);
if (typeid(std::vector<long>) == varVal.value().type())
return T::template Value<std::vector<long>>(varVal, std::string("<vector<long>>"), defaulted);
if (typeid(std::size_t) == varVal.value().type())
return T::template Value<std::size_t>(varVal, std::string("<std::size_t>"), defaulted);
if (typeid(std::vector<std::size_t>) == varVal.value().type())
return T::template Value<std::vector<std::size_t>>(varVal, std::string("<vector<std::size_t>>"), defaulted);
if (typeid(std::uint32_t) == varVal.value().type())
return T::template Value<std::uint32_t>(varVal, std::string("<std::uint32_t>"), defaulted);
if (typeid(std::vector<std::uint32_t>) == varVal.value().type())
return T::template Value<std::vector<std::uint32_t>>(varVal, std::string("<vector<std::uint32_t>>"), defaulted);
if (typeid(std::uint64_t) == varVal.value().type())
return T::template Value<std::uint64_t>(varVal, std::string("<std::uint64_t>"), defaulted);
if (typeid(std::vector<std::uint64_t>) == varVal.value().type())
return T::template Value<std::vector<std::uint64_t>>(varVal, std::string("<vector<std::uint64_t>>"), defaulted);
if (typeid(bool) == varVal.value().type())
return T::template Value<bool>(varVal, std::string("<bool>"), defaulted);
if (typeid(std::vector<bool>) == varVal.value().type())
return T::template Value<std::vector<bool>>(varVal, std::string("<vector<bool>>"), defaulted);
if (typeid(boost::filesystem::path) == varVal.value().type())
return T::template Value<boost::filesystem::path>(varVal, std::string("<boost::filesystem::path>"), defaulted);
if (typeid(std::vector<boost::filesystem::path>) == varVal.value().type())
return T::template Value<std::vector<boost::filesystem::path>>(varVal, std::string("<std::vector<boost::filesystem::path>>"), defaulted);
// if we get here, the type is not supported return unknown info
return T::DefaultValue(defaulted);
}
};
} // namespace mq
} // namespace fair
#endif /* FAIRPROGOPTIONSHELPER_H */