From 9ceab6099caa311761df4ced4c821311e081366e Mon Sep 17 00:00:00 2001 From: winckler Date: Tue, 9 Aug 2016 14:02:43 +0200 Subject: [PATCH] add a signal/slot mechanism. 2 APIs : one generic version, and one string API (commented) --- fairmq/options/FairMQEventManager.h | 133 +++++++++++++++++ fairmq/options/FairMQProgOptions.cxx | 66 ++++++++- fairmq/options/FairMQProgOptions.h | 204 ++++++++++++++++++++++++--- fairmq/options/FairProgOptions.cxx | 19 ++- fairmq/options/FairProgOptions.h | 20 +++ 5 files changed, 415 insertions(+), 27 deletions(-) create mode 100644 fairmq/options/FairMQEventManager.h diff --git a/fairmq/options/FairMQEventManager.h b/fairmq/options/FairMQEventManager.h new file mode 100644 index 00000000..c4b0d35c --- /dev/null +++ b/fairmq/options/FairMQEventManager.h @@ -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 +#include +#include + +#include +#include +#include +#include +#include + + + + +enum class EventId : uint32_t +{ + // Place your new EventManager events here + UpdateParam = 0, + Custom = 1, + +}; + +namespace Events +{ + + template struct Traits; + template struct Traits { using signal_type = boost::signals2::signal; } ; + template struct Traits > { using signal_type = boost::signals2::signal& )>; } ; + + + template <> struct Traits { using signal_type = boost::signals2::signal; } ; + + template struct Traits { using signal_type = boost::signals2::signal; } ; + + template struct Traits { using signal_type = boost::signals2::signal; } ; + + /* + template struct Traits2; + template <> struct Traits2 { using signal_type = boost::signals2::signal; } ; + template struct Traits2 { using signal_type = boost::signals2::signal; } ; + template <> struct Traits2 { using signal_type = boost::signals2::signal; } ; + // */ +} + + +class FairMQEventManager +{ + public: + typedef std::pair EventKey; + + FairMQEventManager() : fEventMap() {} + virtual ~FairMQEventManager(){} + + + template + void Connect(const std::string& key, F&& func) + { + GetSlot(key).connect(std::forward(func)); + } + + template + void Disonnect(const std::string& key) + { + GetSlot(key).disconnect(); + } + + + template + void Emit(const std::string& key, Args&&... args) + { + GetSlot(key)(std::forward(args)...); + } + + + template + bool EventKeyFound(const std::string& key) + { + if (fEventMap.find(std::pair(event,key) ) != fEventMap.end()) + return true; + else + return false; + } + + private: + + + std::map fEventMap; + + + template ::signal_type, + typename SlotPtr = boost::shared_ptr > + Slot& GetSlot(const std::string& key) + { + try + { + EventKey eventKey = std::make_pair(event,key); + //static_assert(std::is_same()),SlotPtr>::value, ""); + if (fEventMap.find(eventKey) == fEventMap.end()) + fEventMap.emplace(eventKey, boost::make_shared()); + + return *boost::any_cast(fEventMap.at(eventKey)); +// auto &&tmp = boost::any_cast(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(event) << " and key" << key; + abort(); + } + } +}; + + +#endif /* FAIRMQEVENTMANAGER_H */ + diff --git a/fairmq/options/FairMQProgOptions.cxx b/fairmq/options/FairMQProgOptions.cxx index 72a17b2c..7ec9c87e 100644 --- a/fairmq/options/FairMQProgOptions.cxx +++ b/fairmq/options/FairMQProgOptions.cxx @@ -18,7 +18,7 @@ using namespace std; FairMQProgOptions::FairMQProgOptions() - : FairProgOptions() + : FairProgOptions(), FairMQEventManager() , fMQParserOptions("MQ-Device parser options") , fMQOptionsInCfg("MQ-Device options") , fMQOptionsInCmd("MQ-Device options") @@ -26,6 +26,7 @@ FairMQProgOptions::FairMQProgOptions() , fHelpTitle("***** FAIRMQ Program Options ***** ") , fVersion("Beta version 0.1") , fMQKeyMap() + // , fSignalMap() //string API { } @@ -43,10 +44,10 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist // init description InitOptionDescription(); // 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"; - exit(EXIT_FAILURE); + // ParseCmdLine return 0 if help or version cmd not called. return 1 if called + exit(EXIT_SUCCESS); } // 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 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"; 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")); } - PrintOptions(); + // check if one of required MQ config option is there auto parserOption_shptr = fMQParserOptions.options(); @@ -115,6 +117,7 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist } else { + // if cmdline mq-config called then use the default xml/json parser if (fVarMap.count("mq-config")) { LOG(DEBUG) << "mq-config: Using default XML/JSON parser"; @@ -177,6 +180,7 @@ void FairMQProgOptions::ParseAll(const int argc, char** argv, bool allowUnregist UserParser(ss, id); } } + FairProgOptions::PrintOptions(); } @@ -206,7 +210,6 @@ int FairMQProgOptions::UpdateChannelMap(const FairMQMap& channels) // create key for variable map as follow : channelName.index.memberName void FairMQProgOptions::UpdateMQValues() { - for(const auto& p : fFairMQMap) { int index = 0; @@ -232,8 +235,15 @@ void FairMQProgOptions::UpdateMQValues() UpdateVarMap(methodKey,channel.GetMethod()); UpdateVarMap(addressKey,channel.GetAddress()); UpdateVarMap(propertyKey,channel.GetProperty()); + + + //UpdateVarMap(sndBufSizeKey, std::to_string(channel.GetSndBufSize()));// string API UpdateVarMap(sndBufSizeKey,channel.GetSndBufSize()); + + //UpdateVarMap(rcvBufSizeKey, std::to_string(channel.GetRcvBufSize()));// string API UpdateVarMap(rcvBufSizeKey,channel.GetRcvBufSize()); + + //UpdateVarMap(rateLoggingKey,std::to_string(channel.GetRateLogging()));// string API UpdateVarMap(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(val)); + } + + //if we get there it means something is wrong + LOG(ERROR) << "update of FairMQChannel map failed for the following key: " + << channelName<<"."< +#include #include -#include "FairProgOptions.h" +#include "FairProgOptions.h" +#include "FairMQEventManager.h" #include "FairMQChannel.h" -class FairMQProgOptions : public FairProgOptions + + +class FairMQProgOptions : public FairProgOptions , public FairMQEventManager { protected: - typedef std::unordered_map> FairMQMap; + using FairMQMap = std::unordered_map>; + //using signal_type = boost::signals2::signal;// string API + //using signal_type_ptr = boost::shared_ptr;// string API public: FairMQProgOptions(); @@ -52,9 +58,6 @@ class FairMQProgOptions : public FairProgOptions return 0; } - - - FairMQMap GetFairMQMap() { return fFairMQMap; @@ -77,30 +80,156 @@ class FairMQProgOptions : public FairProgOptions // - 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) - template - 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); - - if(fMQKeyMap.count(key)) + UpdateValue(key,std::string(val)); + return 0; + } + // overload for string values + int UpdateValue(const std::string& key, const std::string& val) // string API + { + try { - std::string channelName; - int index = 0; - std::string member; - std::tie(channelName, index, member) = fMQKeyMap.at(key); + if(fVarMap.count(key)) + { + + if(!fairmq::is_this_type(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::value || std::is_same::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,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(key,std::string(val)); + return 0; + } + + template + int UpdateValue(const std::string& key, T val) + { + + if(fVarMap.count(key)) + { + // update variable map + UpdateVarMap::type>(key,val); + + // update FairMQChannel map, check first if data are int or string if(std::is_same::value || std::is_same::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::value || std::is_same::value)//if one wants to restrict type + if(EventKeyFound(key)) + EmitUpdate::type >(key,val); + + return 0; + } + else + { + + LOG(ERROR) <<"UpdatedValue failed because the provided key '" + < + void Subscribe(const std::string& key, F&& func) + { + static_assert(!std::is_same::value || !std::is_same::value, + "In template member FairMQProgOptions::Subscribe(key,Lambda) the types const char* or char* for the calback signatures are not supported."); + + if(fVarMap.count(key)) + FairMQEventManager::Connect(key,std::forward(func)); + } + + /* + template + 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()); + (*fSignalMap.at(key)).connect(std::forward(func)); + } + } + */ + // replace FairMQChannelMap, and update variable map accordingly int UpdateChannelMap(const FairMQMap& map); - - protected: po::options_description fMQParserOptions; po::options_description fMQOptionsInCfg; @@ -109,6 +238,21 @@ class FairMQProgOptions : public FairProgOptions std::string fHelpTitle; std::string fVersion; + + + + bool EventKeyFound(const std::string& key) + { + if ( + FairMQEventManager::EventKeyFound(key) + ) + return true; + else + return false; + } + + + typedef std::tuple MQKey;//store key info std::map fMQKeyMap;// key=full path - val=key info @@ -120,7 +264,31 @@ class FairMQProgOptions : public FairProgOptions void UpdateMQValues(); int Store(const FairMQMap& channels); + private: + + /* + // string API + std::map 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 + 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::value || !std::is_same::value, + "In template member FairMQProgOptions::EmitUpdate(key,val) the types const char* or char* for the calback signatures are not supported."); + Emit(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, int val); // for cases other than int and string diff --git a/fairmq/options/FairProgOptions.cxx b/fairmq/options/FairProgOptions.cxx index 46a84cbf..562572e1 100644 --- a/fairmq/options/FairProgOptions.cxx +++ b/fairmq/options/FairProgOptions.cxx @@ -92,6 +92,21 @@ int FairProgOptions::AddToCfgFileOptions(const po::options_description& optDesc, } 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) { @@ -153,7 +168,7 @@ int FairProgOptions::ParseCfgFile(ifstream& ifs, const po::options_description& { if (!ifs) { - cout << "can not open configuration file \n"; + LOG(ERROR) << "can not open configuration file"; return -1; } else @@ -169,7 +184,7 @@ int FairProgOptions::ParseCfgFile(const string& filename, const po::options_desc ifstream ifs(filename.c_str()); if (!ifs) { - cout << "can not open configuration file: " << filename << "\n"; + LOG(ERROR) << "can not open configuration file: " << filename; return -1; } else diff --git a/fairmq/options/FairProgOptions.h b/fairmq/options/FairProgOptions.h index e90b9232..e1784bc3 100644 --- a/fairmq/options/FairProgOptions.h +++ b/fairmq/options/FairProgOptions.h @@ -68,6 +68,9 @@ class FairProgOptions int AddToCmdLineOptions(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); + po::options_description& GetCmdLineOptions(); + po::options_description& GetCfgFileOptions(); + po::options_description& GetEnvironmentOptions(); void UseConfigFile(const std::string& filename = ""); @@ -96,6 +99,23 @@ class FairProgOptions // convert value to string that corresponds to the key std::string GetStringValue(const std::string& key); + //restrict conversion to fundamental types + template + T ConvertTo(const std::string& str_value) + { + if (std::is_arithmetic::value) + { + std::istringstream iss( str_value ); + T val; + iss >> val; + return val; + } + else + { + LOG(ERROR)<<"the provided string "<