Mikaka
Mikaka

Reputation: 83

c++ packing and unpacking parameter pack to call matching function pointer without stl

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

Answers (2)

max66
max66

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

Chris A
Chris A

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

Related Questions