Showing posts with label std::forward. Show all posts
Showing posts with label std::forward. Show all posts

Wednesday, November 21, 2012

movable std::bind with placeholders

And finally the placement compatible version of the movable binder.. from 2012/11/perfect-forwarding-bind-compatable-with.html. this is again using threads to show that it works as with move only objects.

Also this probably needs gcc to work as it has a special placeholder classes call std::_Placeholder<>. Also I had to work around a compiler bug that kills the variadic templates nasty little monster that one..

And I still havent done anything about the problem with copying the binder

Ok this code probably needs a bit of explanation
There are several parts to a binder/lambda
  1. The "Binder Object" this is the main body of the binder. in the following code its called "MovableBinder"
  2. The "Bound function" this is the function that is to be executed in at the later point. In the code below its the typedef or template param called "Func"
  3. The "Closure" this is the part of the binder that captures variables in the context that the binder was created in. In the code below its called "MoveCapture"
  4. The "Placeholder" these are placement markers that allow the parameters that are handed in to be jumbled around for the binding interface. In the below code i attempted to use standard std::placeholders. It hasnt really worked out so great.
  5. The "Parameters" these are the params passed to the bind at the point of its execution. In the following code they are cached into the "MoveHolder" class.
This main core of this code design is the MoveCapture.

On Construction it recurses down the varable param decimates the list to an aggregate object that caches 1 param "A" or the placeholder on each step.

On "call" it again recurses down the MoveCapture structure again but this time its its doing the exact opposite and inductively constructing a list of params into the variadic param list called "InArgs".. If it strikes a placeholder it simple retrieves that entry from the MoveHolders object. By the end of structure it has recreated the full param list for the bound function call.


#include <iostream>
#include <memory>
#include <utility>
#include <tuple>
#include <thread>
#include <functional>
#include <string>

template <typename ...H>
struct MoveHolder 
{
  typedef std::tuple<H...>  Tuple;
  Tuple values_;
  
  MoveHolder(H&& ...h) :
    values_(std::forward<H>(h)...)
  {}

  template <int I>
  typename std::tuple_element<I, Tuple>::type&& access()
  {
    return std::move(std::get<I>(std::move(values_)));
  }
};


template <typename A, typename ...Args> 
struct MoveCapture : public MoveCapture<Args...>
{
  A a_;

  MoveCapture(A&& a, Args&& ...args) :
    MoveCapture<Args...>(std::forward<Args>(args)...),
    a_(std::forward<A>(a))
  {
    std::cout << "DEBUG " << __LINE__ << "\n";
  }

  template <typename Func,
     typename H,
       typename ...InArgs>
  void call(Func& f,
       H&& holders,
       InArgs&& ...args)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";

    MoveCapture<Args...>::call(f, 
          holders,
          std::forward<InArgs>(args)...,
          std::move(a_));
  }
};

template <typename A> 
struct MoveCapture<A>
{
  A a_;

  MoveCapture(A&& a) :
    a_(std::forward<A>(a))
  {
    std::cout << "DEBUG " << __LINE__ << "\n";
  }

  template <typename Func,
     typename H,
       typename ...InArgs>
  void call(Func& f,
       H& holders,
       InArgs&& ...args)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";

    f(std::forward<InArgs>(args)...,
      std::move(a_));
  }
};

template <int I, typename ...Args>
struct MoveCapture<const std::_Placeholder<I>, Args...> : public MoveCapture<Args...>
{
  enum { Index = I };
  
  MoveCapture(const std::_Placeholder<I>& h, Args&& ...args) :
    MoveCapture<Args...>(std::forward<Args>(args)...)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";
  }

  //WARNING complier BUG!
  // COMMENTING THIS WILL CAUSE "call" to fail build
  template <typename H>
  void access(H& holders)
  {
    std::cout << I << "\n";
  }
  //END BUG

  template <typename Func,
     typename H,
       typename ...InArgs>
  void call(Func& f,
       H& holders,
       InArgs&& ...args)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";

    MoveCapture<Args...>::call(f,
          holders,
          std::forward<InArgs>(args)...,
          std::move(holders.access<I-1>()));
  }
};

template <int I>
struct MoveCapture<const std::_Placeholder<I>>
{
  MoveCapture(const std::_Placeholder<I>& h) 
  {
    std::cout << "DEBUG " << __LINE__ << "\n";    
  }

  //WARNING complier BUG!
  // COMMENTING THIS WILL CAUSE "call" to fail build
  template <typename H>
  void access(H& holders)
  {
    std::cout << I << "\n";
  }
  //END BUG

  template <typename Func,
     typename H,
       typename ...InArgs>
  void call(Func& f,
       H& holders,
       InArgs&& ...args)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";

    f(std::forward<InArgs>(args)...,
      std::move(holders.access<(I-1)>()));
  }
};

//template <typename ...P>
template <typename Func, typename Capture>
class MovableBinder
{
private:
  Func func_;
  Capture* capture_;

public:
  MovableBinder(Func func, Capture* cap) :
    func_(func),
    capture_(cap)
  {}

  ~MovableBinder()
  {
    if (capture_!= NULL)
      delete capture_ ;
  }

  template <typename ...V>
  void operator()(V&& ...v)
  {
    std::cout << "DEBUG " << __LINE__ << "\n";
    MoveHolder<V...> holders(std::forward<V>(v)...);
    capture_->call(func_, holders);
  }
};

template <typename ...P>
struct MovableSetup
{
  typedef void (*Func)(P&&...);

  template <typename ...H>
  static MovableBinder<Func,MoveCapture<H...>> setup(Func f, H ...h)
  {
    typedef MoveCapture<H...> Capture;

    std::cout << "DEBUG " << __LINE__ << "\n";
    Capture* capture =  new MoveCapture<H...>(std::forward<H>(h)...);
    return MovableBinder<Func,Capture>(f,capture);
  }
};

void hello(double&& done)
{
  std::cout << done << "\n";
}  

void hello2(double&& done1,
     double&& done2)
{
  std::cout << " part1:" << done1 
     << " part2:" << done2 
     << "\n";
}  

void thread_core() 
{
  std::this_thread::sleep_for(std::chrono::seconds(2));
}

void join_thread(std::thread&& t1)
{
  std::cout << "Joining the thread....\n";
  t1.join();
}

int main()
{
  std::cout << "******** MANUAL USAGE **********\n";
  
  MoveCapture<const std::_Placeholder<1> > capture(std::placeholders::_1);
  MoveHolder<double> param(1.23);
  std::cout << param.access<0>() << "\n";
  
  capture.call(hello, param);
  
  std::cout << "******** NORMAL USAGE *********\n";
  
  auto movable_binder_1a = MovableSetup<double>::setup<const std::_Placeholder<1> >(hello,std::placeholders::_1);
  movable_binder_1a(1.34);

  auto movable_binder_1b = MovableSetup<double>::setup(hello,3.14);
  movable_binder_1b();

  auto movable_binder_2a = 
    MovableSetup<double, double>::setup<const std::_Placeholder<1>,
       const std::_Placeholder<2>>
          (hello2, std::placeholders::_1, std::placeholders::_2);
  movable_binder_2a(4.56,1.37);

  auto movable_binder_2b = 
          MovableSetup<double, double>::setup<double,
           double>
          (hello2,492.54, 743.293);
  movable_binder_2b();

  auto movable_binder_2c = 
          MovableSetup<double, double>::setup<double,
       const std::_Placeholder<2>>
          (hello2,492.54, std::placeholders::_2);
  movable_binder_2c(4.56,1.37);  //trash param 1

  std::cout << "******** THREAD BIND USAGE *********\n";

  std::thread tc(thread_core);
  auto movable_binder_tc = 
    MovableSetup<std::thread>::setup(join_thread, std::move(tc));
  movable_binder_tc();

  std::thread td(thread_core);
  auto movable_binder_td = 
    MovableSetup<std::thread>::setup<const std::_Placeholder<1> >(join_thread, std::placeholders::_1);
  movable_binder_td(std::move(td));

}

Tuesday, November 13, 2012

perfect forwarding std::bind replacement compatable with std::move

And without any more messing around here is the generic version of the movable std::bind replacement.

  1. This version doesnt handle place holders yet.
  2. There is probably problems if you try to copy it
Update: I since added a placeholder version here
#include <iostream>
#include <memory>
#include <utility>
#include <thread>

//A helper class for storage and stitching params together
template <typename A, typename ...Args> 
struct MoveHelper : public MoveHelper<Args...>
{
  A a_;

  MoveHelper(A&& a, Args&& ...args) :
    MoveHelper<Args...>(std::forward<Args>(args)...),
    a_(std::forward<A>(a))
  {}

  template <typename Func, typename ...InArgs>
  void call(Func& f, InArgs && ...args)
  {
    MoveHelper<Args...>::call(f,
         std::forward(args)..., 
         std::move(a_));
  }
};

//The helpers terminal case
template <typename A> 
struct MoveHelper<A>
{
  A a_;

  MoveHelper(A&& a) :
    a_(std::forward<A>(a))
  {}

  template <typename Func, typename ...InArgs>
  void call(Func& f, InArgs && ...args)
  {
    f(std::forward<A>(args)...,
      std::forward<A>(a_));
  }
};

//the Main std::bind movable replacement
template <typename ...P>
class MovableBinder
{
  typedef void (*F)(P&&...);

private:
  F func_;
  MoveHelper<P...> help_;

public:
  MovableBinder(F func, P&& ...p) :
    func_(func),
    help_(std::forward<P>(p)...)
  {}

  MovableBinder(F func, P& ...p) :
    func_(func),
    help_(p...)
  {}
    
  ~MovableBinder()
  {}

  void operator()()
  {
    help_.call(func_);
  }
};

//And the test with threads.. the ultimate move only object...

void thread_core() {
  std::this_thread::sleep_for(std::chrono::seconds(2));
}

void join_thread(std::thread&& t1)
{
  std::cout << "Joining the thread....\n";
  t1.join();
}  

void join_threads(std::thread&& t1, std::thread&& t2)
{
  std::cout << "Joining the threads....\n";
  t1.join();
  t2.join();
}  

int main()
{
  std::thread ta(thread_core);
  std::thread tb(thread_core);
  MovableBinder<std::thread, std::thread> movable_binder_threads(&join_threads, std::move(ta),std::move(tb));

  movable_binder_threads();

  std::thread tc(thread_core);
  MovableBinder<std::thread> movable_binder_thread(&join_thread, std::move(tc));

  movable_binder_thread();

}

Sunday, November 11, 2012

perfect forwarding std::bind - why is std::bind not compatable with std::move??

Update: I have since posted the generic form of this code over Here

This is another oddity in the new spec. the std::bind and lambda are not able to take std:move()'ed items. The crazy part is that the standards community went so far as to define a std::forward to achieve perfect forwarding and didn't even take the next logical step and create a perfect forwarding version of std::bind and lamba...

So I hacked up a perfect forwarding verion of std::bind. This version is the one parameter version of the perfect forwarding std::bind. Still working on the variadic template version for unlimited params.

#include <iostream>
#include <memory>
#include <utility>

class MoveOnlyObj
{
public:
  MoveOnlyObj(int val) :
  val_(val)
  {
    std::cout << "MoveOnlyObj() - " << val_ << "\n";
  }
  
  MoveOnlyObj(MoveOnlyObj&& obj) :
  val_(obj.val_)
  {
    std::cout << "MoveOnlyObj(&&) - " << val_ << "\n";
    obj.val_ = 0; //0 it to make it very visable
  }

  ~MoveOnlyObj()
  {
    std::cout << "~MoveOnlyObj - " << val_ << "\n";
  }

private:
  friend std::ostream& operator<<(std::ostream& out, const MoveOnlyObj& o);
  MoveOnlyObj(MoveOnlyObj& obj);
  
  int val_;
};

std::ostream& operator<<(std::ostream& out, const MoveOnlyObj& o)
{
  out << o.val_;
  return out;
}

  
//lets start with the basic
template <typename P>
class MovableBinder1
{
  typedef void (*F)(P&&);

private:
  F func_;
  P p0_;

public:
  MovableBinder1(F func, P&& p) :
    func_(func),
    p0_(std::forward<P>(p))
  {
    std::cout << "Moved" << p0_ << "\n";
  }

  MovableBinder1(F func, P& p) :
    func_(func),
    p0_(p)
  {
    std::cout << "Copied" << p0_ << "\n";
  }
    
  ~MovableBinder1()
  {
    std::cout << "~MovableBinder1\n";
  }

  void operator()()
  {
    (*func_)(std::forward<P>(p0_));
  }
};

void test_func(int&& i)
{
  std::cout << "test_func: " << i << "\n";
}

void move_func(MoveOnlyObj&& i)
{
  MoveOnlyObj taker(std::move(i));
  std::cout << "move_func: " << taker << "\n";
}

int main()
{
  MovableBinder1<int> movable_binder_1_rvalue(&test_func, 3);
  movable_binder_1_rvalue();

  int i=4;
  MovableBinder1<int> movable_binder_1_lvalue(&test_func, i);
  movable_binder_1_lvalue();

  MoveOnlyObj m(5);
  MovableBinder1<MoveOnlyObj> movable_binder_move_only(&move_func, std::move(m));
  movable_binder_move_only();
}

And the output looks like this:
Moved3
test_func: 3
Copied4
test_func: 4
MoveOnlyObj() - 5
MoveOnlyObj(&&) - 5
Moved5
MoveOnlyObj(&&) - 5
move_func: 5
~MoveOnlyObj - 5
~MovableBinder1
~MoveOnlyObj - 0
~MoveOnlyObj - 0
~MovableBinder1
~MovableBinder1


And here is the gripper... This one passes a thread in as the parameter..
#include <iostream>
#include <memory>
#include <utility>
#include <thread>

//lets start with the basic
template <typename P>
class MovableBinder1
{
  typedef void (*F)(P&&);

private:
  F func_;
  P p0_;

public:
  MovableBinder1(F func, P&& p) :
    func_(func),
    p0_(std::forward<P>(p))
  {}

  MovableBinder1(F func, P& p) :
    func_(func),
    p0_(p)
  {}
    
  ~MovableBinder1()
  {}

  void operator()()
  {
    (*func_)(std::forward<P>(p0_));
  }
};

void thread_core() 
{
  std::this_thread::sleep_for(std::chrono::seconds(3));
}

void join_thread(std::thread&& t)
{
  std::cout << "Joining the thread....\n";
  t.join();
}  

int main()
{
  std::thread t(thread_core);
  MovableBinder1<std::thread> movable_binder_thread(&join_thread, std::move(t));

  movable_binder_thread();
}