0

When I use a big header-only library like Boost Spirit, I often want to isolate its includes into a single cpp file (parser.cpp), in order to reduce the needed compilation time to one file only. The goal is that when a source file (main.cpp) needs the Spirit facilities, it can include a header file which presents a front class with no reference to any Spirit stuff, letting this source file compile quickly. Only one cpp file (parser.cpp) needs to include the Spirit headers and take more time to compile.

But how to create a class which needs Spirit internally, without any reference to it into its declaration? The only way I found is by using a pointer member to an incomplete wrapper class (QiParser), then use new and delete internally to create the real parser.

My question is: is there a better way to do this, without using new and delete?

Here is the kind of code I use now:

main.cpp

#include <iostream>
#include "parser.h"

int main (int, char **)
{
    Parser p;
    int n = 0;
    p.parse("78", n);
    std::cout << "The parsed number is " << n << std::endl;
    return 0;
}

parser.h

#include <string>

class QiParser; //< Incomplete class declaration

class Parser {
public:
    Parser();
    ~Parser();
    bool parse(const std::string &s, int &result) const;
private:
    QiParser *qi_parser; //< The only way I found to not include
                         //  the Spirit headers in this file
};

parser.cpp

#include "parser.h"
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

// The QiParser class is only present to solve my problem;
// I would like to put this code directly into the Parser class, but without showing
// any Spirit stuff inside the parser.h header file
struct QiParser : qi::grammar<std::string::const_iterator, int()>
{
    QiParser() : base_type(start)
    {
        start %= qi::int_;
    }
    qi::rule<std::string::const_iterator, int()> start;
};


// Implementation of the Parser class:

Parser::Parser()
{
    qi_parser = new QiParser;
}

Parser::~Parser()
{
    delete qi_parser;
}

bool Parser::parse(const std::string &s, int &result) const
{
    auto iter = s.begin(), end = s.end();
    return qi::parse(iter, end, *qi_parser, result);
}
yolenoyer
  • 7,117
  • 1
  • 20
  • 47

1 Answers1

0

Yes, use std::unique_ptr<QiParser> qi_parser; instead of QiParser * qi_parser;. This is known as the PImpl idiom

Caleth
  • 35,377
  • 2
  • 31
  • 53