2015-10-12 20 views
5

Sto cercando di analizzare un elenco di numeri in un contenitore std::array di dimensioni fisse utilizzando la versione più recente di boost :: spirit x3 (inclusa in boost 1.54). Poiché std::array ha le funzioni necessarie, viene rilevato come contenitore, ma manca la funzione di inserimento che lo rende incompatibile. Ecco un breve esempio di quello che sto cercando di realizzare:Utilizzo di std :: array come attributo per boost :: spirit :: x3

#include <boost/spirit/home/x3.hpp> 

#include <array> 
#include <iostream> 
#include <string> 

namespace x3 = boost::spirit::x3; 
namespace ascii = boost::spirit::x3::ascii; 

typedef std::array<double, 3> Vertex; 

int main(int, char**) { 
    using x3::double_; 
    using ascii::blank; 

    std::string input = "3.1415 42 23.5"; 
    auto iter = input.begin(); 

    auto vertex = x3::rule<class vertex, Vertex>{} = 
    double_ >> double_ >> double_; 

    Vertex v; 

    bool const res = x3::phrase_parse(iter, input.end(), vertex, blank, v); 
    if (!res || iter != input.end()) return EXIT_FAILURE; 

    std::cout << "Match:" << std::endl; 
    for (auto vi : v) std::cout << vi << std::endl; 
    return EXIT_SUCCESS; 
} 

Questo non verrà compilato dal std::array ha alcuna funzione insert. Come una soluzione che ho usato azioni semantiche:

auto vertex() { 
    using namespace x3; 
    return rule<class vertex_id, Vertex>{} = 
    double_[([](auto &c) { _val(c)[0] = _attr(c); })] >> 
    double_[([](auto &c) { _val(c)[1] = _attr(c); })] >> 
    double_[([](auto &c) { _val(c)[2] = _attr(c); })]; 
} 

e quindi chiamare

x3::phrase_parse(iter, input.end(), vertex(), blank, v); 

invece. Funziona (usando clang 3.6.0 con -std = C++ 14), ma penso che questa soluzione sia molto poco elegante e difficile da leggere.

Così ho cercato di adattare std :: array come una sequenza di fusione utilizzando BOOST_FUSION_ADAPT_ADT in questo modo:

BOOST_FUSION_ADAPT_ADT(
    Vertex, 
    (double, double, obj[0], obj[0] = val) 
    (double, double, obj[1], obj[1] = val) 
    (double, double, obj[2], obj[2] = val)) 

e poi specializzandosi x3::traits::is_container per Vertex per dire x3 di non trattare std :: array come un contenitore:

namespace boost { namespace spirit { namespace x3 { namespace traits { 
    template<> struct is_container<Vertex> : public mpl::false_ {}; 
}}}} 

Ma questo non verrà compilato in combinazione con x3. È un bug o lo sto usando male? Chiamando ad es. fusion::front(v) senza che tutto il codice x3 compili e funzioni, quindi suppongo che il mio codice non sia completamente sbagliato.

Tuttavia sono sicuro che c'è una soluzione più pulita con x3 che non coinvolge adattatori di fusione o azioni semantiche per questo semplice problema.

+0

È 'boost :: array' un'opzione? Credo che includere "boost/fusion/include/boost_array.hpp" 'double_ >> double_ >> double_' dovrebbe funzionare. – llonesmiz

+0

Ho corretto quegli errori stupidi, mi dispiace per quello. 'boost :: array' non è un'opzione dal momento che sto interagendo con il codice esistente che fa molto affidamento su' std :: array'. – ithron

+0

[Questo è un trucco terribile] (http://coliru.stacked-crooked.com/a/412c97fecfa4e4b1) per rendere il tuo approccio compilato. Probabilmente dovresti aspettare una risposta migliore (o effettivamente una buona risposta). – llonesmiz

risposta

0

Il codice che hai postato è pieno di errori di trascuratezza. Non c'è motivo di aspettarsi qualcosa da compilare, davvero.

Indipendentemente da ciò, l'ho ripulito¹. Ovviamente dovresti aver incluso

#include <boost/fusion/adapted/array.hpp> 

Purtroppo non funziona. Ho raggiunto in gran parte la stessa conclusione di (dopo aver provato le stesse cose che hai citato, prima di leggerlo :)).

Questo è un problema di usabilità con Spirit X3, che è ancora in fase sperimentale. Puoi segnalarlo alla mailing list [spirit-general]. La risposta è solitamente piuttosto rapida.


¹ nel caso in cui si vuole vedere ciò che ho lavorato con http://paste.ubuntu.com/12764268/; Ho anche usato x3::repeat(3)[x3::double_] per il confronto.

+0

Sfortunatamente 'std :: array' non è adattato dalla fusione. L'intestazione che suggerisci (o "boost/fusion/include/boost_array.hpp") ha effetto solo su 'boost :: array'. [Questo] (http://coliru.stacked-crooked.com/a/07416373f7d19da3) sembra funzionare con alcune piccole modifiche. [Qui] (https://svn.boost.org/trac/boost/ticket/8241) è un tentativo molto vecchio di adattare 'std :: array' (non ho idea se funzioni ancora). – llonesmiz

+0

Huh. Non ha funzionato vale la pena aumentare l'array per me. Osserverò le differenze quando avrò tempo dopo. – sehe

+1

Un problema simile esiste per 'std :: forward_list'. 'push_back' richiesto per la classe di essere contenitore in termini di Boost.Spirit. – Orient