Boost Spirit Visual C++ 编译器错误,适用于 GCC

Boost Spirit Visual C++ compiler error, fine with GCC

我在堆栈溢出中获取以下 live demo code to compile under visual C++ (2015). The code works fine in GCC as demonstrated here. Could someone please help me resolve this issue. I'm quite new to boost spirit qi parsing and with all the template magic in the background its quite difficult to spot the problem. I got the initial inspiration for CSV parsing from here 时遇到问题。

visual C++下(通过live online compiler)报的编译错误如下:

Error(s):
source_file.cpp(103): error C2146: syntax error: missing ';' before identifier 'context_type'
source_file.cpp(140): note: see reference to class template instantiation 'CsvGrammar<It>::final' being compiled
source_file.cpp(147): note: see reference to class template instantiation 'CsvGrammar<It>' being compiled
source_file.cpp(103): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
source_file.cpp(146): error C2079: 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>::convert' uses undefined struct 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>::final'
source_file.cpp(158): note: see reference to class template instantiation 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>' being compiled

我的桌面在visual C++2015下的报错如下:

1>------ Build started: Project: ConsoleApplication1, Configuration: Debug x64 ------
1>  Source.cpp
1>\geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(103): error C2146: syntax error: missing ';' before identifier 'context_type'
1>  \geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(140): note: see reference to class template instantiation 'CsvGrammar<It>::final' being compiled
1>  \geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(147): note: see reference to class template instantiation 'CsvGrammar<It>' being compiled
1>\geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(103): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>\geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(146): error C2079: 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>::convert' uses undefined struct 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>::final'
1>  \geihome1\home\jcoffey\visual studio 2015\projects\consoleapplication1\consoleapplication1\source.cpp(158): note: see reference to class template instantiation 'CsvGrammar<std::_String_const_iterator<std::_String_val<std::_Simple_types<char>>>>' being compiled
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

这是有问题的代码(也复制到上面指出的现场演示站点)

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <vector>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

enum LineItems {
    SERVICENAME,
    POLYNOMIAL,
    MODULE,
    DLMUW,
    WSU,
    TCP,
    UDP,
    INAIR,
    CLIENTS,
//    RXPORTDCTRL,
//    TXPORTDCTRL,
//    RXPORTWCTRL,
//    TXPORTWCTRL,
//    RXPORTD1,
//    TXPORTD1,
//    RXPORTD2,
//    TXPORTD2,
//    RXPORTD3,
//    TXPORTD3,
//    RXPORTD4,
//    TXPORTD4,
//    RXPORTW1,
//    TXPORTW1,
//    RXPORTW2,
//    TXPORTW2
};

struct CsvLine {
    std::string ServiceName;
    std::string Polynomial;
    std::string Module;
    int DLMUW;
    int WSU;
    int TCP;
    int UDP;
    int InAir;
    int Clients;
//    std::string RxPortDCtrl;
//    std::string TxPortDCtrl;
//    std::string RxPortWCtrl;
//    std::string TxPortWCtrl;
//    int RxPortD1;
//    int TxPortD1;
//    int RxPortD2;
//    int TxPortD2;
//    int RxPortD3;
//    int TxPortD3;
//    int RxPortD4;
//    int TxPortD4;
//    int RxPortW1;
//    int TxPortW1;
//    int RxPortW2;
//    int TxPortW2;
};

using Column  = std::string;
using Columns = std::vector<Column>;
using CsvFile = std::vector<CsvLine>;

template<typename It>
struct CsvGrammar: qi::grammar<It, CsvFile(), qi::locals<std::vector<LineItems>>, qi::blank_type> {
    CsvGrammar() : CsvGrammar::base_type(start) {
        using namespace qi;
        static const char colsep = ',';

        item.add("ServiceName", SERVICENAME)("Polynomial", POLYNOMIAL)("Module", MODULE)("DLMUW", DLMUW)("WSU", WSU)("TCP", TCP)("UDP", UDP)("InAir", INAIR)("Clients", CLIENTS);
        start  = qi::omit[ header[_a=_1] ] >> eol >> line(_a) % eol;

        // Module was unused
        header = (item | omit[column] >> attr(MODULE)) % colsep;
        line   = (column % colsep) [convert];

        column = quoted | *~char_(",\n");
        quoted = '"' >> *("\"\"" | ~char_("\"\n")) >> '"';

        BOOST_SPIRIT_DEBUG_NODES((header)(column)(quoted));
    }

private:
    qi::rule<It, std::vector<LineItems>(),                      qi::blank_type> header;
    qi::rule<It, CsvFile(), qi::locals<std::vector<LineItems>>, qi::blank_type> start;
    qi::rule<It, CsvLine(std::vector<LineItems> const&),        qi::blank_type> line;

    qi::rule<It, Column(), qi::blank_type> column;
    qi::rule<It, std::string()> quoted;
    qi::rule<It, qi::blank_type> empty;

    qi::symbols<char, LineItems> item;

    struct final {
        using Ctx = typename decltype(line)::context_type;

        void operator()(Columns const& columns, Ctx &ctx, bool &pass) const {
            auto& csvLine   = boost::fusion::at_c<0>(ctx.attributes);
            auto& positions = boost::fusion::at_c<1>(ctx.attributes);
            int i =0;

            for (LineItems position : positions) {
                switch (position) {
                case SERVICENAME: 
                    csvLine.ServiceName = columns[i];              
                    break;
                case POLYNOMIAL:  
                    csvLine.Polynomial = columns[i]; 
                    break;
                case MODULE:  
                    csvLine.Module = columns[i]; 
                    break;
                case DLMUW:
                    csvLine.DLMUW = atoi(columns[i].c_str()); 
                    break;
                case WSU:
                    csvLine.WSU = atoi(columns[i].c_str()); 
                    break;
                case TCP:
                    csvLine.TCP = atoi(columns[i].c_str()); 
                    break;
                case UDP:
                    csvLine.UDP = atoi(columns[i].c_str()); 
                    break;
                case INAIR:
                    csvLine.InAir = atoi(columns[i].c_str()); 
                    break;
                case CLIENTS:
                    csvLine.Clients = atoi(columns[i].c_str()); 
                    break;
                default:   
                    break;
                }
                i++;
            }
            pass = true; // returning false fails the `line` rule
        }
    } convert;
};

int main() {
    const std::string s =
        "ServiceName,Polynomial,Module,DLMUW,WSU,TCP,UDP,InAir,Clients\n"
        "ALBF,0x82608EDB,nic1,1,0,1,1,1,6\n"
        "OmsMIS,0x04C11DB7,cmc,1,0,1,1,1,5\n"
        "FMS1,0x82F63B78,proc3,1,0,1,1,1,4\n"
        "FMS2,0x82F63B78,proc5,1,0,1,1,1,3";

    auto f(begin(s)), l(end(s));
    CsvGrammar<std::string::const_iterator> p;

    CsvFile parsed;
    bool ok = qi::phrase_parse(f, l, p, qi::blank, parsed);

    if (ok) {
        for (CsvLine line : parsed) {
            std::cout 
                << '[' << line.ServiceName << ']' 
                << '[' << line.Polynomial << ']' 
                << '[' << line.Module << ']' 
                << '[' << line.DLMUW << ']' 
                << '[' << line.WSU << ']'
                << '[' << line.TCP << ']'
                << '[' << line.UDP << ']'
                << '[' << line.InAir << ']'
                << '[' << line.Clients << ']';
            std::cout << std::endl;
        }
    } else {
        std::cout << "Parse failed\n";
    }

    if (f != l)
        std::cout << "Remaining unparsed: '" << std::string(f, l) << "'\n";
}

我很确定这是 MSVC 中的错误。由于某种原因,编译器在 decltype 上阻塞(因为它是一个语法错误,很可能他们还没有实现它)。这是一个解决方法:

using workaround = decltype(line);
using Ctx = typename workaround::context_type;

This seems like the related bug report。它关闭为 deferred... :(