I have a working piece of C++17 code that I would like to port to C++14 (project's constraints). The code allocates a functor on the heap, based on the lambda returned by a provider.
decltype(auto) fun_provider(std::string msg)
{
return [msg](){ std::cout << msg << std::endl; };
}
int main()
{
auto fun = std::make_unique<
std::invoke_result_t<decltype(fun_provider), std::string>
>(fun_provider("Provided functor"));
(*fun.get())()
}
// output: "Provided functor"
The point is, it avoids hardcoding lambda type as std::function<void()>. Up to my best knowledge, the closure type is unspecified and such construction would imply unnecessary copy of the closure object (hope that's correct).
I would like to achieve the same goal with C++14, is it possible? I tried few constructions with std::result_of and/or decltype but didn't succeed so far.
How about routing fun_provider's return value through another pass of template deduction?
template <class T>
auto to_unique(T&& orig) {
using BaseType = std::remove_cv_t<std::remove_reference_t<T>>;
return std::make_unique<BaseType>(std::forward<T>(orig));
}
int main()
{
auto fun = to_unique(fun_provider("Provided functor"));
(*fun.get())();
}
Is this approach not viable?
auto fun = std::make_unique<
decltype(fun_provider(std::declval<std::string>()))
>(fun_provider("Provided functor"));
The use of std::declval isn't even necessary; std::string{} instead of std::declval<std::string>() is just fine.
Just for the sake of completeness and to answer the original question.
The correct solution using std::result_of_t would look like this:
auto fun = std::make_unique<
std::result_of_t<decltype(&fun_provider)(std::string)>
>(fun_provider("Provided functor"));
Related
In this code, how can I use decltype in std::future to deduce the return type of bar() ? Although directly using std::future<int> works, I would like to know how can decltype be used in such a situation.
#include <iostream>
#include <future>
int bar(int a)
{
return 50;
}
int main()
{
std::packaged_task<decltype(bar)> task(bar);
//std::future<decltype(bar(int))> f = task.get_future(); //doesn't work need to do something like this
std::future<int> f = task.get_future(); //works
std::thread t1(std::move(task), 10);
t1.detach();
int val = f.get();
std::cout << val << "\n";
return 0;
}
Also, is the use of decltype in std::packaged_task correct ?
Note that you can use auto:
auto f = task.get_future();
And everything works as expected.
decltype is used to detect the type of an expression. In this case, bar(int) is not a valid expression. You may use decltype(bar(0)).
Alternatively, you can use the dedicated tools for determining the result of a function invocation. Since you tagged c++11, you can use typename std::result_of<decltype(bar)*(int)>::type (of course, you need to #include <type_traits>).
For the benefit of future readers: I would like to address this question in the perspective of c++17. result_of is expects template argument of the form F(Args...), which suffers since the function type is the return type and is extremely limited. In c++17, invoke_result is introduced, and is way better than result_of: std::invoke_result_t<decltype(bar), int>. Very intuitive.
Background
I have a series of lambdas that perform different checks on the captured variables and return std::nullopt if the check failed. return std::nullopt is the first return statement. Then, if the check succeeded, they go on and compute the value.
Problem
The types of return expressions are not consistent, e.g. std::nullopt_t cannot be converted to std::optional<T>, even though the other way around works. In particular, I'd like the following code to compile and run, printing 2:
#include <functional>
#include <utility>
#include <optional>
int x = 3;
auto lambda = [](){
if (x == 2)
return std::nullopt;
return std::optional(2);
};
#include <iostream>
int main () {
using return_type = std::invoke_result_t<decltype(lambda)>;
static_assert(std::is_same<return_type, std::optional<int>>{},
"return type is still std::nullopt_t");
std::cout << lambda().value() << '\n';
}
Wandbox Demo.
Thoughts
I believe that I need to use std::common_type<Args...> somewhere, but I can neither enforce presence of it or deduce Args, as it might require language support.
Instead of using template type deduction to infer the return type of the lambda, why not explicitly specify that return type?
auto lambda = []() -> std::optional<int> {
if (x == 2)
return std::nullopt;
return 2;
};
std::common_type is commonly with templates, which you don't have.
I suggest to stick with a single return statement and explicitly specified result type without using nullopt at all. It looks somewhat misleading when a function returns either an integer or a nullopt. Especially if the function was longer. Also if value type was something with an explicit constructor then use of emplace allows to avoid typing value type name again.
auto lambda = []()
{
std::optional<int> result{};
if(2 != x)
{
result.emplace(2);
}
return result;
};
I came across this question, where the answer describes nice detail about how can the generic lambda functions be used to replace the std::function technique and how to rewire the stop condition to enable the return type deduction.
Based on the above I created the following working example:
#include <cstdio>
void printSeq(unsigned start) {
auto recursion = [](auto&& self, const char* format, unsigned current) {
printf(format, current);
if(!current)
return static_cast<void>(printf("\n"));
else
self(self, ", %u", current - 1);
};
recursion(recursion, "%u", start);
}
int main() {
return (printSeq(15), 0);
}
My question is that what's the advantage using auto&& over the auto& in this case? Should I use the std::move here?
auto& is lvalue only.
This matters little until you refactor and replace the lvalue recursive object with a temporary proxy memoizer, for example.
auto&& is harmless, and means "I do not mind if this is a temprary or whatever, just don't make a copy", which expresses meaning well here. auto& states "No temporaries allowed!" Sometimes you want to exclude temporaries when making a reference, but it is rare.
auto const&, auto and auto&& should be your bread and butter.
Only use auto& if your operation is explicitly about writing and you are ok with excluding proxy references.
template<class... Ts, class T>
constexpr auto contains(T&&){
auto types = hana::to<hana::tuple_tag>(hana::tuple_t<Ts...>);
return hana::bool_c<hana::find(types, hana::type_c<T>) != hana::nothing>;
}
auto ht = hana::make_tuple(1,2,3,'c');
auto ht1 = hana::filter(ht, [](auto t){
return contains<int,float,double>(t);
});
//prints 0
std::cout << hana::size(ht1) << std::endl;
I am not sure if I am using boost hana correctly but contains seems to work.
std::cout << contains<int,float,double>(5) << std::endl; // 1
std::cout << contains<int,float,double>('c') << std::endl; // 0
std::cout << contains<int,float,double>(5.0f) << std::endl; // 1
Why is the size of ht1 0?
The problem here actually has nothing to do with Hana, it has to do with the way universal references are deduced. Just to clear up things, hana::type_c<T> == hana::type_c<U> is precisely equivalent to std::is_same<T, U>{}. There is no reference or cv-qualifier removing when comparing hana::types. You can look at various articles (like this or this) for these rules.
Now, let me go through your code and modify some things, with comments. First,
auto types = hana::to<hana::tuple_tag>(hana::tuple_t<Ts...>);
is redundant, because you're already creating a hana::tuple with hana::tuple_t. Hence, hana::tuple_t<T...> only is sufficient. Secondly, there's this line:
return hana::bool_c<hana::find(types, hana::type_c<T>) != hana::nothing>;
Instead of checking hana::find(...) != hana::nothing, I would instead use hana::contains, which expresses your intent better and might be more optimized too. In general, and especially with a metaprogramming library with Hana, don't try to reason as to what will be faster. Just state your intent as clearly as possible and hope for me to do my job properly on the implementation side :-). Hence, you'll end up with
return hana::bool_c<hana::contains(types, hana::type_c<T>)>;
Now, that hana::bool_c<...> really is redundant, because hana::contains already returns a boolean integral_constant. Hence, the above is equivalent to the simpler
return hana::contains(types, hana::type_c<T>);
Finally, putting all the bits together and simplifying, you get
template<class... Ts, class T>
constexpr auto contains(T&&){
return hana::contains(hana::tuple_t<Ts...>, hana::type_c<T>);
}
I'm personally not a fan of taking the T&& as an argument, when all you want is actually the type of that object. Indeed, that forces you to actually provide an object to contains function, which might be unwieldy in some circumstances (what if you don't have an object around?). Furthermore, it can be confusing to be comparing values with types:
contains<int, char, double>(3.5) // wtf, 3.5 is not in [int, char, double]!
Instead, I would write the following if it was my own code:
template<class... Ts, class T>
constexpr auto contains(T type){
return hana::contains(hana::tuple_t<Ts...>, type);
}
// and then use it like
contains<int, char, double>(hana::type_c<double>)
But that is part of the interface of your function, and I guess you are a better judge than I to know what your needs are in terms of interface.
The problem is the T&&, I think it deduces the type to be of type T& which means that hana::type_c<T> != hana::type_c<T&> The fix is to leave the && because they are unnecessary.
template<class... Ts, class T>
constexpr auto contains(T){
auto types = hana::tuple_t<Ts...>;
return hana::find(types, hana::type_c<T>) != hana::nothing;
}
Just to add to your answer, your ht1 was calling contains with t an lvalue. The following demonstrates T&& in the case of passing an rvalue and an lvalue:
#include<boost/hana.hpp>
namespace hana = boost::hana;
template<class T>
void test1(T&&) {
static_assert(hana::type_c<T> == hana::type_c<int>, "");
}
int main() {
static_assert(hana::type_c<int> != hana::type_c<int&&>, "");
test1(5);
int x = 5;
test1(x); //fails
}
clang output:
main.cpp:7:3: error: static_assert failed ""
static_assert(hana::type_c<T> == hana::type_c<int>, "");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:3: note: in instantiation of function template specialization 'test1<int &>' requested here
test1(x); //fails
^
1 error generated.
What are the benefits of using boost::any_range?
Here is an example:
typedef boost::any_range<
int
, boost::forward_traversal_tag
, int
, std::ptrdiff_t
> integer_range;
void display_integers(const integer_range& rng)
{
boost::copy(rng,
std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
int main(){
std::vector<int> input{ ... };
std::list<int> input2{ ... };
display_integers(input);
display_integers(input2);
}
But the same functionality with more efficiency can be achieved with a template parameter, which satisfies the ForwardRange concept:
template <class ForwardRange>
void display_integers(const ForwardRange& rng)
{
boost::copy(rng,
std::ostream_iterator<int>(std::cout, ","));
std::cout << std::endl;
}
So I am searching for scenarios when it is worth to use any_range. Maybe I am missing something.
This technique is called Type Erasure. There is a full article describing the pros and cons on the example of any_iterator: On the Tension Between Object-Oriented and Generic Programming in C++.
It is possible to hide (in a separate file/library) the implementation/definition of
void display_integers(const integer_range& rng)
But in the case of
template <class ForwardRange>
void display_integers(const ForwardRange& rng)
you have to provide source code to users (or at least make explicit instantiations somewhere).
Moreover, in the first case, display_integers will be compiled only once, but in the second it will be compiled for every type of the passed range.
Also, you may have somewhere
integer_range rng;
and during lifetime of rng you may assign ranges of different types to it:
vector<int> v;
list<int> l;
integer_range rng;
rng = v;
rng = l;
The biggest disadvantage of type erasure is its runtime cost; all operations are virtual, and cannot be inlined (easily).
P.S. another famous example of type erasure is std::function
boost::any_range can used for returning ranges from functions. Imagine the following example:
auto make_range(std::vector<int> v) -> decltype(???)
{
return v | filter([](int x){ return x % 2 == 0;})
| transform([](int x){ return x * 2;});
}
*: gcc does not compile the above without wrapping it in std::function, hower clang 3.2 works by directly passing the lambda
It is very difficult to know what is being returned from this function. Also, lambda and decltype don't work together so we cannot deduce the type using decltype when passing only a lambda. One solution is to use boost::any_range like the one in your example (another workaround is to use std::function as pointed out by Evgeny Panasyuk in the comments):
integer_range make_range(std::vector<int> v)
{
return v | filter([](int x){ return x % 2 == 0;})
| transform([](int x){ return x * 2;});
}
Working example with gcc using std::function.
Working example with clang passing lambdas directly.