Chris@16: /*============================================================================= Chris@16: Boost.Wave: A Standard compliant C++ preprocessor library Chris@16: Definition of the preprocessor context Chris@16: Chris@16: http://www.boost.org/ Chris@16: Chris@16: Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost Chris@16: Software License, Version 1.0. (See accompanying file Chris@16: LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: =============================================================================*/ Chris@16: Chris@16: #if !defined(CPP_CONTEXT_HPP_907485E2_6649_4A87_911B_7F7225F3E5B8_INCLUDED) Chris@16: #define CPP_CONTEXT_HPP_907485E2_6649_4A87_911B_7F7225F3E5B8_INCLUDED Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #if BOOST_WAVE_SERIALIZATION != 0 Chris@16: #include Chris@16: #include Chris@16: #endif Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: // this must occur after all of the includes and before any code appears Chris@16: #ifdef BOOST_HAS_ABI_HEADERS Chris@16: #include BOOST_ABI_PREFIX Chris@16: #endif Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: namespace boost { Chris@16: namespace wave { Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: // Chris@16: // The C/C++ preprocessor context template class Chris@16: // Chris@16: // The boost::wave::context template is the main interface class to Chris@16: // control the behavior of the preprocessing engine. Chris@16: // Chris@16: // The following template parameters has to be supplied: Chris@16: // Chris@16: // IteratorT The iterator type of the underlying input stream Chris@16: // LexIteratorT The lexer iterator type to use as the token factory Chris@16: // InputPolicyT The input policy type to use for loading the files Chris@16: // to be included. This template parameter is optional and Chris@16: // defaults to the Chris@16: // iteration_context_policies::load_file_to_string Chris@16: // type. Chris@16: // HooksT The hooks policy to use for different notification Chris@16: // callbacks. This template parameter is optional and Chris@16: // defaults to the Chris@16: // context_policies::default_preprocessing_hooks Chris@16: // type. Chris@16: // DerivedT The type of the type being derived from the context Chris@16: // type (if any). This template parameter is optional and Chris@16: // defaults to 'this_type', which means that the context Chris@16: // type will be used assuming no derived type exists. Chris@16: // Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: struct this_type {}; Chris@16: Chris@16: template < Chris@16: typename IteratorT, Chris@16: typename LexIteratorT, Chris@16: typename InputPolicyT = iteration_context_policies::load_file_to_string, Chris@16: typename HooksT = context_policies::eat_whitespace, Chris@16: typename DerivedT = this_type Chris@16: > Chris@16: class context : private boost::noncopyable Chris@16: { Chris@16: private: Chris@16: typedef typename mpl::if_< Chris@16: is_same, context, DerivedT Chris@16: >::type actual_context_type; Chris@16: Chris@16: public: Chris@16: Chris@16: // concept checks Chris@16: // the given iterator should be at least a forward iterator type Chris@16: BOOST_CLASS_REQUIRE(IteratorT, boost, ForwardIteratorConcept); Chris@16: Chris@16: // public typedefs Chris@16: typedef typename LexIteratorT::token_type token_type; Chris@16: typedef typename token_type::string_type string_type; Chris@16: Chris@16: typedef IteratorT target_iterator_type; Chris@16: typedef LexIteratorT lexer_type; Chris@16: typedef pp_iterator iterator_type; Chris@16: Chris@16: typedef InputPolicyT input_policy_type; Chris@16: typedef typename token_type::position_type position_type; Chris@16: Chris@16: // type of a token sequence Chris@16: typedef std::list > Chris@16: token_sequence_type; Chris@16: // type of the policies Chris@16: typedef HooksT hook_policy_type; Chris@16: Chris@16: private: Chris@16: // stack of shared_ptr's to the pending iteration contexts Chris@16: typedef boost::shared_ptr > Chris@16: iteration_ptr_type; Chris@16: typedef boost::wave::util::iteration_context_stack Chris@16: iteration_context_stack_type; Chris@16: typedef typename iteration_context_stack_type::size_type iter_size_type; Chris@16: Chris@16: context *this_() { return this; } // avoid warning in constructor Chris@16: Chris@16: public: Chris@16: context(target_iterator_type const &first_, target_iterator_type const &last_, Chris@16: char const *fname = "", HooksT const &hooks_ = HooksT()) Chris@16: : first(first_), last(last_), filename(fname) Chris@16: , has_been_initialized(false) Chris@16: #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 Chris@16: , current_filename(fname) Chris@16: #endif Chris@16: , current_relative_filename(fname) Chris@16: , macros(*this_()) Chris@16: , language(language_support( Chris@16: support_cpp Chris@16: | support_option_convert_trigraphs Chris@16: | support_option_emit_line_directives Chris@16: #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 Chris@16: | support_option_include_guard_detection Chris@16: #endif Chris@16: #if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0 Chris@16: | support_option_emit_pragma_directives Chris@16: #endif Chris@16: | support_option_insert_whitespace Chris@16: )) Chris@16: , hooks(hooks_) Chris@16: { Chris@16: macros.init_predefined_macros(fname); Chris@16: } Chris@16: Chris@16: // default copy constructor Chris@16: // default assignment operator Chris@16: // default destructor Chris@16: Chris@16: // iterator interface Chris@16: iterator_type begin() Chris@16: { Chris@16: std::string fname(filename); Chris@16: if (filename != "" && filename != "") { Chris@16: using namespace boost::filesystem; Chris@16: path fpath(util::complete_path(path(filename))); Chris@16: fname = fpath.string(); Chris@16: } Chris@16: return iterator_type(*this, first, last, position_type(fname.c_str())); Chris@16: } Chris@16: iterator_type begin( Chris@16: target_iterator_type const &first_, Chris@16: target_iterator_type const &last_) Chris@16: { Chris@16: std::string fname(filename); Chris@16: if (filename != "" && filename != "") { Chris@16: using namespace boost::filesystem; Chris@16: path fpath(util::complete_path(path(filename))); Chris@16: fname = fpath.string(); Chris@16: } Chris@16: return iterator_type(*this, first_, last_, position_type(fname.c_str())); Chris@16: } Chris@16: iterator_type end() const Chris@16: { return iterator_type(); } Chris@16: Chris@16: // maintain include paths Chris@16: bool add_include_path(char const *path_) Chris@16: { return includes.add_include_path(path_, false);} Chris@16: bool add_sysinclude_path(char const *path_) Chris@16: { return includes.add_include_path(path_, true);} Chris@16: void set_sysinclude_delimiter() { includes.set_sys_include_delimiter(); } Chris@16: typename iteration_context_stack_type::size_type get_iteration_depth() const Chris@16: { return iter_ctxs.size(); } Chris@16: Chris@16: // maintain defined macros Chris@16: #if BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0 Chris@16: template Chris@16: bool add_macro_definition(StringT macrostring, bool is_predefined = false) Chris@16: { Chris@16: return boost::wave::util::add_macro_definition(*this, Chris@16: util::to_string(macrostring), is_predefined, Chris@16: get_language()); Chris@16: } Chris@16: #endif Chris@16: // Define and undefine macros, macro introspection Chris@16: template Chris@16: bool add_macro_definition(StringT const &name, position_type const& pos, Chris@16: bool has_params, std::vector ¶meters, Chris@16: token_sequence_type &definition, bool is_predefined = false) Chris@16: { Chris@16: return macros.add_macro( Chris@16: token_type(T_IDENTIFIER, util::to_string(name), pos), Chris@16: has_params, parameters, definition, is_predefined); Chris@16: } Chris@16: template Chris@16: bool is_defined_macro(StringT const &str) const Chris@16: { Chris@16: return macros.is_defined(util::to_string(str)); Chris@16: } Chris@16: template Chris@16: bool get_macro_definition(StringT const &name, Chris@16: bool &has_params, bool &is_predefined, position_type &pos, Chris@16: std::vector ¶meters, Chris@16: token_sequence_type &definition) const Chris@16: { Chris@16: return macros.get_macro(util::to_string(name), Chris@16: has_params, is_predefined, pos, parameters, definition); Chris@16: } Chris@16: template Chris@16: bool remove_macro_definition(StringT const& undefname, bool even_predefined = false) Chris@16: { Chris@16: // strip leading and trailing whitespace Chris@16: string_type name = util::to_string(undefname); Chris@16: typename string_type::size_type pos = name.find_first_not_of(" \t"); Chris@16: if (pos != string_type::npos) { Chris@16: typename string_type::size_type endpos = name.find_last_not_of(" \t"); Chris@16: name = name.substr(pos, endpos-pos+1); Chris@16: } Chris@16: Chris@16: #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 Chris@16: // ensure this gets removed from the list of include guards as well Chris@16: includes.remove_pragma_once_header( Chris@16: util::to_string(name)); Chris@16: #endif Chris@16: return macros.remove_macro(name, macros.get_main_pos(), even_predefined); Chris@16: } Chris@16: void reset_macro_definitions() Chris@16: { macros.reset_macromap(); macros.init_predefined_macros(); } Chris@16: Chris@16: // Iterate over names of defined macros Chris@16: typedef boost::wave::util::macromap macromap_type; Chris@16: typedef typename macromap_type::name_iterator name_iterator; Chris@16: typedef typename macromap_type::const_name_iterator const_name_iterator; Chris@16: Chris@16: name_iterator macro_names_begin() { return macros.begin(); } Chris@16: name_iterator macro_names_end() { return macros.end(); } Chris@16: const_name_iterator macro_names_begin() const { return macros.begin(); } Chris@16: const_name_iterator macro_names_end() const { return macros.end(); } Chris@16: Chris@16: // This version now is used internally mainly, but since it was a documented Chris@16: // API function we leave it in the public interface. Chris@16: bool add_macro_definition(token_type const &name, bool has_params, Chris@16: std::vector ¶meters, token_sequence_type &definition, Chris@16: bool is_predefined = false) Chris@16: { Chris@16: return macros.add_macro(name, has_params, parameters, definition, Chris@16: is_predefined); Chris@16: } Chris@16: Chris@16: // get the Wave version information Chris@16: static std::string get_version() Chris@16: { Chris@16: boost::wave::util::predefined_macros p; Chris@16: return util::to_string(p.get_fullversion()); Chris@16: } Chris@16: static std::string get_version_string() Chris@16: { Chris@16: boost::wave::util::predefined_macros p; Chris@16: return util::to_string(p.get_versionstr()); Chris@16: } Chris@16: Chris@16: // access current language options Chris@16: void set_language(boost::wave::language_support language_, Chris@16: bool reset_macros = true) Chris@16: { Chris@16: language = language_; Chris@16: if (reset_macros) Chris@16: reset_macro_definitions(); Chris@16: } Chris@16: boost::wave::language_support get_language() const { return language; } Chris@16: Chris@16: position_type &get_main_pos() { return macros.get_main_pos(); } Chris@16: position_type const& get_main_pos() const { return macros.get_main_pos(); } Chris@16: Chris@16: // change and ask for maximal possible include nesting depth Chris@16: void set_max_include_nesting_depth(iter_size_type new_depth) Chris@16: { iter_ctxs.set_max_include_nesting_depth(new_depth); } Chris@16: iter_size_type get_max_include_nesting_depth() const Chris@16: { return iter_ctxs.get_max_include_nesting_depth(); } Chris@16: Chris@16: // access the policies Chris@16: hook_policy_type &get_hooks() { return hooks; } Chris@16: hook_policy_type const &get_hooks() const { return hooks; } Chris@16: Chris@16: // return type of actually used context type (might be the derived type) Chris@16: actual_context_type& derived() Chris@16: { return *static_cast(this); } Chris@16: actual_context_type const& derived() const Chris@16: { return *static_cast(this); } Chris@16: Chris@16: // return the directory of the currently preprocessed file Chris@16: boost::filesystem::path get_current_directory() const Chris@16: { return includes.get_current_directory(); } Chris@16: Chris@16: #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) Chris@16: protected: Chris@16: friend class boost::wave::pp_iterator; Chris@16: friend class boost::wave::impl::pp_iterator_functor; Chris@16: #endif Chris@16: Chris@16: // make sure the context has been initialized Chris@16: void init_context() Chris@16: { Chris@16: if (!has_been_initialized) { Chris@16: std::string fname(filename); Chris@16: if (filename != "" && filename != "") { Chris@16: using namespace boost::filesystem; Chris@16: path fpath(util::complete_path(path(filename))); Chris@16: fname = fpath.string(); Chris@16: includes.set_current_directory(fname.c_str()); Chris@16: } Chris@16: has_been_initialized = true; // execute once Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: bool is_defined_macro(IteratorT2 const &begin, IteratorT2 const &end) const Chris@16: { return macros.is_defined(begin, end); } Chris@16: Chris@16: // maintain include paths (helper functions) Chris@16: void set_current_directory(char const *path_) Chris@16: { includes.set_current_directory(path_); } Chris@16: Chris@16: // conditional compilation contexts Chris@16: bool get_if_block_status() const { return ifblocks.get_status(); } Chris@16: bool get_if_block_some_part_status() const Chris@16: { return ifblocks.get_some_part_status(); } Chris@16: bool get_enclosing_if_block_status() const Chris@16: { return ifblocks.get_enclosing_status(); } Chris@16: void enter_if_block(bool new_status) Chris@16: { ifblocks.enter_if_block(new_status); } Chris@16: bool enter_elif_block(bool new_status) Chris@16: { return ifblocks.enter_elif_block(new_status); } Chris@16: bool enter_else_block() { return ifblocks.enter_else_block(); } Chris@16: bool exit_if_block() { return ifblocks.exit_if_block(); } Chris@16: typename boost::wave::util::if_block_stack::size_type get_if_block_depth() const Chris@16: { return ifblocks.get_if_block_depth(); } Chris@16: Chris@16: // stack of iteration contexts Chris@16: iteration_ptr_type pop_iteration_context() Chris@16: { iteration_ptr_type top = iter_ctxs.top(); iter_ctxs.pop(); return top; } Chris@16: void push_iteration_context(position_type const &act_pos, iteration_ptr_type iter_ctx) Chris@16: { iter_ctxs.push(*this, act_pos, iter_ctx); } Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: // Chris@16: // expand_tokensequence(): Chris@16: // expands all macros contained in a given token sequence, handles '##' Chris@16: // and '#' pp operators and re-scans the resulting sequence Chris@16: // (essentially pre-processes the token sequence). Chris@16: // Chris@16: // The expand_undefined parameter is true during macro expansion inside Chris@16: // a C++ expression given for a #if or #elif statement. Chris@16: // Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: template Chris@16: token_type expand_tokensequence(IteratorT2 &first_, IteratorT2 const &last_, Chris@16: token_sequence_type &pending, token_sequence_type &expanded, Chris@16: bool& seen_newline, bool expand_undefined = false) Chris@16: { Chris@16: return macros.expand_tokensequence(first_, last_, pending, expanded, Chris@16: seen_newline, expand_undefined); Chris@16: } Chris@16: Chris@16: template Chris@16: void expand_whole_tokensequence(IteratorT2 &first_, IteratorT2 const &last_, Chris@16: token_sequence_type &expanded, bool expand_undefined = true) Chris@16: { Chris@16: macros.expand_whole_tokensequence(expanded, first_, last_, Chris@16: expand_undefined); Chris@16: Chris@16: // remove any contained placeholder Chris@16: boost::wave::util::impl::remove_placeholders(expanded); Chris@16: } Chris@16: Chris@16: public: Chris@16: #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 Chris@16: // support for #pragma once Chris@16: // maintain the real name of the current preprocessed file Chris@16: void set_current_filename(char const *real_name) Chris@16: { current_filename = real_name; } Chris@16: std::string const &get_current_filename() const Chris@16: { return current_filename; } Chris@16: Chris@16: // maintain the list of known headers containing #pragma once Chris@16: bool has_pragma_once(std::string const &filename_) Chris@16: { return includes.has_pragma_once(filename_); } Chris@16: bool add_pragma_once_header(std::string const &filename_, Chris@16: std::string const& guard_name) Chris@16: { Chris@16: get_hooks().detected_include_guard(derived(), filename_, guard_name); Chris@16: return includes.add_pragma_once_header(filename_, guard_name); Chris@16: } Chris@16: bool add_pragma_once_header(token_type const &pragma_, Chris@16: std::string const &filename_) Chris@16: { Chris@16: get_hooks().detected_pragma_once(derived(), pragma_, filename_); Chris@16: return includes.add_pragma_once_header(filename_, Chris@16: "__BOOST_WAVE_PRAGMA_ONCE__"); Chris@16: } Chris@16: #endif Chris@16: Chris@16: void set_current_relative_filename(char const *real_name) Chris@16: { current_relative_filename = real_name; } Chris@16: std::string const &get_current_relative_filename() const Chris@16: { return current_relative_filename; } Chris@16: Chris@16: bool find_include_file (std::string &s, std::string &d, bool is_system, Chris@16: char const *current_file) const Chris@16: { return includes.find_include_file(s, d, is_system, current_file); } Chris@16: Chris@16: #if BOOST_WAVE_SERIALIZATION != 0 Chris@16: public: Chris@16: BOOST_STATIC_CONSTANT(unsigned int, version = 0x10); Chris@16: BOOST_STATIC_CONSTANT(unsigned int, version_mask = 0x0f); Chris@16: Chris@16: private: Chris@16: friend class boost::serialization::access; Chris@16: template Chris@16: void save(Archive & ar, const unsigned int version) const Chris@16: { Chris@16: using namespace boost::serialization; Chris@16: Chris@16: string_type cfg(BOOST_PP_STRINGIZE(BOOST_WAVE_CONFIG)); Chris@16: string_type kwd(BOOST_WAVE_PRAGMA_KEYWORD); Chris@16: string_type strtype(BOOST_PP_STRINGIZE((BOOST_WAVE_STRINGTYPE))); Chris@16: ar & make_nvp("config", cfg); Chris@16: ar & make_nvp("pragma_keyword", kwd); Chris@16: ar & make_nvp("string_type", strtype); Chris@16: Chris@16: ar & make_nvp("language_options", language); Chris@16: ar & make_nvp("macro_definitions", macros); Chris@16: ar & make_nvp("include_settings", includes); Chris@16: } Chris@16: template Chris@16: void load(Archive & ar, const unsigned int loaded_version) Chris@16: { Chris@16: using namespace boost::serialization; Chris@16: if (version != (loaded_version & ~version_mask)) { Chris@16: BOOST_WAVE_THROW_CTX((*this), preprocess_exception, Chris@16: incompatible_config, "cpp_context state version", Chris@16: get_main_pos()); Chris@16: return; Chris@16: } Chris@16: Chris@16: // check compatibility of the stored information Chris@16: string_type config, pragma_keyword, string_type_str; Chris@16: Chris@16: // BOOST_PP_STRINGIZE(BOOST_WAVE_CONFIG) Chris@16: ar & make_nvp("config", config); Chris@16: if (config != BOOST_PP_STRINGIZE(BOOST_WAVE_CONFIG)) { Chris@16: BOOST_WAVE_THROW_CTX((*this), preprocess_exception, Chris@16: incompatible_config, "BOOST_WAVE_CONFIG", get_main_pos()); Chris@16: return; Chris@16: } Chris@16: Chris@16: // BOOST_WAVE_PRAGMA_KEYWORD Chris@16: ar & make_nvp("pragma_keyword", pragma_keyword); Chris@16: if (pragma_keyword != BOOST_WAVE_PRAGMA_KEYWORD) { Chris@16: BOOST_WAVE_THROW_CTX((*this), preprocess_exception, Chris@16: incompatible_config, "BOOST_WAVE_PRAGMA_KEYWORD", Chris@16: get_main_pos()); Chris@16: return; Chris@16: } Chris@16: Chris@16: // BOOST_PP_STRINGIZE((BOOST_WAVE_STRINGTYPE)) Chris@16: ar & make_nvp("string_type", string_type_str); Chris@16: if (string_type_str != BOOST_PP_STRINGIZE((BOOST_WAVE_STRINGTYPE))) { Chris@16: BOOST_WAVE_THROW_CTX((*this), preprocess_exception, Chris@16: incompatible_config, "BOOST_WAVE_STRINGTYPE", get_main_pos()); Chris@16: return; Chris@16: } Chris@16: Chris@16: try { Chris@16: // read in the useful bits Chris@16: ar & make_nvp("language_options", language); Chris@16: ar & make_nvp("macro_definitions", macros); Chris@16: ar & make_nvp("include_settings", includes); Chris@16: } Chris@16: catch (boost::wave::preprocess_exception const& e) { Chris@16: // catch version mismatch exceptions and call error handler Chris@16: get_hooks().throw_exception(derived(), e); Chris@16: } Chris@16: } Chris@16: BOOST_SERIALIZATION_SPLIT_MEMBER() Chris@16: #endif Chris@16: Chris@16: private: Chris@16: // the main input stream Chris@16: target_iterator_type first; // underlying input stream Chris@16: target_iterator_type last; Chris@16: std::string filename; // associated main filename Chris@16: bool has_been_initialized; // set cwd once Chris@16: #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 Chris@16: std::string current_filename; // real name of current preprocessed file Chris@16: #endif Chris@16: std::string current_relative_filename; // real relative name of current preprocessed file Chris@16: Chris@16: boost::wave::util::if_block_stack ifblocks; // conditional compilation contexts Chris@16: boost::wave::util::include_paths includes; // lists of include directories to search Chris@16: iteration_context_stack_type iter_ctxs; // iteration contexts Chris@16: macromap_type macros; // map of defined macros Chris@16: boost::wave::language_support language; // supported language/extensions Chris@16: hook_policy_type hooks; // hook policy instance Chris@16: }; Chris@16: Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: } // namespace wave Chris@16: } // namespace boost Chris@16: Chris@16: #if BOOST_WAVE_SERIALIZATION != 0 Chris@16: namespace boost { namespace serialization { Chris@16: Chris@16: template< Chris@16: typename Iterator, typename LexIterator, Chris@16: typename InputPolicy, typename Hooks Chris@16: > Chris@16: struct tracking_level > Chris@16: { Chris@16: typedef mpl::integral_c_tag tag; Chris@16: typedef mpl::int_ type; Chris@16: BOOST_STATIC_CONSTANT( Chris@16: int, Chris@16: value = tracking_level::type::value Chris@16: ); Chris@16: }; Chris@16: Chris@16: template< Chris@16: typename Iterator, typename LexIterator, Chris@16: typename InputPolicy, typename Hooks Chris@16: > Chris@16: struct version > Chris@16: { Chris@16: typedef boost::wave::context Chris@16: target_type; Chris@16: typedef mpl::int_ type; Chris@16: typedef mpl::integral_c_tag tag; Chris@16: BOOST_STATIC_CONSTANT(unsigned int, value = version::type::value); Chris@16: }; Chris@16: Chris@16: }} // namespace boost::serialization Chris@16: #endif Chris@16: Chris@16: // the suffix header occurs after all of the code Chris@16: #ifdef BOOST_HAS_ABI_HEADERS Chris@16: #include BOOST_ABI_SUFFIX Chris@16: #endif Chris@16: Chris@16: #endif // !defined(CPP_CONTEXT_HPP_907485E2_6649_4A87_911B_7F7225F3E5B8_INCLUDED)