2 votes

Pourquoi noexcept est-il un problème pour l'action sémantique de boost spirit en c++17 ?

Le code suivant me donne une erreur de compilation bizarre sur VS2019 avec le mode c++17 :

#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

class Duration
{
  std::chrono::duration<int64_t, std::nano> d;

public:
  void addHours(uint64_t val) noexcept
  {
  }
};

int main()
{
  Duration d;
  namespace spirit = boost::spirit;
  namespace phoenix = boost::phoenix;
  namespace qi = boost::spirit::qi;
  qi::int_parser<int64_t, 10> int64_;
  std::string s = "2h";
  boost::spirit::qi::rule<std::string::iterator, Duration()> durationRule = (int64_ >> "h")[phoenix::bind(&Duration::addHours, qi::_val, qi::_1)];

  boost::spirit::qi::phrase_parse(s.begin(), s.end(), durationRule, boost::spirit::ascii::space, d);
}

Si je le compile en mode c++14, tout va bien, mais avec c++17 ou c++20, il échoue. J'ai trouvé que le problème vient du mot clé noexcept derrière la méthode addHours. Quelqu'un peut-il m'expliquer pourquoi c'est un problème ?

1voto

sehe Points 123151

La bibliothèque Phoenix est antérieure à noexcept.

Depuis c++17 La spécification noexcept fait partie du type de fonction et peut figurer dans n'importe quel déclarateur de fonction. .

L'implication est que vous rencontrez les limitations de Phoenix lorsque vous utilisez les spécifications noexcept en mode c++17.

Plus précisément, c'est la déduction du type de retour qui semble ne pas fonctionner : Les fonctions qui ne diffèrent que par leur spécification d'exception ne peuvent pas être surchargées (tout comme le type de retour, la spécification d'exception fait partie du type de fonction, mais pas de la signature de la fonction) (depuis C++17).

Améliorations

Dans votre cas, puisque vous utilisez C++17, pourquoi ne pas simplifier l'ensemble ? En direct sur Coliru

qi::rule<std::string::const_iterator, Duration()> durationRule //
    = qi::eps[_val = 0s]                                       //
    >> (qi::int_ >> "h")[_val += _1 * 1h];

La sortie

"2m" -> failed
"3h" -> 10800
"4s" -> failed

donne mon intention !

Bonus Extension 1 : unités

En utilisant un tableau simple comme

qi::symbols<char, Duration> unit_;
unit_.add
    ("ns", 1ns) ("nano", 1ns)
    ("us", 1us) ("s", 1us) ("micro", 1us)
    ("ms", 1ms) ("milli", 1ms)
    ("s", 1s)
    ("m", 1min) ("min", 1min)
    ("h", 1h) ("hour", 1h)
    ("d", 24h) ("day", 24h)
    ;

Maintenant, avec le changement trivial de

    >> +(qi::int_ >> unit_)[_val += _1 * _2];

Il évalue un grand nombre de séquences intéressantes : En direct sur Coliru

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <chrono>
#include <iomanip>

using namespace std::chrono_literals;
using Duration = std::chrono::duration<int64_t, std::nano>;
namespace qi   = boost::spirit::qi;

int main()
{
    using namespace qi::labels; // _val, _1 etc

    qi::symbols<char, Duration> unit_;
    unit_.add
        ("ns", 1ns) ("nano", 1ns)
        ("us", 1us) ("s", 1us) ("micro", 1us)
        ("ms", 1ms) ("milli", 1ms)
        ("s", 1s)
        ("m", 1min) ("min", 1min)
        ("h", 1h) ("hour", 1h)
        ("d", 24h) ("day", 24h)
        ;

    qi::int_parser<int64_t, 10> int64_;
    qi::rule<std::string::const_iterator, Duration(), qi::blank_type>
        durationRule         //
        = qi::eps[_val = 0s] //
        >> +(int64_ >> unit_)[_val += _1 * _2];

    for (std::string const s :
         {
             "2m",
             "3h",
             "4s",
             "1ms -1ns",
             "1 day -23h",
             "3600000000ns",
             "1 day -23h -50m +3600000000ns",
         }) //
    {
        std::cout << std::quoted(s);

        Duration d{};
        if (phrase_parse(                //
                s.begin(), s.end(),      //
                durationRule >> qi::eoi, //
                qi::blank, d))
            std::cout << " -> " << d / 1.0s << "s\n";
        else
            std::cout << " -> failed\n";
    }
}

Impressions

"2m" -> 120s
"3h" -> 10800s
"4s" -> 4s
"1ms -1ns" -> 0.000999999s
"1 day -23h" -> 3600s
"3600000000ns" -> 3.6s
"1 day -23h -50m +3600000000ns" -> 603.6s

Notez que j'ai modifié certains détails pour que cela fonctionne. Notamment qi::eoi pour vérifier que toute l'entrée est consommée, et en déclarant le skipper dans la règle ( blank au lieu de space pour exclure les nouvelles lignes). Sans cela, la règle était implicitement un lexème ( Questions relatives au skipper de l'esprit Boost )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X