Difficulty in verifying valide calls with boost::hana::is_valid - c++

I have a class Foo that can be constructed from C-style strings, string views and non-temporary strings (in reality it contains other members and methods, and it is templated on the character to pass to basic_string*s templates):
struct Foo {
explicit constexpr Foo()
: text{}
{}
explicit constexpr Foo(std::string_view text)
: text{std::move(text)}
{}
explicit constexpr Foo(char const* text)
: Foo{std::string_view{text}}
{}
explicit constexpr Foo(char* text)
: Foo{std::string_view{text}}
{}
explicit constexpr Foo(std::string&&) = delete;
std::string_view text;
};
With the help of Boost.Hana, I can assert what Foo can be constructed from and what not, for documentation purposes, in a test:
for_each(make_basic_tuple(
type_c<std::string>,
// clearly I'm not also listing type_c<int> and all the countless imaginable types that wouldn't work
type_c<std::string&&>
),
[](auto t){
static_assert(!std::is_constructible_v<Foo, typename decltype(t)::type>);
});
for_each(make_basic_tuple(
type_c<char*>,
type_c<char const*>,
// ...
type_c<std::string_view>,
type_c<std::string const&>
),
[](auto t){
static_assert(std::is_constructible_v<Foo, typename decltype(t)::type>);
});
But via Boost.Hana, a make_line helper function is defined too:
namespace boost::hana {
template <>
struct make_impl<Foo> {
static constexpr Foo apply(const char* text) {
return Foo{text};
}
static constexpr Foo apply(std::string const& text) {
return Foo{text};
}
static constexpr Foo apply(std::string_view text) {
return Foo{std::move(text)};
}
static constexpr Foo apply(std::string&&) = delete;
};
}
inline constexpr auto make_foo = boost::hana::make<Foo>;
and I can easily verify that it works only with the intendend category values of the arguments:
make_foo("");
make_foo(""sv);
make_foo(sv);
make_foo(s);
//make_foo(std::move(s)); // correctly doesn't compile
//make_foo(""s); // correctly doesn't compile
However, I'm not able to write this in a test via hana::is_valid. Here's my failed attampt:
std::string s{};
std::string_view sv{};
constexpr auto can_make_foo_from =
is_valid([](auto&& obj) -> decltype(make_foo(std::forward<decltype(obj)>(obj))){});
static_assert( decltype(can_make_foo_from(""))::value);
static_assert( decltype(can_make_foo_from(""sv))::value);
static_assert( decltype(can_make_foo_from(sv))::value);
static_assert( decltype(can_make_foo_from(s))::value);
//static_assert(!decltype(can_make_foo_from(std::move(s)))::value);
//static_assert(!decltype(can_make_foo_from(""s))::value);
where in my intentions the last 2 lines should compile, but they don't.
Here's the full example on Compiler Explorer.

From boost/hana/fwd/core/make.hpp we can see:
template <typename Tag>
struct make_t {
template <typename ...X>
constexpr decltype(auto) operator()(X&& ...x) const {
return make_impl<Tag>::apply(static_cast<X&&>(x)...);
}
};
template <typename Tag>
BOOST_HANA_INLINE_VARIABLE constexpr make_t<Tag> make{};
decltype(auto) doesn't trigger SFINAE.
So any make<MyType>(Ts...) is valid, but can generate hard errors.
Fix would be:
template <typename Tag>
struct make_t {
template <typename ...X>
constexpr auto operator()(X&& ...x) const
-> decltype(make_impl<Tag>::apply(static_cast<X&&>(x)...))
{
return make_impl<Tag>::apply(static_cast<X&&>(x)...);
}
};
Demo

Related

A type trait to detect functors using C++17?

Problem description:
C++17 introduces std::invocable<F, Args...>, which is nice to detect if a type... is invocable with the given arguments. However, would there be a way to do it for any arguments for functors (because combinations of the existing traits of the standard library already allow to detect functions, function pointers, function references, member functions...)?
In other words, how to implement the following type trait?
template <class F>
struct is_functor {
static constexpr bool value = /*using F::operator() in derived class works*/;
};
Example of use:
#include <iostream>
#include <type_traits>
struct class0 {
void f();
void g();
};
struct class1 {
void f();
void g();
void operator()(int);
};
struct class2 {
void operator()(int);
void operator()(double);
void operator()(double, double) const noexcept;
};
struct class3 {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
union union0 {
unsigned int x;
unsigned long long int y;
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
struct final_class final {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
int main(int argc, char* argv[]) {
std::cout << is_functor<int>::value;
std::cout << is_functor<class0>::value;
std::cout << is_functor<class1>::value;
std::cout << is_functor<class2>::value;
std::cout << is_functor<class3>::value;
std::cout << is_functor<union0>::value;
std::cout << is_functor<final_class>::value << std::endl;
return 0;
}
should output 001111X. In an ideal world, X should be 1, but I don't think it's doable in C++17 (see bonus section).
Edit:
This post seems to present a strategy that solves the problem. However, would there be a better/more elegant way to do it in C++17?
Bonus:
And as a bonus, would there be a way to make it work on final types (but that's completely optional and probably not doable)?
Building on my answer to my answer to this qustion, i was able to solve your problem, including the bonus one :-)
The following is the code posted in the other thread plus some little tweaks to get a special value when an object can't be called. The code needs c++17, so currently no MSVC...
#include<utility>
constexpr size_t max_arity = 10;
struct variadic_t
{
};
struct not_callable_t
{
};
namespace detail
{
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hece, to 'simulate' an arbitrary function signature.
template <size_t>
struct arbitrary_t
{
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&& ();
template <typename T>
operator T& ();
};
template <typename F, size_t... Is,
typename U = decltype(std::declval<F>()(arbitrary_t<Is>{}...))>
constexpr auto test_signature(std::index_sequence<Is...>)
{
return std::integral_constant<size_t, sizeof...(Is)>{};
}
template <size_t I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{}))
{
return {};
}
template <size_t I, typename F, std::enable_if_t<(I == 0), int> = 0>
constexpr auto arity_impl(...) {
return not_callable_t{};
}
template <size_t I, typename F, std::enable_if_t<(I > 0), int> = 0>
constexpr auto arity_impl(...)
{
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, size_t MaxArity = 10>
constexpr auto arity_impl()
{
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity + 1, F>(0);
if constexpr(std::is_same_v<std::decay_t<decltype(tmp)>, not_callable_t>) {
return not_callable_t{};
}
else if constexpr (tmp == MaxArity + 1)
{
// if that works, F is considered variadic
return variadic_t{};
}
else
{
// if not, tmp will be the correct arity of F
return tmp;
}
}
}
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity(F&& f) { return detail::arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity_v = detail::arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, size_t MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_t>;
// HERE'S THE IS_FUNCTOR
template<typename T>
constexpr bool is_functor_v = !std::is_same_v<std::decay_t<decltype(arity_v<T>)>, not_callable_t>;
Given the classes in yout question, the following compiles sucessfully (you can even use variadic lambdas:
constexpr auto lambda_func = [](auto...){};
void test_is_functor() {
static_assert(!is_functor_v<int>);
static_assert(!is_functor_v<class0>);
static_assert(is_functor_v<class1>);
static_assert(is_functor_v<class2>);
static_assert(is_functor_v<class3>);
static_assert(is_functor_v<union0>);
static_assert(is_functor_v<final_class>);
static_assert(is_functor_v<decltype(lambda_func)>);
}
See also a running example here.

Create an overload set class relying on std::invoke with classical overload resolution rules

I would like to design a class:
template <class... F> class overload_set;
that will take a list of callables at construction, and will have an operator() that will apply the classical overload resolutions rules to decide which function to call.
For two functions, this would be something like this:
template <class F0, class F1>
struct overload_set: F0, F1
{
constexpr overload_set(F0 f0, F1 f1): F0(f0), F1(f1) {}
using F0::operator();
using F1::operator();
};
However, this would only work with function objects, while I would like it to work with any callable working with std::invoke (free functions, functors, lambdas, functions members, members...). And I would like it of course, to work with all the subtelties of the ref/const qualification of function members like in:
struct subtle
{
constexpr void operator()() noexcept {}
constexpr void operator()() & noexcept {}
constexpr void operator()() && noexcept {}
constexpr void operator()() const& noexcept {}
constexpr void operator()() const&& noexcept {}
};
If subtle is passed to overload_set it should work well.
Is it possible to create such a class overload_set in C++17, and if so how (with as much template metaprogramming tricks as required, but as little C macros as possible (hopefully none))?
Implementing what #KerrekSB said
template<typename F>
struct functor
{
using type = F;
};
template<bool Noexcept, typename R, typename... Args>
struct functor<R (*)(Args...) noexcept(Noexcept)>
{
struct fn
{
R (*p)(Args...) noexcept(Noexcept);
R operator()(Args&&... args) const noexcept(Noexcept)
{
return p(std::forward<Args>(args)...);
}
};
using type = fn;
};
template<typename F>
using func = typename functor<std::decay_t<F>>::type;
template<typename... Fs>
struct over : func<Fs>...
{
template<typename... Gs>
over(Gs&&... gs) : func<Fs>{std::forward<Gs>(gs)}... {}
using func<Fs>::operator()...;
};
template<typename... Gs>
auto makeover(Gs&&... gs)
{
return over<func<Gs>...>{std::forward<Gs>(gs)...};
}
You use it as
int main()
{
auto o = makeover([](int){ std::cout << "int\n"; },
+[](double){ std::cout << "double\n"; });
o(42); // prints int
o(42.0); // prints double
auto o2 = makeover(+[]() noexcept {});
std::cout << noexcept(o2()); // prints 1
}
Demo

SFINAE failing when evaluating a constexpr in a template parameter?

For some reason, this constexpr is not being evaluated correctly in a template parameter context:
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
template <typename T, bool IS_BUILTIN>
class is_value
{
T item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T item_to_find, std::function<bool(T, T)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T>
class is_value<T, false>
{
T const& item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, std::function<bool(T, T)> predicate = [](T lhs, T rhs) { return lhs == rhs; })
{
return detail::is_value<T, true>(item_to_find, predicate);
}
template <typename T, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate = [](T const& lhs, T const& rhs) { return lhs == rhs; })
{
return detail::is_value<T, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}
I've tested this with clang, g++ and vc++. Each had different errors:
clang
source_file.cpp:98:5: error: no matching function for call to 'fn'
fn<3>();
^~~~~
source_file.cpp:92:10: note: candidate template ignored: substitution failure [with I = 3]: non-type template argument is not a constant expression
void fn()
^
1 error generated.
g++
source_file.cpp: In function ‘int main()’:
source_file.cpp:98:11: error: no matching function for call to ‘fn()’
fn<3>();
...
vc++
source_file.cpp(91): fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'msc1.cpp', line 1421)
...
Is my code invalid or are the compilers just not up to the job yet?
Your code is invalid. The compiler (GCC7.1 for me) provides helpful error that allows us to solve this problem.
Issue:
...\main.cpp|19|note: 'detail::is_value<int, true>' is not literal because:|
...\main.cpp|19|note: 'detail::is_value<int, true>' has a non-trivial destructor|
The reason detail::is_value does not have a trivial destructor is due to the std::function<> member; std::function<> might perform dynamic memory allocation (among other reasons), thus it is not trivial. You have to replace it with a trivially destructible type; I present a simple solution below.
Note: Even if std::function<> was trivially destructible, its operator() does not seem to be declared as constexpr (see: http://en.cppreference.com/w/cpp/utility/functional/function/operator()), so it would not work either.
Sample working code (adapt as needed):
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
// notice the new template parameter F
template <typename T, typename F, bool IS_BUILTIN>
class is_value
{
T item_to_find;
F predicate;
public:
constexpr is_value(T item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T, typename F>
class is_value<T, F, false>
{
T const& item_to_find;
F predicate;
public:
constexpr is_value(T const& item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const& item, Ts const&... args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// sample predicate
template<class T>
struct default_compare
{
constexpr bool operator()(T const& lhs, T const& rhs) const
noexcept(noexcept(std::declval<T const&>() == std::declval<T const&>()))
{
return lhs == rhs;
}
};
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, typename F = default_compare<T>, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, F predicate = {})
{
return detail::is_value<T, F, true>(item_to_find, predicate);
}
template <typename T, typename F = default_compare<T>, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, F predicate = {})
{
return detail::is_value<T, F, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}

Implementing a compile-time "static-if" logic for different string types in a container

I'd like to write a function template that operates on a container of strings, for example a std::vector.
I'd like to support both CString and std::wstring with the same template function.
The problem is that CString and wstring have different interfaces, for example to get the "length" of a CString, you call the GetLength() method, instead for wstring you call size() or length().
If we had a "static if" feature in C++, I could write something like:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
static_if(strings::value_type is CString)
{
// Use the CString interface
}
static_else_if(strings::value_type is wstring)
{
// Use the wstring interface
}
}
}
Is there some template programming technique to achieve this goal with currently available C++11/14 tools?
PS
I know it's possible to write a couple of DoSomething() overloads with vector<CString> and vector<wstring>, but that's not the point of the question.
Moreover, I'd like this function template to work for any container on which you can iterate using a range-for loop.
#include <type_traits>
template <typename T, typename F>
auto static_if(std::true_type, T t, F f) { return t; }
template <typename T, typename F>
auto static_if(std::false_type, T t, F f) { return f; }
template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(std::integral_constant<bool, B>{}, t, f); }
template <bool B, typename T>
auto static_if(T t) { return static_if(std::integral_constant<bool, B>{}, t, [](auto&&...){}); }
Test:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
static_if<std::is_same<typename ContainerOfStrings::value_type, CString>{}>
([&](auto& ss)
{
// Use the CString interface
ss.GetLength();
})(s);
static_if<std::is_same<typename ContainerOfStrings::value_type, wstring>{}>
([&](auto& ss)
{
// Use the wstring interface
ss.size();
})(s);
}
}
DEMO
You could provide function overloads that do what you need:
size_t getSize(const std::string& str)
{
return str.size();
}
size_t getSize(const CString& str)
{
return str.GetLength();
}
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
...
auto size = getSize(s);
...
}
}
Here is one with a pretty syntax.
The goal is to get rid of the extra ()s in #Piotr's solution.
Lots of boilerplate:
template<bool b>
struct static_if_t {};
template<bool b>
struct static_else_if_t {};
struct static_unsolved_t {};
template<class Op>
struct static_solved_t {
Op value;
template<class...Ts>
constexpr
decltype(auto) operator()(Ts&&...ts) {
return value(std::forward<Ts>(ts)...);
}
template<class Rhs>
constexpr
static_solved_t operator->*(Rhs&&)&&{
return std::move(*this);
}
};
template<class F>
constexpr
static_solved_t<std::decay_t<F>> static_solved(F&& f) {
return {std::forward<F>(f)};
}
template<class F>
constexpr
auto operator->*(static_if_t<true>, F&& f) {
return static_solved(std::forward<F>(f));
}
template<class F>
constexpr
static_unsolved_t operator->*(static_if_t<false>, F&&) {
return {};
}
constexpr
static_if_t<true> operator->*(static_unsolved_t, static_else_if_t<true>) {
return {};
}
constexpr
static_unsolved_t operator->*(static_unsolved_t, static_else_if_t<false>) {
return {};
}
template<bool b>
constexpr static_if_t<b> static_if{};
template<bool b>
constexpr static_else_if_t<b> static_else_if{};
constexpr static_else_if_t<true> static_else{};
Here is what it looks like at point of use:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings) {
for (const auto & s : strings)
{
auto op =
static_if<std::is_same<typename ContainerOfStrings::value_type,CString>{}>->*
[&](auto&& s){
// Use the CString interface
}
->*static_else_if<std::is_same<typename ContainerOfStrings::value_type, std::cstring>{}>->*
[&](auto&& s){
// Use the wstring interface
};
op(s); // fails to compile if both of the above tests fail
}
}
with an unlimited chain of static_else_ifs supported.
It does not prevent you from doing an unlimited chain of static_else (static_else in the above is just an alias for static_else_if<true>).
One common way to solve this is to extract the required interface out into a trait class. Something like this:
template <class S>
struct StringTraits
{
static size_t size(const S &s) { return s.size(); }
// More functions here
};
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
auto len = StringTraits<typename std::decay<decltype(s)>::type>::size(s);
}
}
// Anyone can add their own specialisation of the traits, such as:
template <>
struct StringTraits<CString>
{
static size_t size(const CString &s) { return s.GetLength(); }
// More functions here
};
Of course, you can then go fancy and change the function itself to allow trait selection in addition to the type-based selection:
template <class ContainerOfStrings, class Traits = StringTraits<typename ContainerOfString::value_type>>
void DoSomething(const ContainerOfStrings& strings)
You could provide two overloads for getting the length:
template<typename T>
std::size_t getLength(T const &str)
{
return str.size();
}
std::size_t getLength(CString const &str)
{
return str.GetLength();
}

Capturing perfectly-forwarded variable in lambda

template<typename T> void doSomething(T&& mStuff)
{
auto lambda([&mStuff]{ doStuff(std::forward<T>(mStuff)); });
lambda();
}
Is it correct to capture the perfectly-forwarded mStuff variable with the &mStuff syntax?
Or is there a specific capture syntax for perfectly-forwarded variables?
EDIT: What if the perfectly-forwarded variable is a parameter pack?
Is it correct to capture the perfectly-forwarded mStuff variable with
the &mStuff syntax?
Yes, assuming that you don't use this lambda outside doSomething. Your code captures mStuff per reference and will correctly forward it inside the lambda.
For mStuff being a parameter pack it suffices to use a simple-capture with a pack-expansion:
template <typename... T> void doSomething(T&&... mStuff)
{
auto lambda = [&mStuff...]{ doStuff(std::forward<T>(mStuff)...); };
}
The lambda captures every element of mStuff per reference. The closure-object saves an lvalue reference for to each argument, regardless of its value category. Perfect forwarding still works; In fact, there isn't even a difference because named rvalue references would be lvalues anyway.
To make the lambda valid outside the scope where it's created, you need a wrapper class that handles lvalues and rvalues differently, i.e., keeps a reference to an lvalue, but makes a copy of (by moving) an rvalue.
Header file capture.h:
#pragma once
#include <type_traits>
#include <utility>
template < typename T >
class capture_wrapper
{
static_assert(not std::is_rvalue_reference<T>{},"");
std::remove_const_t<T> mutable val_;
public:
constexpr explicit capture_wrapper(T&& v)
noexcept(std::is_nothrow_move_constructible<std::remove_const_t<T>>{})
:val_(std::move(v)){}
constexpr T&& get() const noexcept { return std::move(val_); }
};
template < typename T >
class capture_wrapper<T&>
{
T& ref_;
public:
constexpr explicit capture_wrapper(T& r) noexcept : ref_(r){}
constexpr T& get() const noexcept { return ref_; }
};
template < typename T >
constexpr typename std::enable_if<
std::is_lvalue_reference<T>{},
capture_wrapper<T>
>::type
capture(std::remove_reference_t<T>& t) noexcept
{
return capture_wrapper<T>(t);
}
template < typename T >
constexpr typename std::enable_if<
std::is_rvalue_reference<T&&>{},
capture_wrapper<std::remove_reference_t<T>>
>::type
capture(std::remove_reference_t<T>&& t)
noexcept(std::is_nothrow_constructible<capture_wrapper<std::remove_reference_t<T>>,T&&>{})
{
return capture_wrapper<std::remove_reference_t<T>>(std::move(t));
}
template < typename T >
constexpr typename std::enable_if<
std::is_rvalue_reference<T&&>{},
capture_wrapper<std::remove_reference_t<T>>
>::type
capture(std::remove_reference_t<T>& t)
noexcept(std::is_nothrow_constructible<capture_wrapper<std::remove_reference_t<T>>,T&&>{})
{
return capture_wrapper<std::remove_reference_t<T>>(std::move(t));
}
Example/test code that shows it works. Note that the "bar" example shows how one can use std::tuple<...> to work around the lack of pack expansion in lambda capture initializer, useful for variadic capture.
#include <cassert>
#include <tuple>
#include "capture.h"
template < typename T >
auto foo(T&& t)
{
return [t = capture<T>(t)]()->decltype(auto)
{
auto&& x = t.get();
return std::forward<decltype(x)>(x);
// or simply, return t.get();
};
}
template < std::size_t... I, typename... T >
auto bar_impl(std::index_sequence<I...>, T&&... t)
{
static_assert(std::is_same<std::index_sequence<I...>,std::index_sequence_for<T...>>{},"");
return [t = std::make_tuple(capture<T>(t)...)]()
{
return std::forward_as_tuple(std::get<I>(t).get()...);
};
}
template < typename... T >
auto bar(T&&... t)
{
return bar_impl(std::index_sequence_for<T...>{}, std::forward<T>(t)...);
}
int main()
{
static_assert(std::is_same<decltype(foo(0)()),int&&>{}, "");
assert(foo(0)() == 0);
auto i = 0;
static_assert(std::is_same<decltype(foo(i)()),int&>{}, "");
assert(&foo(i)() == &i);
const auto j = 0;
static_assert(std::is_same<decltype(foo(j)()),const int&>{}, "");
assert(&foo(j)() == &j);
const auto&& k = 0;
static_assert(std::is_same<decltype(foo(std::move(k))()),const int&&>{}, "");
assert(foo(std::move(k))() == k);
auto t = bar(0,i,j,std::move(k))();
static_assert(std::is_same<decltype(t),std::tuple<int&&,int&,const int&,const int&&>>{}, "");
assert(std::get<0>(t) == 0);
assert(&std::get<1>(t) == &i);
assert(&std::get<2>(t) == &j);
assert(std::get<3>(t) == k and &std::get<3>(t) != &k);
}
TTBOMK, for C++14, I think the above solutions for lifetime handling can be simplified to:
template <typename T> capture { T value; }
template <typename T>
auto capture_example(T&& value) {
capture<T> cap{std::forward<T>(value)};
return [cap = std::move(cap)]() { /* use cap.value *; };
};
or more anonymous:
template <typename T>
auto capture_example(T&& value) {
struct { T value; } cap{std::forward<T>(value)};
return [cap = std::move(cap)]() { /* use cap.value *; };
};
Used it here (admittedly, this particular block of code is rather useless :P)
https://github.com/EricCousineau-TRI/repro/blob/3fda1e0/cpp/generator.cc#L161-L176
Yes you can do perfect capturing, but not directly. You will need to wrap the type in another class:
#define REQUIRES(...) class=std::enable_if_t<(__VA_ARGS__)>
template<class T>
struct wrapper
{
T value;
template<class X, REQUIRES(std::is_convertible<T, X>())>
wrapper(X&& x) : value(std::forward<X>(x))
{}
T get() const
{
return std::move(value);
}
};
template<class T>
auto make_wrapper(T&& x)
{
return wrapper<T>(std::forward<T>(x));
}
Then pass them as parameters to a lambda that returns a nested lambda that captures the parameters by value:
template<class... Ts>
auto do_something(Ts&&... xs)
{
auto lambda = [](auto... ws)
{
return [=]()
{
// Use `.get()` to unwrap the value
some_other_function(ws.get()...);
};
}(make_wrapper(std::forward<Ts>(xs)...));
lambda();
}
Here's a solution for C++17 that uses deduction guides to make it easy. I'm elaborating on Vittorio Romeo's (the OP) blog post, where he provides a solution to his own question.
std::tuple can be used to wrap the perfectly forwarded variables, making a copy or keeping a reference of each of them on a per-variable basis, as needed. The tuple itself is value-captured by the lambda.
To make it easier and cleaner, I'm going to create a new type derived from std::tuple, so to provide guided construction (that will let us avoid the std::forward and decltype() boilerplate) and pointer-like accessors in case there's just one variable to capture.
// This is the generic case
template <typename... T>
struct forwarder: public std::tuple<T...> {
using std::tuple<T...>::tuple;
};
// This is the case when just one variable is being captured.
template <typename T>
struct forwarder<T>: public std::tuple<T> {
using std::tuple<T>::tuple;
// Pointer-like accessors
auto &operator *() {
return std::get<0>(*this);
}
const auto &operator *() const {
return std::get<0>(*this);
}
auto *operator ->() {
return &std::get<0>(*this);
}
const auto *operator ->() const {
return &std::get<0>(*this);
}
};
// std::tuple_size needs to be specialized for our type,
// so that std::apply can be used.
namespace std {
template <typename... T>
struct tuple_size<forwarder<T...>>: tuple_size<tuple<T...>> {};
}
// The below two functions declarations are used by the deduction guide
// to determine whether to copy or reference the variable
template <typename T>
T forwarder_type(const T&);
template <typename T>
T& forwarder_type(T&);
// Here comes the deduction guide
template <typename... T>
forwarder(T&&... t) -> forwarder<decltype(forwarder_type(std::forward<T>(t)))...>;
And then one can use it like following.
The variadic version:
// Increment each parameter by 1 at each invocation and print it.
// Rvalues will be copied, Lvalues will be passed as references.
auto variadic_incrementer = [](auto&&... a)
{
return [a = forwarder(a...)]() mutable
{
std::apply([](auto &&... args) {
(++args._value,...);
((std::cout << "variadic_incrementer: " << args._value << "\n"),...);
}, a);
};
};
The non-variadic version:
// Increment the parameter by 1 at each invocation and print it.
// Rvalues will be copied, Lvalues will be passed as references.
auto single_incrementer = [](auto&& a)
{
return [a = forwarder(a)]() mutable
{
++a->_value;
std::cout << "single_incrementer: " << a->_value << "\n";
};
};