Reputation: 83
I'm trying to create a class which will store pointer to function with variable number of arguments, and call it later.
The idea is to create a wrapper for function that will call said function when the object destructs. That way I can for example ensure some cleanup happens after exiting some function.
What I have now is a little modified code written by Faheem Mitha posted here.
Here is the working code with example (I'm compiling this with Visual Studio 2015):
#include "stdafx.h"
#include <tuple>
#include <iostream>
using namespace std;
template<int...> struct seq {};
template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {};
template<int... S> struct gens<0, S...> { typedef seq<S...> type; };
template<typename Return, typename... Args> class CallOnExit
{
Return(*_func)(Args...);
std::tuple<Args...> _args;
template<int... S> Return call_func(seq<S...>)
{
return _func(std::get<S>(_args)...);
}
Return call()
{
return call_func(typename gens<sizeof...(Args)>::type());
}
public:
CallOnExit(Return(*func)(Args...), Args&&... args)
{
_func = func;
_args = std::forward_as_tuple(args...);
}
~CallOnExit()
{
if (_func != nullptr)
call();
}
};
void f(int i, double d)
{
cout << i << endl << d << endl;
}
int main()
{
{
CallOnExit<void, int, double> c(f, 1, 2);
}
cin.get();
return 0;
}
The problem is I have to make this work on Arduino platform where stl is not available (so no std::tuple, no std::forward, no std::get). C++11 is supported on Arduino.
What is the minimal amount of work required to make this example work without stl?
Upvotes: 4
Views: 1268
Reputation: 66230
Hoping this helps
#include <tuple>
#include <iostream>
using namespace std;
template<int...> struct seq {};
template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {};
template<int... S> struct gens<0, S...> { typedef seq<S...> type; };
// NEW
template <int N, typename ... Args>
struct typeCont;
template <typename A0, typename ... Args>
struct typeCont<0, A0, Args...>
{ using type = A0; };
template <int N, typename A0, typename ... Args>
struct typeCont<N, A0, Args...>
{ using type = typename typeCont<N-1, Args...>::type; };
// NEW
template <typename X, typename ... Args>
class packCont;
template <typename X, typename A0, typename ... Args>
class packCont<X, A0, Args...>
{
public:
X x;
packCont<A0, Args...> n;
public:
packCont (X const & x0, A0 const & a0, Args const & ... args)
: x(x0), n(a0, args...)
{ }
X const & getX () const
{ return x; }
packCont<A0, Args...> const & getN () const
{ return n; }
};
template <typename X>
class packCont<X>
{
private:
X x;
public:
packCont (X const & x0)
: x(x0)
{ }
X const & getX () const
{ return x; }
};
// NEW
template <int N>
struct getPc
{
template <typename ... Args>
static typename typeCont<N, Args...>::type const &
func (packCont<Args...> const & pc)
{ return getPc<N-1>::func(pc.getN()); }
};
template <>
struct getPc<0>
{
template <typename A0, typename ... Args>
static A0 const & func (packCont<A0, Args...> const & pc)
{ return pc.getX(); }
};
template<typename Return, typename... Args> class CallOnExit
{
Return(*_func)(Args...);
packCont<Args...> _args; // MODIFIED
// MODIFIED
template<int... S> Return call_func(seq<S...>)
{
return _func( getPc<S>:: template func<Args...>(this->_args) ... );
}
Return call()
{
return call_func(typename gens<sizeof...(Args)>::type());
}
public:
// MODIFIED
CallOnExit(Return(*func)(Args...), Args&&... args)
: _func(func), _args(args...)
{ }
~CallOnExit()
{
if (_func != nullptr)
call();
}
};
void f(int i, double d)
{
cout << i << endl << d << endl;
}
int main()
{
{
CallOnExit<void, int, double> c(f, 1, 2);
}
cin.get();
return 0;
}
Upvotes: 0
Reputation: 1485
The idea is to create a wrapper for function that will call said function when the object destructs. That way I can for example ensure some cleanup happens after exiting some function.
The code you are trying to use seems like something that has been a little over thought, or at least tailored to some other specific uses. For the quote above, here is how I would accomplish the task using lambdas and simple functions.
Now the calls can be as custom as you like, and the implementation stays nice and simple:
template< typename T > struct TRef{
TRef(T &in) : t(in) {}
~TRef(){ t(); }
T t;
};
template< typename T > TRef<T> RunOnExit(T t){
return TRef<T>( t );
}
void setup() {
Serial.begin(9600);
}
void loop() {
int a = 3, b = 4;
auto test = RunOnExit( [](){ func(1, 2); } );
{
auto test1 = RunOnExit( [&a, &b](){ func(a, b); } );
}
}
void func( int a, int b){
auto pause = RunOnExit( wait );
Serial.print( a, HEX );
Serial.print( ", " );
Serial.println( b, HEX );
}
void wait(){ delay(500); }
Upvotes: 2