bind make_shared with variadic template - c++

I'm trying to write the following factory class, but I can't find the proper syntax:
template<class T, typename... TArgs>
class Factory {
public:
Factory(TArgs... args) {
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
// ^^^ some error around here
}
std::shared_ptr<T> Create() const {
return creator_();
}
private:
std::function<std::shared_ptr<T>()> creator_;
};
This is how I use the factory:
class Foo {
public:
Foo(bool value) {}
};
class Bar {
public:
Bar(const std::string& value) {}
};
Factory<Foo, bool> f1(true);
Factory<Bar, std::string> f2("string");
These are the errors I get when declaring f1 and f2:
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Foo>()>' and 'std::_Bind_helper<false, std::shared_ptr<Foo> (*)(bool&&), bool&>::type {aka std::_Bind<std::shared_ptr<Foo> (*(bool))(bool&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
error: no match for 'operator=' (operand types are 'std::function<std::shared_ptr<Bar>()>' and 'std::_Bind_helper<false, std::shared_ptr<Bar> (*)(std::basic_string<char>&&), std::basic_string<char, std::char_traits<char>, std::allocator<char> >&>::type {aka std::_Bind<std::shared_ptr<Bar> (*(std::basic_string<char>))(std::basic_string<char>&&)>}')
creator_ = std::bind(&std::make_shared<T, TArgs...>, args...);
^
What is the correct syntax I must use with std::bind?

std::make_shared is declared like this:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
As such, std::make_shared<T, TArgs...> will result in a function taking rvalue references, which won't bind to args.... A simple fix for this is to force it to take lvalue references by collapsing the reference:
creator_ = std::bind(&std::make_shared<T,TArgs&...>, args...);
// ^
An alternative is to use a lambda instead, which is more readable:
creator_ = [=](){return std::make_shared<T>(args...);};

So, a maximally efficient C++14 solution that doesn't use bind is actually awkward.
template<class T>
struct Factory {
template<class...Args>
Factory(Args&&... args):
creator_(
make_creator(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)...
)
)
{}
std::shared_ptr<T> operator()() const {
return creator_();
}
private:
using signature = std::shared_ptr<T>();
using creator = std::function<signature>;
creator creator_;
// helper, to make a lambda with a tuple to unpack:
template<class Tup, size_t...Is>
static creator make_creator(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
};
this version has a few improvements.
First, no need to specify arguments you create the T from:
Factory<Foo> f1(true);
Factory<Bar> f2("string");
Second, instead of f1.Create(), we have f1(). Invoking a factory clearly creates the thing the factory creates -- calling a named method is just noise.
We could go a step further:
template<class T>
using Factory = std::function<std::shared_ptr<T>()>;
namespace details {
template<class T, class Tup, size_t...Is>
Factory<T> make_factory(std::index_sequence<Is...>, Tup&& tup) {
return [tup = std::forward<Tup>(tup)]{
return std::make_shared<T>( std::get<Is>(tup)... );
};
}
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return details::make_factory<T>(
std::index_sequence_for<Args...>{},
std::make_tuple( std::forward<Args>(args)... )
);
}
where we do away with the Factory type entirely -- Factory<T> just becomes an alias for a std::function that takes nothing and returns a shared_ptr<T>.
live example.
Now I find details::make_factory to be boring.
namespace details {
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup, std::index_sequence<Is...> )
-> std::result_of_t<F( std::tuple_element_t<Is, std::decay_t<Tup>>... )>
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tup>(tup))... );
}
}
template<class F, class Tup, size_t...Is>
auto invoke( F&& f, Tup&& tup )
{
using count = std::tuple_size< std::decay_t<Tup> >;
using indexes = std::make_index_sequence< count{} >;
return details::invoke(
std::forward<F>(f),
std::forward<Tup>(tup),
indexes{}
);
}
template<class T>
auto shared_maker() {
return [](auto&&...args){
return std::make_shared<T>( decltype(args)(args)... );
};
}
template<class T, class...Args>
Factory<T> make_factory(Args&&...args) {
return [tup=std::make_tuple(std::forward<Args>(args)...)]{
return invoke(
shared_maker<T>(),
tup
);
};
}
live example, where we take the 'invoke a function from a tuple' and write it as invoke separately.
template<class T>
const auto shared_maker = [](auto&&...args){
return std::make_shared<T>(decltype(args)(args)...);
};
would be slightly slicker, but gcc 5.2.0 doesn't like it.

Related

Call lambda with other lambda result when its return type is void

The following function makes a lambda that calls the second callable with the first callable's result. If the first callable returns a tuple it will be applied to the second callable.
template<typename T>
struct is_tuple : std::false_type{};
template<typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type{};
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
However this does not compile when the source callable returns void, since it will try to call target with incomplete type void, so I tried the following:
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else if constexpr(std::is_void_v<source_return>)
{
source(std::forward<decltype(args)>(args)...);
return target();
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
But this doesn't seem to work somehow since it always takes the same as void branch, even when the source function cannot return void in any situation. I geuss there is something wrong with the decltype deducing the source_return. I tried to assign the result of source to a variable to decltype the that variable instead of decltype(source(args...)) but then it gives me the error that the variable is of incomplete type void in the cases it actually does return void, so I do have to check it before actually calling source.
Here is an example of pipeline usage that does not compile:
auto callable = pipeline([]{ return 10 },
[](size_t val){ return val * 10});
callable();
The reason it does not compile is because it takes the source_return is same as void branch for some reason. Anybody has any idea how I can figure out the return type of source when called with args... in a way that is more robust?
EDIT:
I got it to work by using a call_pipeline helper function. I still don't understand why this one would work and the other one doesn't though.
template<typename S, typename T, typename... Args>
constexpr decltype(auto) call_pipeline(const S& source, const T& target, Args&&... args)
{
using source_return = decltype(source(std::forward<Args>(args)...));
if constexpr(std::is_void_v<source_return>)
{
source(std::forward<Args>(args)...);
return target();
}
else
{
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<Args>(args)...));
}
else
{
return target(source(std::forward<Args>(args)...));
}
}
}
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source_init, T&& target_init)
{
return [callables = std::tuple<S, T>(std::forward<S>(source_init),
std::forward<T>(target_init))]
(auto&&... args)
{
const auto&[source, target] = callables;
return call_pipeline(source, target, std::forward<decltype(args)>(args)...);
};
}
Not sure about isn't working your way but I propose the following alternative
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source),
std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
auto tplr = [](auto s, auto && ... as)
{
using source_return
= decltype(s(std::forward<decltype(as)>(as)...));
if constexpr ( is_tuple<source_return>::value )
return s(std::forward<decltype(as)>(as)...);
else if constexpr ( std::is_void_v<source_return> )
{
s(std::forward<decltype(as)>(as)...);
return std::make_tuple();
}
else
return std::make_tuple(s(std::forward<decltype(as)>(as)...));
}(source, std::forward<decltype(args)>(args)...);
std::apply(target, tplr);
};
}
The idea is ever made target() called through std::apply with a std::tuple (maybe empty) of arguments.

How to Deduce Argument List from Function Pointer?

Given two or more example functions, is it possible to write templated code which would be able to deduce the arguments of a function provided as a template parameter?
This is the motivating example:
void do_something(int value, double amount) {
std::cout << (value * amount) << std::endl;
}
void do_something_else(std::string const& first, double & second, int third) {
for(char c : first)
if(third / c == 0)
second += 13.7;
}
template<void(*Func)(/*???*/)>
struct wrapper {
using Args = /*???*/;
void operator()(Args&& ... args) const {
Func(std::forward<Args>(args)...);
}
};
int main() {
wrapper<do_something> obj; //Should be able to deduce Args to be [int, double]
obj(5, 17.4); //Would call do_something(5, 17.4);
wrapper<do_something_else> obj2; //Should be able to deduce Args to be [std::string const&, double&, int]
double value = 5;
obj2("Hello there!", value, 70); //Would call do_something_else("Hello there!", value, 70);
}
In both uses of /*???*/, I am trying to work out what I could put there that would enable this kind of code.
The following doesn't appear to work, due to Args not being defined before its first use (along with what I have to assume are numerous syntax errors besides), and even if it did, I'm still looking for a version that doesn't require explicit writing of the types themselves:
template<void(*Func)(Args ...), typename ... Args)
struct wrapper {
void operator()(Args ...args) const {
Func(std::forward<Args>(args)...);
}
};
wrapper<do_something, int, double> obj;
With C++17 we can have auto template non-type parameters which make possible the Wrapper<do_something> w{} syntax 1).
As for deducing Args... you can do that with a specialization.
template <auto* F>
struct Wrapper {};
template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<F>
{
auto operator()(Args... args) const
{
return F(args...);
}
};
Wrapper<do_something> w{};
w(10, 11.11);
1) Without C++17 it's impossible to have the Wrapper<do_something> w{} nice syntax.
The best you can do is:
template <class F, F* func>
struct Wrapper {};
template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct Wrapper<Ret (Args...), F>
{
auto operator()(Args... args) const
{
return F(args...);
}
};
Wrapper<declype(do_something), do_something> w{};
With C++17, you can do this:
template <auto FUNC, typename = decltype(FUNC)>
struct wrapper;
template <auto FUNC, typename RETURN, typename ...ARGS>
struct wrapper<FUNC, RETURN (*)(ARGS...)> {
RETURN operator()(ARGS ...args) {
return FUNC(args...);
}
};
I've learned this technique from W.F.'s answer
Further improvement of C++17 version: less template parameters and proper noexcept annotation:
template<auto VFnPtr> struct
wrapper;
template<typename TResult, typename... TArgs, TResult ( * VFnPtr)(TArgs...)> struct
wrapper<VFnPtr>
{
TResult
operator ()(TArgs... args) const noexcept(noexcept((*VFnPtr)(::std::forward<TArgs>(args)...)))
{
return (*VFnPtr)(::std::forward<TArgs>(args)...);
}
};
With C++11 you can consider a templated make_wrapper helper function. However, with this approach the function pointer is not a template parameter. Instead, the function pointer is "carried" by the non-static data member called f_ in the following example:
#include <iostream>
void do_something(int value, double amount) {
std::cout << (value * amount) << std::endl;
}
void do_something_else(std::string const& first, double & second, int third) {
for(char c : first)
if(third / c == 0)
second += 13.7;
}
template<class Ret, class... Args>
using function_pointer = Ret(*)(Args...);
template<class Ret, class... Args>
struct wrapper {
using F = function_pointer<Ret, Args...>;
F f_;
explicit constexpr wrapper(F f) noexcept : f_{f} {}
template<class... PreciseArgs>// not sure if this is required
Ret operator()(PreciseArgs&&... precise_args) const {
return f_(std::forward<PreciseArgs>(precise_args)...);
}
};
template<class Ret, class... Args>
constexpr auto make_wrapper(
function_pointer<Ret, Args...> f
) -> wrapper<Ret, Args...> {
return wrapper<Ret, Args...>(f);
}
int main() {
constexpr auto obj = make_wrapper(do_something);
obj(5, 17.4);
constexpr auto obj2 = make_wrapper(do_something_else);
double value = 5;
obj2("Hello there!", value, 70);
return 0;
}

Constructor overloads to accept any function

I am attempting to create a class template whose constructor(s) can take any kind of function as argument, that is, it takes a function pointer (which can be a member function pointer) and the corresponding function arguments. Additionally, there should be a static_assert that checks whether the function return type (taken from the function pointer) matches the class template parameter type. Thus, the code should look something like this:
template <class ReturnType>
struct Bar
{
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType (* func)(ParamType ...), ParamType && ... args) :
package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
template <class RetType, class ObjType, class ... ParamType>
Bar<ReturnType>(RetType (ObjType::* func)(ParamType ...), ObjType * obj, ParamType && ... args) :
package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{
static_assert(std::is_same<ReturnType, RetType>::value,
"Type mismatch between class parameter type and constructor parameter type");
}
std::packaged_task<ReturnType()> package_;
std::function<void()> function_;
std::future<ReturnType> future_;
};
The idea is that the code compiles for these situations, and allows for Bar::function_ to be called (through the function call operator) without errors:
struct Foo
{
int foo(int i) {
return i;
}
int foo() {
return 1;
}
};
int foo(int i)
{
return i;
}
int foo()
{
return 1;
}
int main()
{
Foo f = Foo();
Bar<int> b1(&Foo::foo, &f, 1);
Bar<int> b2(&Foo::foo, &f);
Bar<int> b3(foo, 1);
Bar<int> b4(foo);
return 0;
}
Unfortunately, I have close to zero experience with template metaprogramming, and even though I have ran over several questions here in SO, and attempted several ways of solving my problem, such as using a more generalized approach to the constructor
template <class RetType, class ... ParamType>
Bar<ReturnType>(RetType func, ParamType && ... args)
and combining it with type_traits to determine the return type), I have yet to find a way to make this work. What changes can I do to the constructor(s) that allow this functionality?
Edit:
max66's answer solved my original problem, however, a new one arose, which I hadn't considered in the previous question. I also want to be able to pass variables to the constructor, like so:
int main()
{
Foo f = Foo();
int i = 1;
Bar<int> b1(&Foo::foo, &f, i); // Error
Bar<int> b2(&Foo::foo, &f, 1); // Ok
Bar<int> b3(&Foo::foo, &f); // Ok
Bar<int> b4(foo, i); // Error
Bar<int> b5(foo, 1); // Ok
Bar<int> b6(foo); // Ok
return 0;
}
however, as it is, a compiler error shows up in the cases marked with Error. I am guessing this is because the parameter func in the constructor uses ParamType to determine its type (which doesn't match with the actual ParamTypes in the case of b1 and b4), but I have no idea how to solve this...
You probably want to use std::invoke. It handles working with member function pointers and regular functions for you.
As an outline of the sort of stuff you can do:
#include <functional>
#include <type_traits>
#include <utility>
template<typename F>
class Bar
{
F f_;
public:
template<typename TF>
Bar(TF && f)
: f_{ std::forward<TF>(f) }
{}
template<typename... Args>
decltype(auto) operator()(Args &&... args) {
return std::invoke(f_, std::forward<Args>(args)...);
}
};
template<typename F>
auto make_bar(F && f)
{
return Bar<std::decay_t<F>>{ std::forward<F>(f) };
}
It can be used like so:
auto b1 = make_bar(&f);
auto result = b1(myArg1, myArg2); // etc
auto b2 = make_bar(&Foo::fn);
auto result = b1(foo, arg1);
In the very least, I would recommend having Bar take the function object type as a template parameter so that you don't have to use std::function, but if you do want to use the exact calling syntax you have, it can be done using std::invoke and std::invoke_result as well.
Sorry but... if you want that the return type of the funtion is equal to the template parameter of the class... why don't you simply impose it?
I mean... you can use ReturnType instead of RetType, as follows
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...), ParamType && ... args)
: package_(std::bind(func, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType && ... args)
: package_(std::bind(func, obj, std::forward<ParamType>(args) ...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
-- EDIT --
To solve the second problem, IF your not interested in moving parameters, you can throw away std::forward and &&, and simply write
template <typename ReturnType>
struct Bar
{
template <typename ... ParamType>
Bar<ReturnType> (ReturnType (*func)(ParamType ...),
ParamType const & ... args)
: package_(std::bind(func, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }
template <typename ObjType, typename ... ParamType>
Bar<ReturnType> (ReturnType (ObjType::* func)(ParamType ...),
ObjType * obj, ParamType const & ... args)
: package_(std::bind(func, obj, args...)),
function_([this] { package_(); }),
future_(package_.get_future())
{ }

template function with corresponding parameters to subset of tuple types

I would like to write function as this find:
multi_set<int, string, double, myType> m; //vector of tuples
m.insert(/*some data*/);
m.find<1,2>("something",2.123);
Or
m.find<0,3>(1,instanceOfMyType);
m.find<1>("somethingelse");
Where find can be parametrized corresponding to any subset of tuple parameters.
My code so far:
template <typename ... T>
class multi_set{
typedef tuple < T... > Tuple;
vector<tuple<T...>> data = vector<tuple<T...>>();
public:
void insert(T... t){
data.push_back(tuple<T...>(t...));
}
template<size_t ... Pos>
void find(???){
// then I would like to use those params to search through data and
// return first matching item
}
}
// test whether a particular tuple is a match
template<size_t... Pos>
static bool is_match(const Tuple& tuple, const typename std::tuple_element<Pos, Tuple>::type &... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == args)... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos>
typename vector<Tuple>::const_iterator find(const typename std::tuple_element<Pos, Tuple>::type &... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, args...); });
}
It's also possible to have find take a type parameter pack and perfectly forward, rather than taking fixed types with tuple_element. The benefit is that you can avoid an unnecessary conversion if == is transparent. The cost is that you can't take anything that can't be perfectly forwarded any more (e.g., braced initializer lists, 0 as a null pointer constant). A side benefit appears to be that MSVC 2013 doesn't choke on this version:
// test whether a particular tuple is a match
template<size_t... Pos, class... Args>
static bool is_match(const Tuple& tuple, Args&&... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == std::forward<Args>(args))... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos, class... Args>
typename vector<Tuple>::const_iterator find(Args&&... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, std::forward<Args>(args)...); });
}
You should look into boost::multi_index. It is very close to what you are looking for.
http://www.boost.org/doc/libs/1_54_0/libs/multi_index/doc/tutorial/index.html
This is a function that takes a seed value, and a set of lambdas. It feeds that seed value through each of the lambdas in turn:
template<class... Fs, class R>
R chain( R r, Fs&&... fs ) {
using in_order = int[];
(void)(in_order{0,
(
(r = std::forward<Fs>(fs)( r ))
, void(), 0
)...
});
return r;
}
Inside your class, we use the above:
template<size_t... Pos, class...Us>
typename std::vector<Tuple>::const_iterator
find(Us const&... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && (std::get<Pos>(tup) == us);
}...
);
}
);
}
this compiles in clang, but not g++ 4.9.2 -- g++ doesn't like parameter packs inside lambdas.
Note the fact we take Us const&... -- this allows for transparent ==, which is important in some cases. std::string == char const* is a classic example, where if you force find to take the same value as in the tuple, you'll force a needless allocation in calling find.
In C++1z, the chain call can be replaced with:
( ... && (std::get<Pos>(tup) == us) )
which is conceptually identical, but much easier to read. This is known as a "fold expression".
Now, a problem with the above is that it uses forwarding references, which causes imperfect forwarding problems of perfect forwarding.
The most annoying of which is the inability to use {} to construct arguments.
If we use matching types, we instead force non-transparent comparison, which can be expensive (examine std::string compared to "hello this is a c string" -- it causes possibly allocation if we force the c string into a std::string.)
A way around this is to type erase down to the concept of equality with a given type.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>( *static_cast<std::decay_t<U>*>(const_cast<void*>(p)) );
return F{}( u, std::forward<Ts>(ts)... );
}),
ptr( static_cast<void const*>(std::addressof(u)) )
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{} >>
erase_view_op(U&& u):erase_view_op( std::forward<U>(u), 0 ){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op( block_deduction<U>&& u ):erase_view_op( std::move(u), 0 ){}
erase_view_op( erase_view_op const& ) = default;
erase_view_op( erase_view_op&& ) = default;
R operator()( Ts... ts ) const {
return f( ptr, std::forward<Ts>(ts)... );
}
};
struct equality {
template<class lhs, class rhs>
bool operator()(lhs const& l, rhs const& r)const {
return l==r;
}
};
template<class T>
using erase_equal_to = erase_view_op< equality, bool(T const&), T >;
using string_equal_to = erase_equal_to< std::string >;
int main() {
static_assert( std::is_same< bool, std::result_of_t< std::equal_to<>(decltype("hello"), std::string const&) > >{}, "hmm" );
string_equal_to s = "hello";
string_equal_to s2 = {{"hello"}};
(void)s2;
std::string x = "hello";
std::string y = "jello";
std::cout << s(x) << s(y) << '\n';
}
then we rewrite find:
template<size_t... Pos>
typename std::vector<Tuple>::const_iterator
find(erase_equal_to< std::remove_reference_t<std::tuple_element_t<Pos, Tuple>> >... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && us(std::get<Pos>(tup));
}...
);
}
);
}
which does both transparent equality and allows {} based construction (well, it does require {{}} based construction -- the outer to say we are constructing the eraser, the inner to construct the T).

How can I store generic packaged_tasks in a container?

I'm trying to take a 'task' in the style of std::async and store it in a container. I'm having to jump through hoops to achieve it, but I think there must be a better way.
std::vector<std::function<void()>> mTasks;
template<class F, class... Args>
std::future<typename std::result_of<typename std::decay<F>::type(typename std::decay<Args>::type...)>::type>
push(F&& f, Args&&... args)
{
auto func = std::make_shared<std::packaged_task<typename std::result_of<typename std::decay<F>::type(typename std::decay<Args>::type...)>::type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
auto future = func->get_future();
// for some reason I get a compilation error in clang if I get rid of the `=, ` in this capture:
mTasks.push_back([=, func = std::move(func)]{ (*func)(); });
return future;
}
So I'm using bind -> packaged_task -> shared_ptr -> lambda -> function. How can I do this better/more optimally? It would certainly be easier if there was a std::function which could take a non-copyable but moveable task. Can I std::forward args into the capture of a lambda, or do I have to use bind?
There is no kill like overkill.
Step 1: write a SFINAE friendly std::result_of and a function to help calling via tuple:
namespace details {
template<size_t...Is, class F, class... Args>
auto invoke_tuple( std::index_sequence<Is...>, F&& f, std::tuple<Args>&& args)
{
return std::forward<F>(f)( std::get<Is>(std::move(args)) );
}
// SFINAE friendly result_of:
template<class Invocation, class=void>
struct invoke_result {};
template<class T, class...Args>
struct invoke_result<T(Args...), decltype( void(std::declval<T>()(std::declval<Args>()...)) ) > {
using type = decltype( std::declval<T>()(std::declval<Args>()...) );
};
template<class Invocation, class=void>
struct can_invoke:std::false_type{};
template<class Invocation>
struct can_invoke<Invocation, decltype(void(std::declval<
typename invoke_result<Inocation>::type
>()))>:std::true_type{};
}
template<class F, class... Args>
auto invoke_tuple( F&& f, std::tuple<Args>&& args)
{
return details::invoke_tuple( std::index_sequence_for<Args...>{}, std::forward<F>(f), std::move(args) );
}
// SFINAE friendly result_of:
template<class Invocation>
struct invoke_result:details::invoke_result<Invocation>{};
template<class Invocation>
using invoke_result_t = typename invoke_result<Invocation>::type;
template<class Invocation>
struct can_invoke:details::can_invoke<Invocation>{};
We now have invoke_result_t<A(B,C)> which is a SFINAE friendly result_of_t<A(B,C)> and can_invoke<A(B,C)> which just does the check.
Next, write a move_only_function, a move-only version of std::function:
namespace details {
template<class Sig>
struct mof_internal;
template<class R, class...Args>
struct mof_internal {
virtual ~mof_internal() {};
// 4 overloads, because I'm insane:
virtual R invoke( Args&&... args ) const& = 0;
virtual R invoke( Args&&... args ) & = 0;
virtual R invoke( Args&&... args ) const&& = 0;
virtual R invoke( Args&&... args ) && = 0;
};
template<class F, class Sig>
struct mof_pimpl;
template<class R, class...Args, class F>
struct mof_pimpl<F, R(Args...)>:mof_internal<R(Args...)> {
F f;
virtual R invoke( Args&&... args ) const& override { return f( std::forward<Args>(args)... ); }
virtual R invoke( Args&&... args ) & override { return f( std::forward<Args>(args)... ); }
virtual R invoke( Args&&... args ) const&& override { return std::move(f)( std::forward<Args>(args)... ); }
virtual R invoke( Args&&... args ) && override { return std::move(f)( std::forward<Args>(args)... ); }
};
}
template<class R, class...Args>
struct move_only_function<R(Args)> {
move_only_function(move_only_function const&)=delete;
move_only_function(move_only_function &&)=default;
move_only_function(std::nullptr_t):move_only_function() {}
move_only_function() = default;
explicit operator bool() const { return pImpl; }
bool operator!() const { return !*this; }
R operator()(Args...args) & { return pImpl().invoke(std::forward<Args>(args)...); }
R operator()(Args...args)const& { return pImpl().invoke(std::forward<Args>(args)...); }
R operator()(Args...args) &&{ return std::move(*this).pImpl().invoke(std::forward<Args>(args)...); }
R operator()(Args...args)const&&{ return std::move(*this).pImpl().invoke(std::forward<Args>(args)...); }
template<class F,class=std::enable_if_t<can_invoke<decay_t<F>(Args...)>>
move_only_function(F&& f):
m_pImpl( std::make_unique<details::mof_pimpl<std::decay_t<F>, R(Args...)>>( std::forward<F>(f) ) )
{}
private:
using internal = details::mof_internal<R(Args...)>;
std::unique_ptr<internal> m_pImpl;
// rvalue helpers:
internal & pImpl() & { return *m_pImpl.get(); }
internal const& pImpl() const& { return *m_pImpl.get(); }
internal && pImpl() && { return std::move(*m_pImpl.get()); }
internal const&& pImpl() const&& { return std::move(*m_pImpl.get()); } // mostly useless
};
not tested, just spewed the code. The can_invoke gives the constructor basic SFINAE -- you can add "return type converts properly" and "void return type means we ignore the return" if you like.
Now we rework your code. First, your task are move-only functions, not functions:
std::vector<move_only_function<X>> mTasks;
Next, we store the R type calculation once, and use it again:
template<class F, class... Args, class R=std::result_of_t<std::decay<F>_&&(std::decay_t<Args>&&...)>>
std::future<R>
push(F&& f, Args&&... args)
{
auto tuple_args=std::make_tuple(std::forward<Args>(args)...)];
// lambda will only be called once:
std::packaged_task<R()> task([f=std::forward<F>(f),args=std::move(tuple_args)]
return invoke_tuple( std::move(f), std::move(args) );
});
auto future = func.get_future();
// for some reason I get a compilation error in clang if I get rid of the `=, ` in this capture:
mTasks.emplace_back( std::move(task) );
return future;
}
we stuff the arguments into a tuple, pass that tuple into a lambda, and invoke the tuple in a "only do this once" kind of way within the lambda. As we will only invoke the function once, we optimize the lambda for that case.
A packaged_task<R()> is compatible with a move_only_function<R()> unlike a std::function<R()>, so we can just move it into our vector. The std::future we get from it should work fine even though we got it before the move.
This should reduce your overhead by a bit. Of course, there is lots of boilerplate.
I have not compiled any of the above code, I just spewed it out, so the odds it all compiles are low. But the errors should mostly be tpyos.
Randomly, I decided to give move_only_function 4 different () overloads (rvalue/lvalue and const/not). I could have added volatile, but that seems reckless. Which increase boilerplate, admittedly.
Also my move_only_function lacks the "get at the underlying stored stuff" operation that std::function has. Feel free to type erase that if you like. And it treats (R(*)(Args...))0 as if it was a real function pointer (I return true when cast to bool, not like null: type erasure of convert-to-bool might be worthwhile for a more industrial quality implementation.
I rewrote std::function because std lacks a std::move_only_function, and the concept in general is a useful one (as evidenced by packaged_task). Your solution makes your callable movable by wrapping it with a std::shared_ptr.
If you don't like the above boilerplate, consider writing make_copyable(F&&), which takes an function object F and wraps it up using your shared_ptr technique to make it copyable. You can even add SFINAE to avoid doing it if it is already copyable (and call it ensure_copyable).
Then your original code would be cleaner, as you'd just make the packaged_task copyable, then store that.
template<class F>
auto make_function_copyable( F&& f ) {
auto sp = std::make_shared<std::decay_t<F>>(std::forward<F>(f));
return [sp](auto&&...args){return (*sp)(std::forward<decltype(args)>(args)...); }
}
template<class F, class... Args, class R=std::result_of_t<std::decay<F>_&&(std::decay_t<Args>&&...)>>
std::future<R>
push(F&& f, Args&&... args)
{
auto tuple_args=std::make_tuple(std::forward<Args>(args)...)];
// lambda will only be called once:
std::packaged_task<R()> task([f=std::forward<F>(f),args=std::move(tuple_args)]
return invoke_tuple( std::move(f), std::move(args) );
});
auto future = func.get_future();
// for some reason I get a compilation error in clang if I get rid of the `=, ` in this capture:
mTasks.emplace_back( make_function_copyable( std::move(task) ) );
return future;
}
this still requires the invoke_tuple boilerplate above, mainly because I dislike bind.