Reputation: 51
EDIT Code I was talkin about here is available on my Github. https://github.com/SP8EBC/ParaGADACZ/blob/master/src/PlaylistAssembler.cpp . Lines between 103 and 110
I try to make a wrapper around value_of
method from std::optional
in c++17. An idea is to throw an exception if optional doesn't hold any object, and to keep code clean not to write explicit ifs.
I came up with such construction
std::function<std::string()> lazyFailsafe = [=]() {PlaylistAssembler::throwOnEmptyOptional();};
// add current weather anouncement
playlist->push_back(playlistSampler
.getConstantElement(PlaylistSampler_ConstanElement::CURRENT_WEATHER)
.value_or(lazyFailsafe));
The problem is that regardless if i will capture this
or =
or anything else it never compiles. GCC always returns an error message like that
../src/PlaylistAssembler.cpp: In member function ‘void PlaylistAssembler::currentWeather(std::vectorstd::pair<std::__cxx11::basic_string<char, org::openapitools::client::model::Summary> >&, std::vector&)’: ../src/PlaylistAssembler.cpp:103:105: error: conversion from ‘PlaylistAssembler::currentWeather(std::vectorstd::pair<std::__cxx11::basic_string<char, org::openapitools::client::model::Summary> >&, std::vector&)::<lambda()>’ to non-scalar type ‘std::functionstd::__cxx11::basic_string<char()>’ requested 103 | std::functionstd::string() lazyFailsafe = this {PlaylistAssembler::throwOnEmptyOptional();}; |
Worth mentioning is that PlaylistAssembler::throwOnEmptyOptional();
is of course a static class member. How should I create and use such object?
EDIT After a hint given by @UnholySheep i added missing return and now this snipped looks like that
> std::function<std::string()> lazyFailsafe = [=]()
> {PlaylistAssembler::throwOnEmptyOptional(); return std::string();};
>
> // add current weather anouncement playlist->push_back(playlistSampler
> .getConstantElement(PlaylistSampler_ConstanElement::CURRENT_WEATHER)
> .value_or(lazyFailsafe));
>
> ``` Unfortunately I still have compile errors related to this
> value_or. Printout is very, very long but it begins with
>
> In file included from ../src/PlaylistSampler.h:13,
> from ../src/PlaylistAssembler.h:17,
> from ../src/PlaylistAssembler.cpp:8: /usr/include/c++/11/optional: In instantiation of ‘constexpr _Tp
> std::optional<_Tp>::value_or(_Up&&) && [with _Up =
> std::function<std::__cxx11::basic_string<char>()>&; _Tp =
> std::__cxx11::basic_string<char>]’:
> ../src/PlaylistAssembler.cpp:108:13: required from here
> /usr/include/c++/11/optional:997:25: error: static assertion failed
> 997 | static_assert(is_convertible_v<_Up&&, _Tp>);
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/include/c++/11/optional:997:25: note:
> ‘std::is_convertible_v<std::function<std::__cxx11::basic_string<char>()>&,
> std::__cxx11::basic_string<char> >’ evaluates to false
> /usr/include/c++/11/optional:1002:20: error: no matching function for
> call to
> ‘std::__cxx11::basic_string<char>::basic_string(std::function<std::__cxx11::basic_string<char>()>&)’
> 1002 | return static_cast<_Tp>(std::forward<_Up>(__u));
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Upvotes: 0
Views: 140
Reputation: 2942
I think what you're doing somewhat goes against the idea of how to use an optional. If you need to execute custom code when an empty optional is accessed, you should probably create a helper function.
However, it is possible. You tried to pass a functor returning a string into value_or
. However, value_or
expects a value that is implicitly convertible to a string, so the functor won't work.
In case you want to keep your approach, I came up with this:
#include <optional>
#include <iostream>
#include <stdexcept>
struct Checker
{
template<typename T>
operator T() const
{
std::cout << "This gets executed when the optional is empty" << std::endl;
throw std::runtime_error{"empty optional"}; // maybe throw bad_optional_access instead
}
};
constexpr Checker checker{};
int main()
{
std::optional<int> full = 1;
std::cout << "Full optional: " << full.value_or(checker) << std::endl;
std::optional<std::string> empty{};
std::cout << "Empty optional: " << empty.value_or(checker) << std::endl;
return 0;
}
Explanation: the type Checker is implicitly convertible to any other type using the templated conversion operator. Passing it to value_or passes a reference to checker, which only an empty std::optional will try to convert to its value type.
Upvotes: 2
Reputation: 398
In your code line 108 .value_or(lazyFailsafe)
. The lazyFailsafe
object type is std::function<std::string()>
and the related optional object type is std::optional<std::string>
.
From std::optional::value_or description template< class U > constexpr T value_or( U&& default_value )
and its one of type requirement is U&& must be convertible to T
.
So there is no conversion allowed from std::function<std::string()>
to std::string
. You have to use lazyFailsafe
's call operator in value_or()
call like this: .value_or(lazyFailsafe())
.
Upvotes: 1