Mateusz L
Mateusz L

Reputation: 51

creating std::functional object to wrap a lambda with a call to static member function

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

Answers (2)

Wutz
Wutz

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.

godbolt

Upvotes: 2

M. Galib Uludag
M. Galib Uludag

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

Related Questions