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();
}

No comments:

Post a Comment