How to enclose std::is_base_of in a std::function practically?
I don't think it is possible, because the type is erased.
How to workaround? I want to cache std::function f=check whether X derived from B.
It will be called later.
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
class B{ };
class C: B{};
int main(){
std::cout<<std::is_base_of<B,C>()<<std::endl; //print 1
std::function<bool()> f=[](X)->bool{ //<--- syntax error (X is class name)
return std::is_base_of<B,X>();
};
std::cout<<f(C)<<std::endl; //<--- possible? (should print 1)
return 0;
}
In real case, f is called in a far-away location of code, and I don't want to instantiate B or C.
(Thus, I can't use dynamic_cast to check.)
(Edit) Underlying Problem :-
I am trying to get a 2D-bool table (result #) for some sophisticated types manipulation.
class B{};
class C : B{};
class D{};
int main(){
for(auto cachefunction : cacheFunctions){
cachefunction();
//^ "cacheFunctions" is {register<B>(), register<C>(), register<D>()}
}
//in real case, cacheFunctions.size() ~ 200+
auto result=.... //#
}
I can edit code inside register<T>() to whatever I want, but I can't request user to call register<T1,T2> for every possible tuple.
Roughly speaking, the result # is an array of bool flag whether T2 derived from T1.
B C D (parent)
------------
(derived)
B x 0 0
C 1 x 0
D 0 0 x (x=don't care, 0 or 1 are OK)
int[]/std::vector<int> result = {x,0,0 , 1,x,0 , 0,0,x}.
My ultimate goal is to get the table at the line //#.
(Edit) Underlying Problem 2nd attempt :-
I have a user code that calls my library like this.
These lines are scattering around many user's .cpp :-
requestSystem<S1>()->someBFunction();
requestSystem<S2>()->someCFunction();
....
Si are sub-systems in my library (Here, I use composition rather that inheritance.).
So far, I have successfully used some trick (array of std::function) to instantiate those sub-system (new Si()), before those functions are actually called at run-time. Thus, it runs fine.
As my program grow, more Si are born.
I notice that there are appropriate cases where some Sy are best to inherit from a certain Sx.
In such cases, I find that if I instantiate both new Sx() and new Sy(), my program will act strangely, because there are two instance of base class Sx (it should be singleton by design).
I think it would be nice if I can detect such cases automatically by embedding some additional code inside requestSystem<T>() to instantiate only new S_Y(), and let requestSystem<S_X>() return the same pointer to S_Y.
I can't use the same trick (array of std::function) for std::is_base_of to check inheritance because the type is erased. Moreover, I intend to not call new S_X(), so I can't cache the type by its instance and use dynamic_cast later.
Here is MCVE (ideone).
The first part is Manager's definition:-
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class Manager{
public: Manager(){std::cout<<"Manager"<<std::endl; }
};
class EntityManager : public Manager{
public: EntityManager(){std::cout<<"EntityManager"<<std::endl;}
};
class AdvanceEntityManager : public EntityManager{
public: int testField=5; //just for testing
public: AdvanceEntityManager(){
std::cout<<"AdvanceEntityManager"<<std::endl;
}
};
Here is the type-manipulator :-
template<class T> class DummyHook{ public: static int dummyInt; };
template<class T> class IndexCache{ public: static int index; };
//^ index = index of T* inside "globalManagerList"
std::vector<Manager*> globalManagerList;
std::vector<std::function<Manager*(int)>>* getFunctionPointers(){
static std::vector<std::function<Manager*(int)>> cacher;
return &cacher;
}
/** it is called indirectly by "requestSystem" */
template<class T> int indirectCall(){
std::function<Manager*(int)> func=[](int assignIndex){
IndexCache<T>::index = assignIndex;
auto re= new T();
globalManagerList.push_back(re);
return re;
};
getFunctionPointers()->push_back(func);
int dummy=42;return dummy;
}
template<class T> T* requestSystem(){
int k=DummyHook<T>::dummyInt;
//^ optimized out, but force calling "indirectCall()" before main() at #
return static_cast<T*>(globalManagerList[IndexCache<T>::index]);
}
template<class T> int DummyHook<T>::dummyInt = indirectCall<T>(); //#
template<class T> int IndexCache<T>::index = -42;
Here is the main function :-
int main() {
auto fs=getFunctionPointers();
int n=0;
for(auto ele: *fs){
ele(n); ++n;
//^ call every function in
// static std::vector<std::function<Manager*(int)>> cacher
}
std::cout<<"All initialized, ready!"<<std::endl;
auto entityManagerPtr=requestSystem<EntityManager>();
auto advanceManagerPtr=requestSystem<AdvanceEntityManager>();
//^ In this program, they are different instance, but I want it to be the same instance.
std::cout<<"Should be 5 : "<<advanceManagerPtr->testField<<std::endl;
return 0;
}
This is the output (both managers are instantiated :():-
Manager
EntityManager
Manager
EntityManager
AdvanceEntityManager
All initialized, ready!
Should be 5 : 5
The list of bases of a type is not avaliable at runtime, which is what your code in effect requires.
We can however write our own typesystem. In this case, we mark up each type with a list of each parent, and enable compile time reflection over that list.
Then when we set up the factories, we also replace the factories for the parent types, and ensure that only the child type is used.
Here is some random tools to help:
template<class...Ts> struct types_t {};
template<class T> struct tag_t { constexpr tag_t() {} using type=T; };
template<class T> constexpr tag_t<T> tag{};
template<class Tag> using type_t=typename Tag::type;
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
template<class F>
auto invoke_foreach( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args){
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
}
template<class F>
auto invoke_on_tuple( F&& f ) {
return [f=std::forward<F>(f)](auto&& tuple)->decltype(auto) {
using Tuple = decltype(tuple);
using dTuple = std::decay_t<Tuple>;
using Size = std::tuple_size<dTuple>;
return index_upto<Size{}>()( [&](auto&&...args)->decltype(auto){
return f( std::get<decltype(args){}>( decltype(tuple)(tuple) )... );
});
};
}
template<class...Ts>
constexpr std::tuple< tag_t<Ts>... > as_tuple_tags( types_t<Ts...> ) { return std::make_tuple(tag<Ts>...); }
template<class F>
struct y_combinator_t {
F f;
template<class...Args>
decltype(auto) operator()( Args&&... args ) {
return f(*this, std::forward<Args>(args)...);
}
};
template<class F>
y_combinator_t<std::decay_t<F>> y_combinate( F&& f ) { return {std::forward<F>(f)}; }
Now, we mark up types with their parents:
class Manager{
public: Manager(){std::cout<<"Manager"<<std::endl; }
};
class EntityManager : public Manager{
public:
int base_value = 3;
EntityManager(){std::cout<<"EntityManager"<<std::endl;}
using parents = types_t<Manager>;
};
class AdvanceEntityManager : public EntityManager{
public: int testField=5; //just for testing
public:
AdvanceEntityManager(){
std::cout<<"AdvanceEntityManager"<<std::endl;
base_value = 1;
}
using parents = types_t<EntityManager>;
};
and use the starting code to let us easily work with parents:
template<class T, class Parents = typename T::parents>
auto foreach_parent( tag_t<T> ) {
constexpr auto parent_tuple = as_tuple_tags( Parents() );
return [](auto&& f) {
return invoke_on_tuple(invoke_foreach(f))(decltype(parent_tuple){});
};
}
template<class T, class...Ts>
auto foreach_parent( tag_t<T>, Ts&&... ) {
return [](auto&& f) {};
}
we set up a two-entry cache, a singleton factory, and use smart pointers:
template<class T> class IndexCache{
public:
static int index;
static int factory;
};
template<class T> class Dummy{ public: static int index; };
using pManager = std::shared_ptr<Manager>;
using ManagerFactory = std::function<pManager()>;
using Task = std::function<void()>;
std::vector<pManager> globalManagerList;
std::vector<Task>& getManagerFactories(){
static std::vector<Task> cacher{};
return cacher;
}
template<class T>
ManagerFactory singleton_factory() {
return []{
static auto single = (void(std::cout << "making " << typeid(T).name() << " singlton" << std::endl), std::make_shared<T>());
return single;
};
}
and modify indirect call to replace the parent factory tasks:
template<class T>
void indirectCall(){
std::cout << "Setting up " << typeid(T).name() << std::endl;
auto func=[](auto tag){
return [tag](){
IndexCache<type_t<decltype(tag)>>::index = globalManagerList.size();
globalManagerList.push_back(singleton_factory<T>()());
};
};
//std::cout << "Adding " << typeid(T).name() << " factory " << std::endl;
IndexCache<T>::factory = getManagerFactories().size();
getManagerFactories().push_back(func(tag<T>));
auto replace_parents = y_combinate(
[&](auto& replace_parents, auto child_tag) {
foreach_parent(child_tag)([&](auto parent_tag){
using Parent = type_t<decltype(parent_tag)>;
std::cout << "Replacing " << typeid(Parent).name() << " factory with " << typeid(T).name() << " factory" << std::endl;
getManagerFactories()[IndexCache<Parent>::factory] = func(tag<Parent>);
replace_parents( parent_tag );
});
}
);
replace_parents(tag<T>);
std::cout << "Added " << typeid(T).name() << " factory " << std::endl;
}
In requestSystem, we ensure that all of the setup has been done:
void setup_code() {
for (auto&& factory:getManagerFactories())
factory();
}
void setup() {
static int unused = (setup_code(),7);
(void)unused;
}
template<class T>
T* requestSystem()
{
int dummy = Dummy<T>::index;
(void)dummy;
std::cout << "Requesting " << typeid(T).name() << std::endl;
setup();
return static_cast<T*>(globalManagerList[IndexCache<T>::index].get());
}
template<class T> int IndexCache<T>::index = -1;
template<class T> int IndexCache<T>::factory = -1;
template<class T> int Dummy<T>::index = (indirectCall<T>(), 7);
and then we test it:
int main() {
std::cout<<"All initialized, ready!"<<std::endl;
auto entityManagerPtr=requestSystem<EntityManager>();
//std::cout<<"Phase 1"<<std::endl;
(void)entityManagerPtr;
auto advanceManagerPtr=requestSystem<AdvanceEntityManager>();
//std::cout<<"Phase 2"<<std::endl;
//^ In this program, they are different instance, but I want it to be the same instance.
std::cout<<"Should be 5 : "<<advanceManagerPtr->testField<<std::endl;
std::cout<<"Should be 1 : "<<entityManagerPtr->base_value<<std::endl;
return 0;
}
here we ask for the entityManagerPtr, and we get the advanceManagerPtr. We can see from the logs that only one object was created, and its entityManagerPtr->base_value was 1.
Live example.
Related
I am trying to implement an std::unordered_map that returns pairs of either double, int or std::string. The keys for the map are std::strings. Below is what I have tried so far:
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <utility>
#include <vector>
// A base class for boundary class
class Boundbase {
public:
Boundbase(){};
virtual ~Boundbase(){};
};
// A different map of boundaries for each different data type
template <class dType>
class Boundary : public Boundbase {
std::pair<dType, dType> bpair;
public:
//Constructor
Boundary(const std::string &lbound,
const std::string &ubound) {
setbound(lbound, ubound);
};
//A method to set boundary pair
void setbound(const std::string &lbound,
const std::string &ubound);
// A method to get boundary pair
std::pair<dType, dType> getbound() {return bpair;}
};
// Class to hold the different boundaries
class Boundaries {
std::unordered_map<std::string, Boundbase*> bounds;
public:
//Constructor
Boundaries() {};
// A method to set boundary map
void setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb);
// A template to get boundaries.
std::unordered_map<std::string, Boundbase*> getbounds()
{return bounds;}
};
// A method to set covariate boundary
template <class dType> void
Boundary<dType>::setbound(const std::string &lbound,
const std::string &ubound) {
dType val;
std::istringstream isa(lbound);
while(isa >> val) {
bpair.first = val;
}
std::istringstream isb(ubound);
while(isb >> val) {
bpair.second = val;
}
}
// A method to set boundary map
void Boundaries::setboundmap(std::unordered_map<std::string,
std::vector<std::string>> xtb) {
for(auto s : xtb) {
char type = s.second[1][0];
switch(type) {
case 'd': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<double>(
s.second[2], s.second[3]);
bounds.insert(opair);
}
break;
case 'i': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<int>(
s.second[2], s.second[3]);
bounds.insert(opair);
break;
}
case 'c': {
std::pair<std::string, Boundbase*> opair;
opair.first = s.first;
opair.second = new Boundary<std::string>(
s.second[2], s.second[2]);
bounds.insert(opair);
break;
}
}
}
}
This compiles ok using g++. When I try to run it though ( as follows):
int main() {
Data D;
Boundaries B;
std::ifstream iss("tphinit.txt");
D.read_lines(iss);
auto dbounds = D.get_xtypebound();
B.setboundmap(dbounds);
auto tbounds = B.getbounds();
auto sbound = tbounds["X1"];
std::cout << sbound->bpair.first << ","
<< sbound->bpair.second << std::endl;
}
I get 'class Boundbase' has no member named 'bpair' which is true because I am pointing to the base class and not the derived class. As far as I can tell, trying to get the derived member bpair requires that I use the visitor pattern. Now, it is clear that I am noob so when I had a look at different ways of doing this on SO I was a little in over my head (no reflection on the authors, just on my inexperience).
So my main question is: Is this the best and simplest way to go about this? I would like to avoid boost::variant if at all possible (mainly for the sake of purity: this cannot be that difficult). A sub-question is whether I have to use the visitor pattern or is there a better/simpler way to get the member pbair?
I will have to perform this lookup many times so I am hoping to make it as fast as possible but using the stl for the sake of simplicity.
Make your values std variants over the 3 types.
Failing that, boost variant.
Std and boost variant really are what you want. You'll end up implementing some subset of its implementation.
Failing that, find a tutorial on how to implement ones of them, or use std any. Failing that, dynamic casts around an otherwise useless wrapper type with a virtual dtor stored in a unique ptr, or do manual RTTI with try get methods.
This just gets increasingly ugly and/or inefficient however.
Boost variant, and std variant from it, was implemented for a reason, and that reason was solving the exact problem you are describing in an efficient manner.
#include <tuple>
#include <utility>
#include <string>
template<class...Ts>
struct destroy_helper {
std::tuple<Ts*...> data;
destroy_helper( std::tuple<Ts*...> d ):data(d){}
template<class T>
static void destroy(T* t){ t->~T(); }
template<std::size_t I>
void operator()(std::integral_constant<std::size_t, I>)const {
destroy( std::get<I>( data ) );
}
};
struct construct_helper {
template<class T, class...Args>
void operator()(T* target, Args&&...args)const {
::new( (void*)target ) T(std::forward<Args>(args)...);
}
};
template<std::size_t...Is>
struct indexes {};
template<std::size_t N, std::size_t...Is>
struct make_indexes:make_indexes<N-1, N-1, Is...> {};
template<std::size_t...Is>
struct make_indexes<0, Is...>{
using type=indexes<Is...>;
};
template<std::size_t N>
using make_indexes_t = typename make_indexes<N>::type;
template<class F>
void magic_switch( std::size_t i, indexes<>, F&& f ) {}
template<std::size_t I0, std::size_t...Is, class F>
void magic_switch( std::size_t i, indexes<I0,Is...>, F&& f )
{
if (i==I0) {
f( std::integral_constant<std::size_t, I0>{} );
return;
}
magic_switch( i, indexes<Is...>{}, std::forward<F>(f) );
}
template<class T0>
constexpr T0 max_of( T0 t0 ) {
return t0;
}
template<class T0, class T1, class...Ts>
constexpr T0 max_of( T0 t0, T1 t1, Ts... ts ) {
return (t1 > t0)?max_of(t1, ts...):max_of(t0, ts...);
}
template<class...Ts>
struct Variant{
using Data=typename std::aligned_storage< max_of(sizeof(Ts)...), max_of(alignof(Ts)...)>::type;
std::size_t m_index=-1;
Data m_data;
template<std::size_t I>
using alternative_t=typename std::tuple_element<I, std::tuple<Ts...>>::type;
using pointers=std::tuple<Ts*...>;
using cpointers=std::tuple<Ts const*...>;
template<class T> T& get(){ return *reinterpret_cast<T*>(&m_data); }
template<class T> T const& get() const { return *reinterpret_cast<T*>(&m_data); }
template<std::size_t I>
alternative_t<I>& get(){ return std::get<I>(get_pointers()); }
template<std::size_t I>
alternative_t<I> const& get()const{ return std::get<I>(get_pointers()); }
pointers get_pointers(){
return pointers( (Ts*)&m_data... );
}
cpointers get_pointers()const{
return cpointers( (Ts const*)&m_data... );
}
std::size_t alternative()const{return m_index;}
void destroy() {
if (m_index == -1)
return;
magic_switch(m_index, make_indexes_t<sizeof...(Ts)>{}, destroy_helper<Ts...>(get_pointers()));
}
template<std::size_t I, class...Args>
void emplace(Args&&...args) {
destroy();
construct_helper{}( std::get<I>(get_pointers()), std::forward<Args>(args)... );
m_index = I;
}
Variant()=default;
Variant(Variant const&)=delete;//todo
Variant&operator=(Variant const&)=delete;//todo
Variant(Variant &&)=delete;//todo
Variant&operator=(Variant &&)=delete;//todo
~Variant(){destroy();}
};
int main() {
Variant<int, double, std::string> bob;
bob.emplace<0>( 7 );
bob.emplace<1>( 3.14 );
bob.emplace<2>( "hello world" );
}
here is a really simple variant interface.
The hard part is turning a runtime index into which of the compile time indexes you want to use. I call that the magic switch problem.
You might also want to implement apply visitor.
...
Or...
template<class T>
struct Derived;
struct Base {
virtual ~Base() {}
template<class T>
friend T* get(Base* base) {
Derived<T>* self = dynamic_cast<T*>(base);
return self?&self.t:nullptr;
}
template<class T>
friend T const* get(Base const* base) {
Derived<T> const* self = dynamic_cast<T const*>(base);
return self?&self.t:nullptr;
}
};
template<class T>
struct Derived:Base {
Derived(T in):t(std::move(in)){}
T t;
};
std::unordered_map<std::string, std::unique_ptr<Base>> map;
map["hello"] = std::unique_ptr<Base>( new Derived<int>(-1) );
map["world"] = std::unique_ptr<Base>( new Derived<double>(3.14) );
int* phello = get<int>(map["hello"]);
if (phello) std::cout << *hello << "\n";
double* pworld = get<double>(map["world"]);
if (pworld) std::cout << *world << "\n";
which is a seriously bargain-basement std::any.
I want to create class which should containe map with function pointers (subscribers). But that functions can be with different signature. My code looks like this but it not completed and I am not sure if that is right. Can somebody help me please how to correct append pointers to map and invoke them in myMainClass::start()?
myMainClass.h
#pragma once
#include "iostream";
#include "mySubscriber.h"
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
}
// SHOULD BE SOMETHING LIKE THIS
bool start() {
mySubscriber ss;
ss.subscribe("callback1", callback1);
ss.subscribe("callback2", callback2);
ss.getSubscribe("callback1")(5);
ss.getSubscribe("callback2")("test");
}
};
mySubscriber.h
#pragma once
#include "map";
#include "string";
#include "functional";
class mySubscriber {
typedef std::function<void()> func;
std::map<std::string, func*> _subscribes;
public:
mySubscriber() : _subscribes{} {}
~mySubscriber() {
_subscribes.clear();
}
/*
* append or change function pointer
*/
void subscribe(std::string fName, func* f) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
find->second = f;
}
else
{
_subscribes.emplace(fName, f);
}
}
/*
* get subscribe function
*/
func* getSubscribe(std::string fName) {
auto find = _subscribes.find(fName);
if (find != _subscribes.end())
{
return find->second;
}
return NULL;
}
};
At first some general hints:
Avoid raw pointer usage as far as possible, even for internals! Use std::unique_ptr or std::shared_ptr instead!
Reducing a data conglomerate to a standard container, indexing it via a dynamic data type like std::string and use it in a plain void std::function context results (almost?) always in type erasure and a loss of according type safe outer access. In fact, this even has nothing to do with further differences between plain functions and member methods in the first place.
A possible first solution approach:
This is a minimal working example that should cover your quite dynamic requirements. For me it compiles and runs well with MS VS 2017 (C++17). I tried to use your original structs as far as possible.
#include <variant>
#include <set>
#include <string>
#include <iostream>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
template <typename T> class CallbackBaseTmpl;
template <typename Ret, typename ...Args>
class CallbackBaseTmpl<Ret(Args...)>
{
public:
using Signature = Ret(Args...);
CallbackBaseTmpl(const std::function<Signature>& func) : m_function(func) {}
CallbackBaseTmpl(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class Callback1Type : public CallbackBaseTmpl<bool(int)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback2Type : public CallbackBaseTmpl<bool(std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
class Callback3Type : public CallbackBaseTmpl<bool(int, std::string)>
{
using CallbackBaseTmpl::CallbackBaseTmpl;
};
using CompoundCallbackType = std::variant<Callback1Type, Callback2Type, Callback3Type>;
class CallbackHolder
{
public:
CallbackHolder(const CompoundCallbackType& callbackImpl) : m_callbacksImpl(callbackImpl) {}
inline auto getIndex() const { return m_callbacksImpl.index(); }
inline CompoundCallbackType& getImpl() const { return m_callbacksImpl; }
private:
mutable CompoundCallbackType m_callbacksImpl;
};
class CallbacksContainer
{
public:
template <typename VariantType>
bool subscribe(const VariantType& compoundType)
{
return subscribe(CallbackHolder(compoundType));
}
bool subscribe(const CallbackHolder& cHolder)
{
auto res = m_containerImpl.insert(cHolder);
return res.second;
}
template <typename CallbackType, typename... Args>
auto getSubscribe(Args&&... args)
{
// linear search - can be optimized
for (auto& implEntry : m_containerImpl)
{
bool isWanted = std::visit([&args...](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, CallbackType>)
return true;
else
return false;
}, implEntry.getImpl());
if (isWanted)
return std::get<CallbackType>(implEntry.getImpl()).Func(std::forward<Args>(args)...);
}
throw std::logic_error("Cannot access element");
}
private:
struct CustomComparer {
bool operator() (const CallbackHolder& lhs, const CallbackHolder& rhs) const
{
// Each variant entry allowed only once in the set
return lhs.getIndex() < rhs.getIndex();
}
};
std::set<CallbackHolder, CustomComparer> m_containerImpl;
};
bool start() {
CallbacksContainer ms;
ms.subscribe(Callback1Type(std::bind(&myMainClass::callback1, this, std::placeholders::_1)));
ms.subscribe(Callback2Type(std::bind(&myMainClass::callback2, this, std::placeholders::_1)));
ms.getSubscribe<Callback1Type>(5);
ms.getSubscribe<Callback2Type>("TEST");
ms.subscribe(Callback3Type(std::bind(&myMainClass::callback3, this, std::placeholders::_1, std::placeholders::_2)));
ms.getSubscribe<Callback3Type>(2, "");
return true;
}
};
Explanation: I replaced your original map with an std::set as a kind of registry container so there are still no duplicates allowed. Some efforts are required via Wrappers to achieve the desired final access scheme.
You can easily change the desired registered functions for a type in a dynamic but always very type safe way now. Feel free to extend this scheme for your own purposes. Likely, there are several parts that can be optimized, shortened or extended. Maybe there's also a nice way to avoid this mutable inside the CallbackHolder. The (non-grave for a few functions) linear search within the set can be avoided via an actual typeid sorting and specialized according finding for instance.
Update due to feedback:
If strings as keys are required and a maximum degree of freedom should be given, i.e. any callback type should be providable without the necessity for compile time registration, this solution might be an alternative:
#include <map>
#include <string>
#include <iostream>
#include <functional>
#include <memory>
struct myMainClass {
myMainClass() {}
~myMainClass() {}
bool callback1(int iData) {
std::cout << "callback 1 with iData " << iData << std::endl;
return true;
}
bool callback2(std::string sData) {
std::cout << "callback 2 with sData " << sData << std::endl;
return true;
}
bool callback3(int iData, std::string sData) {
std::cout << "callback 1 with iData " << iData << ", sData " << sData << std::endl;
return true;
}
class ICallback
{
public:
virtual ~ICallback() = default;
};
template <typename T> class TypedCallback;
template <typename Ret, typename ...Args>
class TypedCallback<Ret(Args...)> : public ICallback
{
public:
using Signature = Ret(Args...);
TypedCallback(const std::function<Signature>& func) : m_function(func) {}
TypedCallback(std::function<Signature>&& func) :
m_function(std::move(func)) {}
inline Ret Func(Args&&... args) { return m_function(std::forward<Args>(args)...); }
private:
std::function<Signature> m_function;
};
class CallbacksContainer
{
private:
template <typename T> struct CallTraits {};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename C, typename Ret, typename... Args>
struct CallTraits<Ret(C::*)(Args...) const>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename F>
struct FuncTraits
{
using FuncClass = std::decay_t<F>;
using OperatorSignature = decltype(&FuncClass::operator());
using signature = typename CallTraits<OperatorSignature>::Signature;
using returnType = typename CallTraits<OperatorSignature>::ReturnType;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(*)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
template <typename Ret, typename... Args>
struct FuncTraits<Ret(&)(Args...)>
{
using Signature = Ret(Args...);
using ReturnType = Ret;
};
public:
template <typename T>
bool subscribe(const std::string& key, T&& func)
{
auto res = m_subscriptions.try_emplace(
key, std::make_unique<TypedCallback<typename FuncTraits<T>::signature>>(std::forward<T>(func)));
return res.second;
}
template <typename Ret, typename... Args>
auto getSubscribe(const std::string& key, Args&&... args) const
{
using Signature = Ret(Args...);
const auto& entry = m_subscriptions.at(key);
auto rp = entry.get();
auto typedCB = dynamic_cast<TypedCallback<Signature>*>(rp);
if (typedCB == nullptr)
{
// TODO: Possible further check if functor can be used due to convertible types, for instance
// with an acyclic visitor?
std::logic_error("Wrong callback signature provided.");
}
return typedCB->Func(std::forward<Args>(args)...);
}
private:
std::map<std::string, std::unique_ptr<ICallback>> m_subscriptions;
};
bool start() {
CallbacksContainer ms;
// Usage with non static member methods
ms.subscribe("callback1", [this](int x) { return callback1(x); });
ms.subscribe("callback2", [this](std::string x) { return callback2(x); });
ms.subscribe("callback3", [this](int x, std::string str) { return callback3(x, str); });
// Usage with lambda
ms.subscribe("callback4", [](int y) { return y != 0; });
// Usage with std::function itself
ms.subscribe("callback5", std::function<bool(int)>([](int y) { return y != 0; }));
// Getters - Unfortunately, exact types are required. Maybe acyclic visitor could help here?
ms.getSubscribe<bool>("callback1", 1);
ms.getSubscribe<bool>("callback2", std::string("TEST"));
ms.getSubscribe<bool>("callback3", 1, std::string("TEST"));
ms.getSubscribe<bool>("callback4", 1);
return true;
}
};
PROs:
No static/compile time method signature registration required -> no variants
At least with C++20, method subscription will be an easy going here, added some helper traits to make things a bit easier here already
Only one underlying map used
CONs:
Less type-safety at some points and the dynamic_cast might be a bit slow but might be improved in terms of performance via a simple type index comparison
The getSubscribe() method has to be used with care. Exact types are required here (the former dynamically registered ones) and it doesn't unfortunately support common signature conversion ways. I see currently no way to get rid of this problem with pre C++20 features. Maybe some tricks with a generic acyclic visitor pattern or SFINAE magic + visitor might help here but that breaks the mould by far here I think. If that arises as a real issue, one can still use the chained parameter scheme in doubt, that guarantees type safety on its own.
You have to somehow turn the memberfunction pointers to regular old function pointers, in order to store them in the same container. You have three options that I can come up with:
#include <functional>
struct Foo {
void foo(int x, int y, int z) {}
/*
Putting the instance as the first parameter is crucial, because the
first argument to a member function call is an implicit this. If instance
is not the first parameter the compiler has to shift around the argument
list, otherwise it's a direct forwarding call.
*/
static void callback(void* instance, int x, int y, int z) {
return static_cast<Foo*>(instance)->foo(x, y, z);
}
};
int main() {
Foo foo;
void (*f0)(void*, int, int, int){&Foo::callback};
/*
Capturing lambda cannot decay to function pointer, have to use
std::function or smth. similar
*/
std::function<void(int, int, int)> f1{
[&](int x, int y, int z) { return foo.foo(x, y, z); }};
auto f2 = std::mem_fn(&Foo::foo);
f0(&foo, 1, 2, 3);
f1(1, 2, 3);
f2(&foo, 1, 2, 3);
}
Here's a godbolt with the generated assembly https://godbolt.org/z/K9eM4E
I have the following code where I try to detect what concept(member functions in this case) alternative in variant holds.
It is verbose and relatively ugly.
Is there a nicer way to do this?
Note that I do not want to use inheritance, and I do not want to use static polymorphism(let's assume what engine is used is not known at compile time).
In simple terms I am looking for runtime mix of if constexpr(that only checks concepts at compile time) and std::holds_alternative (that can only check for if specific type is in variant, not if any of the types satisfying the concept is in variant).
#include <iostream>
#include <variant>
struct simple_engine1{
};
struct simple_engine2{
};
struct complex_engine1{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
struct complex_engine2{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
template< class, class = std::void_t<> >
struct has_reduce_thrust : std::false_type { };
template< class T >
struct has_reduce_thrust<T,
std::void_t<decltype( std::declval<T>().reduce_thrust(42) )>
> : std::true_type { };
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(has_reduce_thrust<complex_engine1>::value);
struct visitor{
template<typename T>
void operator()(T& t){
dispatch(t, has_reduce_thrust<T>{});
}
template<typename T>
void dispatch(T& t, std::true_type /*has_reduce_thrust*/){
t.reduce_thrust(perc);
reduced_thrust=true;
}
template<typename T>
void dispatch(T& , std::false_type){
reduced_thrust=false;
}
int perc = 0;
bool reduced_thrust = false;
};
// tries to reduce speed by reducing thrust if engine supports it, if not
// it fires reverse engines(more expensive wrt fuel usage)
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2>* var_engine){
visitor v;
v.perc = 47;
std::visit(v, *var_engine);
if (v.reduced_thrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
int main() {
std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2> var_engine{simple_engine1{}};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
var_engine = simple_engine2{};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
}
You can simplify the visitor a lot by using if constexpr:
struct visitor{
template<typename T>
void operator()(T& t) {
if constexpr (has_reduce_thrust<T>::value) {
t.reduce_thrust(perc);
reduced_thrust = true;
}
else {
reduced_thrust = false;
}
}
int perc = 0;
bool reduced_thrust = false;
};
You could then abstract further by accepting any predicate and two functions for either branch of the if constexpr:
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class MatchedFunc, class UnmatchedFunc>
class predicated_visitor {
public:
predicated_visitor(MatchedFunc matchedFunc, UnmatchedFunc unmatchedFunc)
: _matchedFunc(matchedFunc), _unmatchedFunc(unmatchedFunc) {}
template <typename T>
void operator()(T& t) {
if constexpr (TypePred<T>::value)
_matchedFunc(t);
else
_unmatchedFunc(t);
}
private:
MatchedFunc _matchedFunc;
UnmatchedFunc _unmatchedFunc;
};
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class F1, class F2>
auto makePredicatedVisitor(F1 f1, F2 f2) {
return predicated_visitor<TypePred, F1, F2>(f1, f2);
}
The resulting code is quite nice I feel:
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1,
complex_engine2>* var_engine) {
int perc = 47;
bool reducedThrust = false;
auto reduceableThrustAction = [perc, &reducedThrust](auto& t) {
t.reduce_thrust(perc);
reducedThrust = true;
};
auto alternativeAction = [](auto& t) {
}; // Could explicitly set reduceThrust to false for clarity.
auto thrust_visitor = makePredicatedVisitor<has_reduce_thrust>(
reduceableThrustAction, alternativeAction);
std::visit(thrust_visitor, *var_engine);
if (reducedThrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
Demo
This example compiles to essentially the same assembly code as yours but can be reused in any way you like.
I apologize for the inconsistent capitalization...
I am trying to implement a composite pattern for std::functions with use of template classes, where each composite class processes the return values of its children.
So the pattern classes might look something like this:
class AbstractClass {
public:
virtual void process() = 0;
};
template<typename ReturnType>
class PrimitiveClass : public AbstractClass {
public:
ReturnType process() {
// please note, that the result is not returned by the return statement
return this->func(); //this is just for simplicity
}
private:
std::function<ReturnType()> func;
}
template<typename ReturnType, typename ...Args>
class CompositeClass : public AbstractClass {
public:
ReturnType process() {
// --> This is where I want to process all children first and then pass their return values to this->func
// the following code is kind of a pseudo code:
for(auto it = vector.begin(); it != vector.end(); ++it {
results.add((**it).process())
}
return this->func(results)
}
private:
std::function<ReturnType(Args...)> func;
std::vector<std::shared_ptr<AbstractClass>> children;
};
So for example, I have a CompositeClass with a std::function<int(int, double, bool) and the argument types of that function are also the ReturnTypes of its children. And I want to pass the return values of the children to above-mentioned std::function
Can anyone think of a way, how I can achieve this?
If I understand what you want (and if I'm not wrong)...
(1) to solve the problem of the no-covariant returned value from process() (see comment from Igor Tandetnik) you need a template abstract class to express the correct return value; by example
template <typename T>
struct abstClass
{ virtual T process() const = 0; };
(2) so your CompositeClass (renamed nodeClass, in my following example) inherit from abstClass<ReturnType>
(3) your PrimitiveClass is useless because you can manage the case (reference to a function without arguments) as a CompositeClass with zero Args
(4) you need a leafClass to handle basic values
(5) in CompositeClass (nodeClass), children, instead of a std::vector of shared_ptr<AbstractClass> (that can't do what do you want), can be a
std::tuple<std::shared_ptr<abstClass<Args>>...> children;
Given these points, I propose the following solution (that, unfortunately, is C++14 because use std::index_sequence and std::make_index_sequence that are available starting from C++14; but if you need a C++11 solution, isn't difficult write substitutes for they)
#include <tuple>
#include <memory>
#include <iostream>
#include <functional>
template <typename T>
struct abstClass
{ virtual T process() const = 0; };
template <typename T>
class leafClass : public abstClass<T>
{
private:
T value;
public:
leafClass (T && v0) : value { std::forward<T>(v0) }
{ }
T process () const
{ return value; };
};
template <typename RetT, typename ... ArgTs>
class nodeClass : public abstClass<RetT>
{
private:
using funcT = std::function<RetT(ArgTs...)>;
template <typename T>
using shrPAC = std::shared_ptr<abstClass<T>>;
funcT func;
std::tuple<shrPAC<ArgTs>...> childrens;
template <std::size_t ... Is>
RetT processH (std::index_sequence<Is...> const &) const
{ return func(std::get<Is>(childrens)->process()...); }
public:
nodeClass (funcT && f0, shrPAC<ArgTs> && ... as)
: func { std::forward<funcT>(f0) },
childrens { std::forward<shrPAC<ArgTs>>(as)... }
{ }
RetT process () const
{ return processH(std::make_index_sequence<sizeof...(ArgTs)>{}); }
};
int main ()
{
auto func0 = [](int i, double d, bool b) { return int( b ? i+d : i-d ); };
auto shpLci = std::make_shared<leafClass<int>>(1);
auto shpLcd = std::make_shared<leafClass<double>>(2.2);
auto shpNb = std::make_shared<nodeClass<bool>>([](){ return true; });
auto shpNc0 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpLci, shpLcd, shpNb);
auto shpNc1 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpNc0, shpLcd, shpNb);
auto shpNc2 = std::make_shared<nodeClass<int, int, double, bool>>
(func0, shpNc1, shpLcd, shpNb);
std::cout << shpNc0->process() << std::endl; // print 3
std::cout << shpNc1->process() << std::endl; // print 5
std::cout << shpNc2->process() << std::endl; // print 7
}
I have a template class where each template argument stands for one type of value the internal computation can handle. Templates (instead of function overloading) are needed because the values are passed as boost::any and their types are not clear before runtime.
To properly cast to the correct types, I would like to have a member list for each variadic argument type, something like this:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::vector<T1> m_argumentsOfType1;
std::vector<T2> m_argumentsOfType2; // ...
};
Or alternatively, I'd like to store the template argument types in a list, as to do some RTTI magic with it (?). But how to save them in a std::initializer_list member is also unclear to me.
Thanks for any help!
As you have already been hinted, the best way is to use a tuple:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::tuple<std::vector<AcceptedTypes>...> vectors;
};
This is the only way to multiply the "fields" because you cannot magically make it spell up the field names. Another important thing may be to get some named access to them. I guess that what you're trying to achieve is to have multiple vectors with unique types, so you can have the following facility to "search" for the correct vector by its value type:
template <class T1, class T2>
struct SameType
{
static const bool value = false;
};
template<class T>
struct SameType<T, T>
{
static const bool value = true;
};
template <typename... Types>
class MyClass
{
public:
typedef std::tuple<vector<Types>...> vtype;
vtype vectors;
template<int N, typename T>
struct VectorOfType: SameType<T,
typename std::tuple_element<N, vtype>::type::value_type>
{ };
template <int N, class T, class Tuple,
bool Match = false> // this =false is only for clarity
struct MatchingField
{
static vector<T>& get(Tuple& tp)
{
// The "non-matching" version
return MatchingField<N+1, T, Tuple,
VectorOfType<N+1, T>::value>::get(tp);
}
};
template <int N, class T, class Tuple>
struct MatchingField<N, T, Tuple, true>
{
static vector<T>& get(Tuple& tp)
{
return std::get<N>(tp);
}
};
template <typename T>
vector<T>& access()
{
return MatchingField<0, T, vtype,
VectorOfType<0, T>::value>::get(vectors);
}
};
Here is the testcase so you can try it out:
int main( int argc, char** argv )
{
int twelf = 12.5;
typedef reference_wrapper<int> rint;
MyClass<float, rint> mc;
vector<rint>& i = mc.access<rint>();
i.push_back(twelf);
mc.access<float>().push_back(10.5);
cout << "Test:\n";
cout << "floats: " << mc.access<float>()[0] << endl;
cout << "ints: " << mc.access<rint>()[0] << endl;
//mc.access<double>();
return 0;
}
If you use any type that is not in the list of types you passed to specialize MyClass (see this commented-out access for double), you'll get a compile error, not too readable, but gcc at least points the correct place that has caused the problem and at least such an error message suggests the correct cause of the problem - here, for example, if you tried to do mc.access<double>():
error: ‘value’ is not a member of ‘MyClass<float, int>::VectorOfType<2, double>’
An alternate solution that doesn't use tuples is to use CRTP to create a class hierarchy where each base class is a specialization for one of the types:
#include <iostream>
#include <string>
template<class L, class... R> class My_class;
template<class L>
class My_class<L>
{
public:
protected:
L get()
{
return val;
}
void set(const L new_val)
{
val = new_val;
}
private:
L val;
};
template<class L, class... R>
class My_class : public My_class<L>, public My_class<R...>
{
public:
template<class T>
T Get()
{
return this->My_class<T>::get();
}
template<class T>
void Set(const T new_val)
{
this->My_class<T>::set(new_val);
}
};
int main(int, char**)
{
My_class<int, double, std::string> c;
c.Set<int>(4);
c.Set<double>(12.5);
c.Set<std::string>("Hello World");
std::cout << "int: " << c.Get<int>() << "\n";
std::cout << "double: " << c.Get<double>() << "\n";
std::cout << "string: " << c.Get<std::string>() << std::endl;
return 0;
}
One way to do such a thing, as mentioned in πάντα-ῥεῖ's comment is to use a tuple. What he didn't explain (probably to save you from yourself) is how that might look.
Here is an example:
using namespace std;
// define the abomination
template<typename...Types>
struct thing
{
thing(std::vector<Types>... args)
: _x { std::move(args)... }
{}
void print()
{
do_print_vectors(std::index_sequence_for<Types...>());
}
private:
template<std::size_t... Is>
void do_print_vectors(std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow{0, (print_one(std::get<Is>(_x)), 0)...};
}
template<class Vector>
void print_one(const Vector& v)
{
copy(begin(v), end(v), ostream_iterator<typename Vector::value_type>(cout, ","));
cout << endl;
}
private:
tuple<std::vector<Types>...> _x;
};
// test it
BOOST_AUTO_TEST_CASE(play_tuples)
{
thing<int, double, string> t {
{ 1, 2, 3, },
{ 1.1, 2.2, 3.3 },
{ "one"s, "two"s, "three"s }
};
t.print();
}
expected output:
1,2,3,
1.1,2.2,3.3,
one,two,three,
There is a proposal to allow this kind of expansion, with the intuitive syntax: P1858R1 Generalized pack declaration and usage. You can also initialize the members and access them by index. You can even support structured bindings by writing using... tuple_element = /*...*/:
template <typename... Ts>
class MyClass {
std::vector<Ts>... elems;
public:
using... tuple_element = std::vector<Ts>;
MyClass() = default;
explicit MyClass(std::vector<Ts>... args) noexcept
: elems(std::move(args))...
{
}
template <std::size_t I>
requires I < sizeof...(Ts)
auto& get() noexcept
{
return elems...[I];
}
template <std::size_t I>
requires I < sizeof...(Ts)
const auto& get() const
{
return elems...[I];
}
// ...
};
Then the class can be used like this:
using Vecs = MyClass<int, double>;
Vecs vecs{};
vecs.[0].resize(3, 42);
std::array<double, 4> arr{1.0, 2.0, 4.0, 8.0};
vecs.[1] = {arr.[:]};
// print the elements
// note the use of vecs.[:] and Vecs::[:]
(std::copy(vecs.[:].begin(), vecs.[:].end(),
std::ostream_iterator<Vecs::[:]>{std::cout, ' '},
std::cout << '\n'), ...);
Here is a less than perfectly efficient implementation using boost::variant:
template<typename ... Ts>
using variant_vector = boost::variant< std::vector<Ts>... >;
template<typename ...Ts>
struct MyClass {
using var_vec = variant_vector<Ts...>;
std::array<var_vec, sizeof...(Ts)> vecs;
};
we create a variant-vector that can hold one of a list of types in it. You have to use boost::variant to get at the contents (which means knowing the type of the contents, or writing a visitor).
We then store an array of these variant vectors, one per type.
Now, if your class only ever holds one type of data, you can do away with the array, and just have one member of type var_vec.
I cannot see why you'd want one vector of each type. I could see wanting a vector where each element is one of any type. That would be a vector<variant<Ts...>>, as opposed to the above variant<vector<Ts>...>.
variant<Ts...> is the boost union-with-type. any is the boost smart-void*. optional is the boost there-or-not.
template<class...Ts>
boost::optional<boost::variant<Ts...>> to_variant( boost::any );
may be a useful function, that takes an any and tries to convert it to any of the Ts... types in the variant, and returns it if it succeeds (and returns an empty optional if not).