add a signal/slot mechanism. 2 APIs : one generic version, and one string API (commented)

This commit is contained in:
winckler 2016-08-09 14:02:43 +02:00 committed by Mohammad Al-Turany
parent 5e5ddd5b7b
commit 9ceab6099c
5 changed files with 415 additions and 27 deletions

View File

@ -0,0 +1,133 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence version 3 (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/*
* File: FairMQEventManager.h
* Author: winckler
*
* Created on August 12, 2016, 13:50 PM
*/
#ifndef FAIRMQEVENTMANAGER_H
#define FAIRMQEVENTMANAGER_H
#include <map>
#include <utility>
#include <string>
#include <boost/any.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/signals2/signal.hpp>
enum class EventId : uint32_t
{
// Place your new EventManager events here
UpdateParam = 0,
Custom = 1,
};
namespace Events
{
template <EventId,typename ...Args> struct Traits;
template <typename T> struct Traits<EventId::UpdateParam, T> { using signal_type = boost::signals2::signal<void(const std::string&, T)>; } ;
template <typename T> struct Traits<EventId::UpdateParam, std::vector<T> > { using signal_type = boost::signals2::signal<void(const std::string&, const std::vector<T>& )>; } ;
template <> struct Traits<EventId::UpdateParam, std::string> { using signal_type = boost::signals2::signal<void(const std::string&, const std::string&)>; } ;
template<std::size_t N> struct Traits<EventId::UpdateParam, const char[N]> { using signal_type = boost::signals2::signal<void(const std::string&, const std::string&)>; } ;
template <typename ...T> struct Traits<EventId::Custom,T...> { using signal_type = boost::signals2::signal<void(T...)>; } ;
/*
template <EventId, typename ...Args> struct Traits2;
template <> struct Traits2<EventId::UpdateParam> { using signal_type = boost::signals2::signal<void(const std::string&, const std::string&)>; } ;
template <typename ...T> struct Traits2<EventId::UpdateParam,T...> { using signal_type = boost::signals2::signal<void(const std::string&, T...)>; } ;
template <> struct Traits2<EventId::UpdateParamInt> { using signal_type = boost::signals2::signal<void(const std::string&, int)>; } ;
// */
}
class FairMQEventManager
{
public:
typedef std::pair<EventId,std::string> EventKey;
FairMQEventManager() : fEventMap() {}
virtual ~FairMQEventManager(){}
template <EventId event, typename... ValueType, typename F>
void Connect(const std::string& key, F&& func)
{
GetSlot<event,ValueType...>(key).connect(std::forward<F>(func));
}
template <EventId event, typename... ValueType>
void Disonnect(const std::string& key)
{
GetSlot<event,ValueType...>(key).disconnect();
}
template <EventId event, typename... ValueType, typename... Args>
void Emit(const std::string& key, Args&&... args)
{
GetSlot<event,ValueType...>(key)(std::forward<Args>(args)...);
}
template <EventId event>
bool EventKeyFound(const std::string& key)
{
if (fEventMap.find(std::pair<EventId,std::string>(event,key) ) != fEventMap.end())
return true;
else
return false;
}
private:
std::map<EventKey, boost::any> fEventMap;
template <EventId event, typename... T, typename Slot = typename Events::Traits<event,T...>::signal_type,
typename SlotPtr = boost::shared_ptr<Slot> >
Slot& GetSlot(const std::string& key)
{
try
{
EventKey eventKey = std::make_pair(event,key);
//static_assert(std::is_same<decltype(boost::make_shared<Slot>()),SlotPtr>::value, "");
if (fEventMap.find(eventKey) == fEventMap.end())
fEventMap.emplace(eventKey, boost::make_shared<Slot>());
return *boost::any_cast<SlotPtr>(fEventMap.at(eventKey));
// auto &&tmp = boost::any_cast<SlotPtr>(fEventMap.at(eventKey));
// return *tmp;
}
catch (boost::bad_any_cast const &e)
{
LOG(ERROR) << "Caught instance of boost::bad_any_cast: "
<< e.what() << " on event #" << static_cast<uint32_t>(event) << " and key" << key;
abort();
}
}
};
#endif /* FAIRMQEVENTMANAGER_H */

View File

@ -18,7 +18,7 @@
using namespace std; using namespace std;
FairMQProgOptions::FairMQProgOptions() FairMQProgOptions::FairMQProgOptions()
: FairProgOptions() : FairProgOptions(), FairMQEventManager()
, fMQParserOptions("MQ-Device parser options") , fMQParserOptions("MQ-Device parser options")
, fMQOptionsInCfg("MQ-Device options") , fMQOptionsInCfg("MQ-Device options")
, fMQOptionsInCmd("MQ-Device options") , fMQOptionsInCmd("MQ-Device options")
@ -26,6 +26,7 @@ FairMQProgOptions::FairMQProgOptions()
, fHelpTitle("***** FAIRMQ Program Options ***** ") , fHelpTitle("***** FAIRMQ Program Options ***** ")
, fVersion("Beta version 0.1") , fVersion("Beta version 0.1")
, fMQKeyMap() , fMQKeyMap()
// , fSignalMap() //string API
{ {
} }
@ -43,10 +44,10 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist
// init description // init description
InitOptionDescription(); InitOptionDescription();
// parse command line options // parse command line options
if (ParseCmdLine(argc, argv, fCmdLineOptions, fVarMap, allowUnregistered)) if (FairProgOptions::ParseCmdLine(argc, argv, fCmdLineOptions, fVarMap, allowUnregistered))
{ {
LOG(ERROR) << "Could not parse cmd options"; // ParseCmdLine return 0 if help or version cmd not called. return 1 if called
exit(EXIT_FAILURE); exit(EXIT_SUCCESS);
} }
// if txt/INI configuration file enabled then parse it as well // if txt/INI configuration file enabled then parse it as well
@ -55,8 +56,9 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist
// check if file exist // check if file exist
if (fs::exists(fConfigFile)) if (fs::exists(fConfigFile))
{ {
if (ParseCfgFile(fConfigFile.string(), fConfigFileOptions, fVarMap, allowUnregistered)) if (FairProgOptions::ParseCfgFile(fConfigFile.string(), fConfigFileOptions, fVarMap, allowUnregistered))
{ {
// ParseCfgFile return -1 if cannot open or read config file. It return 0 otherwise
LOG(ERROR) << "Could not parse config"; LOG(ERROR) << "Could not parse config";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -86,7 +88,7 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist
set_global_log_level(log_op::operation::GREATER_EQ_THAN, fSeverityMap.at("DEBUG")); set_global_log_level(log_op::operation::GREATER_EQ_THAN, fSeverityMap.at("DEBUG"));
} }
PrintOptions();
// check if one of required MQ config option is there // check if one of required MQ config option is there
auto parserOption_shptr = fMQParserOptions.options(); auto parserOption_shptr = fMQParserOptions.options();
@ -115,6 +117,7 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist
} }
else else
{ {
// if cmdline mq-config called then use the default xml/json parser
if (fVarMap.count("mq-config")) if (fVarMap.count("mq-config"))
{ {
LOG(DEBUG) << "mq-config: Using default XML/JSON parser"; LOG(DEBUG) << "mq-config: Using default XML/JSON parser";
@ -177,6 +180,7 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist
UserParser<FairMQParser::XML>(ss, id); UserParser<FairMQParser::XML>(ss, id);
} }
} }
FairProgOptions::PrintOptions();
} }
@ -206,7 +210,6 @@ int FairMQProgOptions::UpdateChannelMap(const FairMQMap& channels)
// create key for variable map as follow : channelName.index.memberName // create key for variable map as follow : channelName.index.memberName
void FairMQProgOptions::UpdateMQValues() void FairMQProgOptions::UpdateMQValues()
{ {
for(const auto& p : fFairMQMap) for(const auto& p : fFairMQMap)
{ {
int index = 0; int index = 0;
@ -232,8 +235,15 @@ void FairMQProgOptions::UpdateMQValues()
UpdateVarMap<std::string>(methodKey,channel.GetMethod()); UpdateVarMap<std::string>(methodKey,channel.GetMethod());
UpdateVarMap<std::string>(addressKey,channel.GetAddress()); UpdateVarMap<std::string>(addressKey,channel.GetAddress());
UpdateVarMap<std::string>(propertyKey,channel.GetProperty()); UpdateVarMap<std::string>(propertyKey,channel.GetProperty());
//UpdateVarMap<std::string>(sndBufSizeKey, std::to_string(channel.GetSndBufSize()));// string API
UpdateVarMap<int>(sndBufSizeKey,channel.GetSndBufSize()); UpdateVarMap<int>(sndBufSizeKey,channel.GetSndBufSize());
//UpdateVarMap<std::string>(rcvBufSizeKey, std::to_string(channel.GetRcvBufSize()));// string API
UpdateVarMap<int>(rcvBufSizeKey,channel.GetRcvBufSize()); UpdateVarMap<int>(rcvBufSizeKey,channel.GetRcvBufSize());
//UpdateVarMap<std::string>(rateLoggingKey,std::to_string(channel.GetRateLogging()));// string API
UpdateVarMap<int>(rateLoggingKey,channel.GetRateLogging()); UpdateVarMap<int>(rateLoggingKey,channel.GetRateLogging());
/* /*
@ -369,6 +379,48 @@ int FairMQProgOptions::UpdateChannelMap(const std::string& channelName, int inde
} }
/*
// string API
int FairMQProgOptions::UpdateChannelMap(const std::string& channelName, int index, const std::string& member, const std::string& val)
{
if(member == "type")
{
fFairMQMap.at(channelName).at(index).UpdateType(val);
return 0;
}
if(member == "method")
{
fFairMQMap.at(channelName).at(index).UpdateMethod(val);
return 0;
}
if(member == "address")
{
fFairMQMap.at(channelName).at(index).UpdateAddress(val);
return 0;
}
if(member == "property")
{
fFairMQMap.at(channelName).at(index).UpdateProperty(val);
return 0;
}
else
{
if(member == "sndBufSize" || member == "rcvBufSize" || member == "rateLogging")
{
UpdateChannelMap(channelName,index,member,ConvertTo<int>(val));
}
//if we get there it means something is wrong
LOG(ERROR) << "update of FairMQChannel map failed for the following key: "
<< channelName<<"."<<index<<"."<<member;
return 1;
}
}
*/
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------

View File

@ -17,16 +17,22 @@
#define FAIRMQPROGOPTIONS_H #define FAIRMQPROGOPTIONS_H
#include <unordered_map> #include <unordered_map>
#include <map>
#include <set> #include <set>
#include "FairProgOptions.h"
#include "FairProgOptions.h"
#include "FairMQEventManager.h"
#include "FairMQChannel.h" #include "FairMQChannel.h"
class FairMQProgOptions : public FairProgOptions
class FairMQProgOptions : public FairProgOptions , public FairMQEventManager
{ {
protected: protected:
typedef std::unordered_map<std::string, std::vector<FairMQChannel>> FairMQMap; using FairMQMap = std::unordered_map<std::string, std::vector<FairMQChannel>>;
//using signal_type = boost::signals2::signal<void(const std::string&, const std::string&)>;// string API
//using signal_type_ptr = boost::shared_ptr<signal_type>;// string API
public: public:
FairMQProgOptions(); FairMQProgOptions();
@ -52,9 +58,6 @@ class FairMQProgOptions : public FairProgOptions
return 0; return 0;
} }
FairMQMap GetFairMQMap() FairMQMap GetFairMQMap()
{ {
return fFairMQMap; return fFairMQMap;
@ -77,30 +80,156 @@ class FairMQProgOptions : public FairProgOptions
// - if UpdateChannelMap(const FairMQMap& map) method is called // - if UpdateChannelMap(const FairMQMap& map) method is called
// - if UserParser template method is called (it is called in the ParseAll method if json or xml MQ-config files is provided) // - if UserParser template method is called (it is called in the ParseAll method if json or xml MQ-config files is provided)
template<typename T>
int UpdateValue(const std::string& key, const T& val) /* // string API
//overload for string literal
int UpdateValue(const std::string& key, const char* val) // string API
{ {
UpdateVarMap(key,val); UpdateValue(key,std::string(val));
return 0;
if(fMQKeyMap.count(key)) }
// overload for string values
int UpdateValue(const std::string& key, const std::string& val) // string API
{
try
{ {
std::string channelName; if(fVarMap.count(key))
int index = 0; {
std::string member;
std::tie(channelName, index, member) = fMQKeyMap.at(key);
if(!fairmq::is_this_type<std::string>(fVarMap.at(key)))
{
LOG(ERROR) << "You try to update a value as string (for key="<< key <<") while it has been defined with a different type in the option description.";
abort();
}
// update variable map
UpdateVarMap(key,val);
if(fMQKeyMap.count(key))
{
std::string channelName;
int index = 0;
std::string member;
std::tie(channelName, index, member) = fMQKeyMap.at(key);
UpdateChannelMap(channelName, index, member, val);
}
// execute stored function of a given key if exist
//if(std::is_same<T, int>::value || std::is_same<T, std::string>::value)//if one wants to restrict type
if(fSignalMap.count(key))
EmitUpdate(key,val);
return 0;
}
else
{
LOG(ERROR) <<"UpdatedValue failed because the provided key '"
<<key
<<"' is not found in the variable map";
return 1;
}
}
catch (std::exception& e)
{
LOG(ERROR) << "Caught exception on key "<<key;
abort();
}
return 0;
}
*/
//overload for string literal
/*int UpdateValue(const std::string& key, const char* val) // string API
{
UpdateValue<std::string>(key,val);
return 0;
}*/
// specialization/overloading for string, pass by const ref
int UpdateValue(const std::string& key, const std::string& val) // string API
{
UpdateValue(key,val);
return 0;
}
int UpdateValue(const std::string& key, const char* val) // string API
{
UpdateValue<std::string>(key,std::string(val));
return 0;
}
template<typename T>
int UpdateValue(const std::string& key, T val)
{
if(fVarMap.count(key))
{
// update variable map
UpdateVarMap<typename std::decay<T>::type>(key,val);
// update FairMQChannel map, check first if data are int or string
if(std::is_same<T, int>::value || std::is_same<T, std::string>::value) if(std::is_same<T, int>::value || std::is_same<T, std::string>::value)
UpdateChannelMap(channelName, index, member, val); if(fMQKeyMap.count(key))
{
std::string channelName;
int index = 0;
std::string member;
std::tie(channelName, index, member) = fMQKeyMap.at(key);
UpdateChannelMap(channelName, index, member, val);
}
// execute stored function of a given key if exist
//if(std::is_same<T, int>::value || std::is_same<T, std::string>::value)//if one wants to restrict type
if(EventKeyFound(key))
EmitUpdate<typename std::decay<T>::type >(key,val);
return 0;
}
else
{
LOG(ERROR) <<"UpdatedValue failed because the provided key '"
<<key
<<"' is not found in the variable map";
return 1;
} }
return 0; return 0;
} }
template <typename T, typename F>
void Subscribe(const std::string& key, F&& func)
{
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.");
if(fVarMap.count(key))
FairMQEventManager::Connect<EventId::UpdateParam,T>(key,std::forward<F>(func));
}
/*
template <typename F>
void Subscribe(const std::string& key, F&& func)
{
if(fVarMap.count(key))
{
//if key-value not yet found, then add it
if(fSignalMap.find(key) == fSignalMap.end())
fSignalMap.emplace(key, boost::make_shared<signal_type>());
(*fSignalMap.at(key)).connect(std::forward<F>(func));
}
}
*/
// replace FairMQChannelMap, and update variable map accordingly // replace FairMQChannelMap, and update variable map accordingly
int UpdateChannelMap(const FairMQMap& map); int UpdateChannelMap(const FairMQMap& map);
protected: protected:
po::options_description fMQParserOptions; po::options_description fMQParserOptions;
po::options_description fMQOptionsInCfg; po::options_description fMQOptionsInCfg;
@ -109,6 +238,21 @@ class FairMQProgOptions : public FairProgOptions
std::string fHelpTitle; std::string fHelpTitle;
std::string fVersion; std::string fVersion;
bool EventKeyFound(const std::string& key)
{
if (
FairMQEventManager::EventKeyFound<EventId::UpdateParam>(key)
)
return true;
else
return false;
}
typedef std::tuple<std::string,int,std::string> MQKey;//store key info typedef std::tuple<std::string,int,std::string> MQKey;//store key info
std::map<std::string,MQKey> fMQKeyMap;// key=full path - val=key info std::map<std::string,MQKey> fMQKeyMap;// key=full path - val=key info
@ -120,7 +264,31 @@ class FairMQProgOptions : public FairProgOptions
void UpdateMQValues(); void UpdateMQValues();
int Store(const FairMQMap& channels); int Store(const FairMQMap& channels);
private: private:
/*
// string API
std::map<std::string, signal_type_ptr > fSignalMap;
void EmitUpdate(const std::string& key, const char* val)
{
EmitUpdate(key,std::string(val));
}
void EmitUpdate(const std::string& key, const std::string& val)
{
(*fSignalMap.at(key))(key,val);
}
*/
template<typename T>
void EmitUpdate(const std::string& key, T val)
{
//compile time check whether T is const char* or char*, and in that case a compile time error is thrown.
static_assert(!std::is_same<T,const char*>::value || !std::is_same<T, char*>::value,
"In template member FairMQProgOptions::EmitUpdate<T>(key,val) the types const char* or char* for the calback signatures are not supported.");
Emit<EventId::UpdateParam,T>(key,key,val);
}
int UpdateChannelMap(const std::string& channelName, int index, const std::string& member, const std::string& val); int UpdateChannelMap(const std::string& channelName, int index, const std::string& member, const std::string& val);
int UpdateChannelMap(const std::string& channelName, int index, const std::string& member, int val); int UpdateChannelMap(const std::string& channelName, int index, const std::string& member, int val);
// for cases other than int and string // for cases other than int and string

View File

@ -92,6 +92,21 @@ int FairProgOptions::AddToCfgFileOptions(const po::options_description& optDesc,
} }
return 0; return 0;
} }
//*
po::options_description& FairProgOptions::GetCmdLineOptions()
{
return fCmdLineOptions;
}
po::options_description& FairProgOptions::GetCfgFileOptions()
{
return fConfigFileOptions;
}
po::options_description& FairProgOptions::GetEnvironmentOptions()
{
return fEnvironmentDesc;
}
int FairProgOptions::AddToEnvironmentOptions(const po::options_description& optDesc) int FairProgOptions::AddToEnvironmentOptions(const po::options_description& optDesc)
{ {
@ -153,7 +168,7 @@ int FairProgOptions::ParseCfgFile(ifstream& ifs, const po::options_description&
{ {
if (!ifs) if (!ifs)
{ {
cout << "can not open configuration file \n"; LOG(ERROR) << "can not open configuration file";
return -1; return -1;
} }
else else
@ -169,7 +184,7 @@ int FairProgOptions::ParseCfgFile(const string& filename, const po::options_desc
ifstream ifs(filename.c_str()); ifstream ifs(filename.c_str());
if (!ifs) if (!ifs)
{ {
cout << "can not open configuration file: " << filename << "\n"; LOG(ERROR) << "can not open configuration file: " << filename;
return -1; return -1;
} }
else else

View File

@ -68,6 +68,9 @@ class FairProgOptions
int AddToCmdLineOptions(const po::options_description& optDesc, bool visible = true); int AddToCmdLineOptions(const po::options_description& optDesc, bool visible = true);
int AddToCfgFileOptions(const po::options_description& optDesc, bool visible = true); int AddToCfgFileOptions(const po::options_description& optDesc, bool visible = true);
int AddToEnvironmentOptions(const po::options_description& optDesc); int AddToEnvironmentOptions(const po::options_description& optDesc);
po::options_description& GetCmdLineOptions();
po::options_description& GetCfgFileOptions();
po::options_description& GetEnvironmentOptions();
void UseConfigFile(const std::string& filename = ""); void UseConfigFile(const std::string& filename = "");
@ -96,6 +99,23 @@ class FairProgOptions
// convert value to string that corresponds to the key // convert value to string that corresponds to the key
std::string GetStringValue(const std::string& key); std::string GetStringValue(const std::string& key);
//restrict conversion to fundamental types
template<typename T>
T ConvertTo(const std::string& str_value)
{
if (std::is_arithmetic<T>::value)
{
std::istringstream iss( str_value );
T val;
iss >> val;
return val;
}
else
{
LOG(ERROR)<<"the provided string "<<str_value << " cannot be converted in the requested type. The target types must be arithmetic types";
}
}
const po::variables_map& GetVarMap() const { return fVarMap; } const po::variables_map& GetVarMap() const { return fVarMap; }
// boost prog options parsers // boost prog options parsers