Skip to content

Instantly share code, notes, and snippets.

@KholdStare
Last active May 2, 2017 06:42
Show Gist options
  • Save KholdStare/4313633 to your computer and use it in GitHub Desktop.
Save KholdStare/4313633 to your computer and use it in GitHub Desktop.
Example code from articles on perfect forwarding to async lambdas. Involves moves, async, templates and perfect forwarding.
PROGS=part1 part2
CC=g++-4.7
CFLAGS= -Wall -Werror -O3 -std=c++0x
LIBS= -lpthread
all: $(PROGS)
%: %.cpp
$(CC) $(CFLAGS) -o $@ $^ $(INC_DIRS:%=-I%) $(LIB_DIRS:%=-L%) $(LIBS)
.PHONY: clean
clean:
rm $(PROGS)
#ifndef MOVE_CHECKER_HPP_12ZK6OPS
#define MOVE_CHECKER_HPP_12ZK6OPS
#include <vector>
#include <utility>
#include <memory>
#include <iostream>
class move_checker
{
// shared counters of copies and moves
std::shared_ptr<int> copies_;
std::shared_ptr<int> moves_;
public:
// expensive payload
std::vector<int> payload;
typedef std::vector<int>::const_iterator const_iterator;
move_checker()
: copies_(new int(0)),
moves_(new int(0)),
payload({1, 2, 3, 4, 5, 6, 7})
{ }
// copy constructor. counts copy operations
move_checker(move_checker const& other)
: copies_(other.copies_),
moves_(other.moves_),
payload(other.payload)
{
*copies_ += 1;
}
// copy assignment
move_checker& operator = (move_checker const& other)
{
copies_ = other.copies_;
moves_ = other.moves_;
payload = other.payload;
*copies_ += 1;
return *this;
}
// move constructor. counts move operations
move_checker(move_checker&& other)
: copies_(other.copies_),
moves_(other.moves_),
payload(std::move(other.payload))
{
*moves_ += 1;
}
// move assignment
move_checker& operator = (move_checker&& other)
{
copies_ = other.copies_;
moves_ = other.moves_;
payload = std::move(other.payload);
*moves_ += 1;
return *this;
}
const_iterator begin() const { return payload.begin(); }
const_iterator end() const { return payload.end(); }
// methods to report on the number of copies/moves
int copies() const { return *copies_; }
int moves() const { return *moves_; }
};
void check_copies(move_checker const& checker, int expected)
{
std::cout << "Copies: " << checker.copies()
<< " (expected " << expected << ")" << std::endl;
}
void check_moves(move_checker const& checker, int expected)
{
std::cout << "Moves: " << checker.moves()
<< " (expected " << expected << ")" << std::endl;
}
#endif /* end of include guard: MOVE_CHECKER_HPP_12ZK6OPS */
#include "move_checker.hpp"
#include <vector>
#include <string>
#include <thread>
#include <future>
#include <iostream>
#include <cassert>
#include <functional>
class vector_holder
{
std::vector<std::string> vec_;
public:
// copy a vector into this object.
vector_holder(std::vector<std::string> const& vec)
: vec_(vec)
{
std::cout << "copied!" << std::endl;
}
// move a vector into this object.
// essentially a transfer of ownership
vector_holder(std::vector<std::string>&& vec)
: vec_(std::move(vec))
{
std::cout << "moved!" << std::endl;
}
// move constructor.
// instead of copying, we transfer
// the internals into this object
vector_holder(vector_holder&& other)
: vec_(std::move(other.vec_))
{ }
};
// prints the contents of an iterable
template <typename Iterable>
void printContents(Iterable const& iterable)
{
for (auto e : iterable)
{
std::cout << e << std::endl;
}
}
void asyncWithRvalue()
{
move_checker checker;
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
// (hopefully) pass by reference
std::future<void> task =
std::async(
std::launch::async,
printContents<move_checker>,
std::move(checker)
);
// wait for the task to complete
task.wait();
// no copies or moves!
check_copies(checker, 0 );
check_moves(checker, 2 );
}
void asyncWithLvalue()
{
move_checker checker;
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
// (hopefully) pass by reference
std::future<void> task =
std::async(
std::launch::async,
printContents<move_checker>,
checker
);
// wait for the task to complete
task.wait();
// no copies or moves!
check_copies(checker, 1 );
check_moves(checker, 1 );
}
void asyncWithPointer()
{
move_checker checker;
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
// Wrap the reference to fool std::async
std::future<void> task =
std::async(
std::launch::async,
printContents<move_checker>,
std::ref(checker)
);
// wait for the task to complete
task.wait();
// no copies or moves!
check_copies(checker, 0 );
check_moves(checker, 0 );
}
int main(int argc, char const *argv[])
{
// lvalue vs rvalue
std::vector<std::string> lvalue{"Hello", "World"};
vector_holder copy_holder(lvalue);
vector_holder move_holder(std::move(lvalue));
asyncWithRvalue();
asyncWithLvalue();
asyncWithPointer();
return 0;
}
#include "move_checker.hpp"
#include <utility>
#include <future>
#include <cassert>
template <typename T> struct async_forwarder;
template <typename T>
class async_forwarder
{
T val_;
public:
async_forwarder(T&& t) : val_(std::move(t)) { }
async_forwarder(async_forwarder const& other) = delete;
async_forwarder(async_forwarder&& other)
: val_(std::move(other.val_)) { }
operator T&& () { return std::move(val_); }
operator T&& () const { return std::move(val_); }
};
template <typename T>
class async_forwarder<T&>
{
T& val_;
public:
async_forwarder(T& t) : val_(t) { }
async_forwarder(async_forwarder const& other) = delete;
async_forwarder(async_forwarder&& other)
: val_(other.val_) { }
operator T& () { return val_; }
operator T const& () const { return val_; }
};
// forward our move_checker to count moves/copies
template <typename T>
int forwardToLambda(T&& checker)
{
auto lambda =
[](T&& checker) mutable
{
return checker.payload[0];
};
return lambda(std::forward<T>(checker));
}
template <typename InputIterable, typename Func>
std::future<void> connect(InputIterable&& input, Func func)
{
return std::async(std::launch::async,
[func](InputIterable&& input) mutable
{
// have to "dereference" our wrapper.
for (auto&& e : input) {
func(e);
}
},
async_forwarder<InputIterable>(std::forward<InputIterable>(input))
);
}
// simply print a value on a separate line
template <typename T>
void printOnLine(T const& val)
{
std::cout << val << std::endl;
}
int main(int argc, char const *argv[])
{
move_checker checker;
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
forwardToLambda(checker);
// no copies or moves!
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
auto solutionWithLvalue = connect(checker, printOnLine<int>);
solutionWithLvalue.wait();
assert( checker.copies() == 0 );
assert( checker.moves() == 0 );
auto solutionWithRvalue = connect(std::move(checker), printOnLine<int>);
solutionWithRvalue.wait();
check_copies(checker, 0 );
check_moves(checker, 3 );
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment