Boost Wave自定义输入策略

时间:2013-02-15 17:14:02

标签: c++ boost boost-spirit

我正在尝试做的是,在预处理c类文件时 boost :: wave(我实际上是在处理glsl文件),而不是加载 从磁盘中包含文件,我想从地图“加载”它们,其中map.first是文件名,map.second是内容。

我需要为输入策略的内部类提供这些的映射 文件名/文件内容。但是,我不确定我怎么能真正得到一个 映射到内部阶级......

上下文的创建如下:

typedef boost::wave::context<
                std::string::iterator, 
                lex_iterator_type,
                custom_directives_hooks::load_file_or_string_to_string,
                custom_directives_hooks
            > context_type;

自定义指令就是这样(在底部附近定义了自定义输入策略):

#if !defined(BOOST_WAVE_CUSTOM_DIRECTIVES_HOOKS_INCLUDED)
#define BOOST_WAVE_CUSTOM_DIRECTIVES_HOOKS_INCLUDED

#include <cstdio>
#include <iostream>
#include <ostream>
#include <string>
#include <algorithm>
#include <map>

#include <boost/assert.hpp>
#include <boost/config.hpp>

#include <boost/wave/token_ids.hpp>
#include <boost/wave/util/macro_helpers.hpp>
#include <boost/wave/preprocessing_hooks.hpp>
#include <boost/wave/cpp_iteration_context.hpp>

#include <iterator>
#include <fstream>
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
#include <sstream>
#endif

#include <boost/wave/wave_config.hpp>
#include <boost/wave/cpp_exceptions.hpp>
#include <boost/wave/language_support.hpp>
#include <boost/wave/util/file_position.hpp>

namespace wave = boost::wave;

class custom_directives_hooks
:   public wave::context_policies::default_preprocessing_hooks
{
public:

    custom_directives_hooks(std::map<std::string, std::string> files) : files_(files) {
    }

    //custom_directives_hooks(std::set<std::string> &files_) : files(files_), include_depth(0) {
    //}

    template <typename ContextT, typename ContainerT>
    bool
    found_unknown_directive(ContextT const& ctx, ContainerT const& line, 
        ContainerT& pending)
    {
        typedef typename ContainerT::const_iterator iterator_type;
        iterator_type it = line.begin();
        wave::token_id id = wave::util::impl::skip_whitespace(it, line.end());

        if (id != wave::T_IDENTIFIER)
            return false;       // nothing we could do

        if ((*it).get_value() == "version" || (*it).get_value() == "extension") {
            // Handle #version and #extension directives
            std::copy(line.begin(), line.end(), std::back_inserter(pending));
            return true;
        }

        if ( (*it).get_value() == "type") {
            // Handle type directive
            return true;
        }

        // Unknown directive
        return false;
    }


#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
    void opened_include_file(std::string const &relname, std::string const &filename, 
        std::size_t /*include_depth*/, bool is_system_include) 
#else
    template <typename ContextT>
    void opened_include_file(ContextT const& ctx, std::string const& relname, 
        std::string const& filename, bool is_system_include) 
#endif
    {
        std::cout << "opened_include_file: " << "relname: " << relname << " filename: " << filename << " is_system_include: " << is_system_include << std::endl;
        /*
        std::set<std::string>::iterator it = files.find(filename);

        if (it == files.end()) {
            // print indented filename
            for (std::size_t i = 0; i < include_depth; ++i)
                std::cout << " ";
            std::cout << filename << std::endl;

            files.insert(filename);
        }
        ++include_depth;
        */
    }

#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
    void returning_from_include_file() 
#else
    template <typename ContextT>
    void returning_from_include_file(ContextT const& ctx) 
#endif
    {
        //--include_depth;
    }

#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
    // old signature
    void 
    found_include_directive(std::string const& filename, bool include_next) 
    {}
#else
    // new signature
    template <typename ContextT>
    bool 
    found_include_directive(ContextT const& ctx, std::string const& filename, bool include_next) {
        std::cout << "found_include_directive:" << filename << std::endl;

        return false;    // ok to include this file
    }
#endif


    template <typename ContextT>
    bool 
    locate_include_file(ContextT& ctx, std::string &file_path, 
        bool is_system, char const *current_name, std::string &dir_path, 
        std::string &native_name) 
    {
        // Check if file is in the files map
        if (files_.find(file_path) != files_.end()) {
            std::cout << "locate_include_file: file_path:" << file_path << " dir_path:" << dir_path << " native_name:" << native_name << std::endl;
            native_name = file_path;
        } else {
            if (!ctx.find_include_file (file_path, dir_path, is_system, current_name))
                return false;   // could not locate file

            namespace fs = boost::filesystem;

            fs::path native_path(wave::util::create_path(file_path));
            if (!fs::exists(native_path)) {
                //BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_include_file, 
                //    file_path.c_str(), ctx.get_main_pos());
                std::cout << "error: doesn't exist" << std::endl;
                return false;
            }

            // return the unique full file system path of the located file
            native_name = wave::util::native_file_string(native_path);
        }

        typedef typename ContextT::iterator_type iterator_type;
        std::streambuf* buffer = nullptr;
        wave::language_support language();

        ctx.instring.assign(
            std::istreambuf_iterator<char>(buffer),
            std::istreambuf_iterator<char>());

        ctx.first = iterator_type(
            ctx.instring.begin(), ctx.instring.end(),
            PositionT(ctx.filename), language);
        ctx.last = iterator_type();

        return true;      // include file has been located successfully
    }

    struct load_file_or_string_to_string
    {       
        template <typename IterContextT>
        class inner
        {
        public:     
            template <typename PositionT>
            static void init_iterators(IterContextT &iter_ctx,
                PositionT const &act_pos, wave::language_support language)
            {
                static std::map<std::string, std::string> theFiles;

                typedef typename IterContextT::iterator_type iterator_type;

                std::cout << "init_iterators: " << iter_ctx.filename << std::endl;

                std::streambuf* buffer = nullptr;

                const char* cString = iter_ctx.filename.c_str();

                if (theFiles.find(std::string(cString)) != theFiles.end()) {
                    std::stringstream ss( theFiles[std::string(cString)] );
                    buffer = ss.rdbuf();
                } else {
                    // read in the file
                    std::ifstream instream(iter_ctx.filename.c_str());
                    if (!instream.is_open()) {
                        //BOOST_WAVE_THROW_CTX(iter_ctx.ctx, wave::preprocess_exception,
                        //  wave::bad_include_file, iter_ctx.filename.c_str(), act_pos);
                        std::cout << "error: not open" << std::endl;
                        return;
                    }
                    instream.unsetf(std::ios::skipws);
                    buffer = instream.rdbuf();
                }

                iter_ctx.instring.assign(
                    std::istreambuf_iterator<char>(buffer),
                    std::istreambuf_iterator<char>());

                iter_ctx.first = iterator_type(
                    iter_ctx.instring.begin(), iter_ctx.instring.end(),
                    PositionT(iter_ctx.filename), language);
                iter_ctx.last = iterator_type();
            }

        private:
            std::string instring;
        };
    };

    std::map<std::string, std::string> files_;
};

#endif // !defined(BOOST_WAVE_ADVANCED_PREPROCESSING_HOOKS_INCLUDED)

有没有人有任何建议?

1 个答案:

答案 0 :(得分:0)

我能够通过为内部类之外的文件创建静态地图,填充它,然后从init_iterators方法中引用该静态地图来解决问题。

首先,我将inner类声明/实现代码移到了custom_directives_hooks类中。

然后,我在自定义指令头文件中定义了一个files_变量,如下所示:

...
class custom_directives_hooks
:   public wave::context_policies::default_preprocessing_hooks
{
public:
    static std::map<std::string, std::string> files_;

    custom_directives_hooks(std::map<std::string, std::string> files) {
        custom_directives_hooks::files_ = files;
    }
...

在我的自定义指令cpp文件中,我像这样初始化了地图:

std::map<std::string, std::string> custom_directives_hooks::files_ = std::map<std::string, std::string>();

在主要方法之前。

在主要方法中,我补充说:

std::map<std::string, std::string> files;

files["oglre"] = "oglre SOURCE\n";
files["material"] = "material SOURCE\n";
files["light"] = "light SOURCE\n";

然后将files映射传递给自定义指令对象。

然后,我可以在files_类中引用inner变量,如下所示:

...
if (custom_directives_hooks::files_.find(std::string(cString)) != custom_directives_hooks::files_.end()) {
    std::stringstream ss( custom_directives_hooks::files_[std::string(cString)] );
    iter_ctx.instring.assign(
        std::istreambuf_iterator<char>( ss.rdbuf() ),
        std::istreambuf_iterator<char>()
    );
}
...

希望这对有这样一个问题的其他人有所帮助(虽然这是一个非常具体的问题:))。