Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++17 : lambda to std::function conversion failure

I'm currently exploring c++17 additions. After playing around with std::variant, wanted to use std::optional too, with the same example. Currently seeing that the compilation fails because of the following error:

error: no viable conversion from returned value of type
      '(lambda at ./html_parser.hpp:53:9)' to function return type 'Parser<char>' (aka
      'std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
      (std::__1::basic_string<char>)>')
        return [=](std::string& input) -> ParserResult<char> {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/acid/tools/include/c++/v1/functional:1627:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
    function(nullptr_t) _NOEXCEPT : __f_(0) {}
    ^
/home/acid/tools/include/c++/v1/functional:1628:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'const
      std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
      (std::__1::basic_string<char>)> &' for 1st argument
    function(const function&);
    ^
/home/acid/tools/include/c++/v1/functional:1629:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'std::__1::function<std::__1::optional<std::__1::pair<char,
      std::__1::basic_string<char> > > (std::__1::basic_string<char>)> &&' for 1st argument
    function(function&&) _NOEXCEPT;
    ^
/home/acid/tools/include/c++/v1/functional:1631:5: note: candidate template ignored: requirement
      '__callable<(lambda at ./html_parser.hpp:53:9)>::value' was not satisfied [with _Fp =
      (lambda at ./html_parser.hpp:53:9)]
    function(_Fp);
    ^
1 error generated.

To parse the HTML to give the DOM, started with declaring some parser combinators as follows:

#pragma once

#include <string>
#include <utility>
#include <functional>
#include <optional>

namespace dragon {
    namespace html {
        namespace parser {
            template <typename ParserOutput, typename ParserInput = std::string>
            using ParserResult = std::optional<std::pair<ParserOutput, ParserInput>>;

            template<typename ParserOutput, typename ParserInput = std::string>
            using Parser = std::function<ParserResult<ParserOutput, ParserInput>(ParserInput)>;

            template <typename ParserOutput, typename ParserInput = std::string>
            auto parse(Parser<ParserOutput, ParserInput> p, ParserInput i) -> ParserResult<ParserOutput, ParserInput>{
                return p(i);
            }

            // few parser combinators.

            // thenP combinator: applies the first parser, if it succeeds apply the second to the rest of 
            // the input left over by the first parser.
            // currently just fails and returns empty!! does not provide any debugging info/msg 
            // as to why the parsing failed.
            template<typename FirstParser, typename SecondParser>
            auto thenP(FirstParser f, SecondParser s) {
                return [=](std::string input) -> decltype(parse(s, std::string())) {
                    auto fv = parse(f, input);

                    if (fv) {
                        auto fvv = *fv;
                        return parse(s, fvv.second);
                    }
                    else {
                        return {};
                    }
                };
            }

            template<typename FirstParser, typename SecondParser>
            auto choiceP(FirstParser f, SecondParser s) {
                return [=](std::string input) {
                    auto fv = parse(f, input);
                    if (!fv) return parse(s, input);
                    return fv;
                };
            }

            auto charP(char match) -> Parser<char> {
                return [=](std::string& input) -> ParserResult<char> {
                    if ((input.empty() == false) && (input[0] == match)) {
                        return std::make_pair(input[0], input.substr(1));
                    }
                    return {};
                };
            }
        }
    }
}

I'm seeing the above error, when trying to compile the simple use as shown below:

int main()
{
    auto less = Parser::parser::charP('<');
    auto greater = Parser::parser::charP('>');

    auto lag = Parser::parser::thenP(less, greater);
    auto log = Parser::parser::choiceP(less, greater);

    auto lagv = lag("<>");
    auto logv = log("|>");

    return 0;
} 

This compiles fine with Visual Studio 2017 (std=c++-latest). But Clang gives the above error. Trying to figure out the discrepancy between these two compilers. and how to fix this issue with Clang.

like image 203
user3169543 Avatar asked Feb 10 '26 19:02

user3169543


1 Answers

This is ill-formed:

auto charP(char match) -> Parser<char> {
    return [=](std::string& input) -> ParserResult<char> { ... };
}

for the same reason that this is ill-formed:

std::function<void(int)> f = [](int& ){};

The lambda on the right is not invocable with an int, only an int&, so you can't construct a function<void(int)> out of it. MSVC has some permissive mode where it allows constructing a non-const lvalue reference from an rvalue, which is probably why it worked.

like image 168
Barry Avatar answered Feb 12 '26 15:02

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!