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;
};
Related
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"));
The full context of this problem is unfortunately too involved to explain, but suffice to say it involves some complicated template metaprogramming and I find myself having to do some funny things. I've tried to boil down an issue I've encountered into a minimal example, so the question may seem awkward. If it's possible to do this I'd be interested to know how, if not I'd be interested in hearing possible alternatives.
I'd like to create a function that takes as input a lambda function that may return void or something else. If it does return void, I'd like to convert it into an identical lambda that, instead of returning void, returns true.
template <typename InputFuncType>
auto function_converter(InputFuncType lambda_func)
{
// I also need to figure out how to deduce the return type of lambda_func
// might be something like this.
if constexpr (std::is_same<std::result_of<InputFuncType>::type, void>::value)
{
// Current best guess. Note that in the context of the converter, I don't explicitly know
// lambda_func's input type, so I'm not sure how to do something like this.
return [](InputFuncType::input_args_t... args) {lambda_func(args); return true;};
}
return lambda_func;
}
// target usage
const auto lam1 = [](int a) {return;};
const auto lam2 = function_converter(lam1);
int x = 4;
lam1(x); // returns void
const bool y2 = lam2(x); // returns true
I'm using c++17.
A little redundant, I suppose, but the following wrapper should works
template <typename InputFuncType>
auto function_converter (InputFuncType lf)
{
return [=](auto && ... args)
{
using RT = decltype(lf(std::forward<decltype(args)>(args)...));
if constexpr ( true == std::is_same_v<void, RT> )
{
lf(std::forward<decltype(args)>(args)...);
return true;
}
else
return lf(std::forward<decltype(args)>(args)...);
};
}
Substantially, the idea is transfer the check, about the returned type, inside the internal lambda.
I was wondering if there's an elegant solution for the following issue:
Let's say I'd like to have a variable holding a value with a pretty complex type and would the compiler to automatically infer it's type, but declare first and give value later in the code because of initialization in an if statement.
For example:
{
auto a;
if (some predicate)
a = init_a(1);
else
a = init_a(2);
}
Obviously this code doesn't compile, but the I think that the compiler has all the information it needs to infer the type.
If 'a' was just an int, this was not a problem, but if the type is a complex template, I don't want to have to write it (or in some cases even know it).
Also, i wouldn't want to call the default constructor of a and then write over it.
Possible solutions:
Template the type of 'a'.
initialize 'a' using a lambda and move the predicate into the lambda.
Just write the type of 'a' instead auto.
Use a void pointer/shared_ptr and then init 'a' on the heap.
Each of these has its own drawbacks.
Is there a more elegant solution for it?
The compiler doesn't have infinite lookahead what is happening further on in the code. It only knows what's happening at the current statement. Therefore it can't deduce any types without an initializer.
If you don't know the return-type of init_a then you could use decltype:
decltype(init_a(1)) a;
You can also use a lambda call to initialize the variable:
auto a = [ /* Captures needed for the condition... */ ]()
{
if (some_condition)
return init_a(1);
else
return init_a(2);
}(); // Call the lambda immediately
Or, as mentioned in many comments, use the ternary conditional expression:
auto a = some_condition ? init_a(1) : init_a(2);
There's a technique called "Immediately Invoked Lambda Expression" that is using lambda to initialize a variable in a complex way. Using this approach your a can be const which improves const-correctness. More details here.
For a simple binary predicate, consider just using the ternary operator:
struct A { int a; };
A initA(int a) { return A{a}; }
bool somePredicate(int input) { return input == 42; }
int main() {
const auto input = 42;
const auto a = somePredicate(input) ? initA(1) : initA(2);
}
for more complex initialization logic (beyond a binary case), wrap the initialization logic in a lambda:
struct A { int a; };
A initA(int a) { return A{a}; }
bool somePredicate(int input) { return input == 42; }
int main() {
const auto input = 42;
const auto a = []() {
if (somePredicate(input)) { return initA(1); }
else if (input == 100) { return initA(100); }
else { return initA(2); }}();
}
Both these approaches come with additional possibility of making the variable to be initialized const.
If the return types of your lambda are different but convertible to some type then you can force the return type (note the -> is mandatory when specifying a return type):
auto a = [=]() -> ConvertedType {
if (some_predicate) {
return CovertibleType1(1);
} else if (other_predicate) {
return ConvertibleType2(2);
}
return ConvertibleType3(3);
}();
Though I realize this basically defeats the auto declaration...
As I am beginning to take advantage of the C++17 structured bindings and if operator init statements for more elegant function result reporting and checking, I started doing the following, if accordance with C++ Core Guideline F21:
std::pair<bool, int>Foo()
{
return {true, 42}; //true means that function complete with no error and that 42 is a good value
}
void main(void)
{
if (auto [Result, Value] = Foo(); Result)
{
//Do something with the return value here
}
}
Then, of course, I though that it would be nice to have a reusable template for such return types so that nobody has to duplicate bool portion of the pair:
template <typename T> using validated = std::pair<bool,T>;
validated<int> Foo()
{
return {true, 42};
}
void main(void)
{
if (auto [Result, Value] = Foo(); Result)
{
//Do something with the return value here
}
}
This works great for me, but now I am wondering if there is some sort of standard equivalent of this template so that I don't have to reinvent the wheel and define it myself. Seems like an arbitrary type value coupled with a validity flag would be a useful construct, but I could not find anything in standard library. Am I missing something?
std::optional is exactly what you are asking about. It's even in the description:
A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair<T,bool>, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.
The if from the example would look a bit more straightforward:
#include <optional>
#include <iostream>
std::optional<int> Foo(bool fail)
{
if (!fail) return {42};
return {};
}
void process(bool fail) {
if (auto val = Foo(fail)) {
std::cout << val.value() << '\n';
} else {
std::cout << "No value!\n";
}
}
int main() {
std::optional<int> oi;
process(true);
process(false);
}
If you really wished to use Value explicitly then you can always unpack it via reference on a successful branch i.e. auto Value = val.value();
You need to beware of some caveats. 2 from the top of my head:
Performance: Why is the construction of std::optional<int> more expensive than a std::pair<int, bool>? although for the given example up-to-date clang with -O3 looks pretty convicing
Note: static was added for process for brevity - to prevent generation of version for external linking.
It will return false if object was default constructed. That might surprise some, default construction of optional doesn't default construct underlying value.
EDIT:
After the comments I decided to explicitly state that there isn't anything like type alias for pair<T,bool> or similar compatible with standard library. It's not easy to prove something does not exist, but if there would such a type the standard library would most certainly used it in declaration of insert, it doesn't; hence, I strongly imply that there isn't any semantic wrapper around it.
You might be interested in the proposed std::expected.
Its interface follows std::optional pretty closely. The major advantage of
expected<T, E> over optional<T> is the ability to transport an error:
enum class errc {err1, err2, err3};
std::expected<int, errc> Foo()
{
if (/* error condition 1 */) return std::unexpected(errc::err1);
// ... checking other error conditions
return 42; // no error condition (42 is a good value)
// implicit conversion from `int` to `expected<int, errc>`
// avoid boilerplate code
}
int main()
{
auto q = Foo();
if (q)
{
// Do something with the return value here
}
}
You could also take a look at:
Functional exceptionless error-handling with optional and expected;
the standard proposal;
A possible implementation.
As a side note main() must return int.
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.