]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/wave/util/cpp_macromap.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / wave / util / cpp_macromap.hpp
index ae4b712cdcc29e15ce617636996953c65ba9461d..5b09fcdcbca6d38e6858324c12884893366274db 100644 (file)
@@ -10,8 +10,8 @@
     LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 =============================================================================*/
 
-#if !defined(CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED)
-#define CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED
+#if !defined(BOOST_CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED)
+#define BOOST_CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED
 
 #include <cstdlib>
 #include <cstdio>
@@ -33,6 +33,7 @@
 
 #include <boost/filesystem/path.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
 
 #include <boost/wave/util/time_conversion_helper.hpp>
 #include <boost/wave/util/unput_queue_iterator.hpp>
@@ -43,6 +44,9 @@
 #include <boost/wave/util/cpp_macromap_predef.hpp>
 #include <boost/wave/util/filesystem_compatibility.hpp>
 #include <boost/wave/grammars/cpp_defined_grammar_gen.hpp>
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+#include <boost/wave/grammars/cpp_has_include_grammar_gen.hpp>
+#endif
 
 #include <boost/wave/wave_version.hpp>
 #include <boost/wave/cpp_exceptions.hpp>
@@ -91,59 +95,68 @@ public:
     }
     ~macromap() {}
 
-//  Add a new macro to the given macro scope
+    //  Add a new macro to the given macro scope
     bool add_macro(token_type const &name, bool has_parameters,
         parameter_container_type &parameters,
         definition_container_type &definition, bool is_predefined = false,
         defined_macros_type *scope = 0);
 
-//  Tests, whether the given macro name is defined in the given macro scope
+    //  Tests, whether the given macro name is defined in the given macro scope
     bool is_defined(string_type const &name,
         typename defined_macros_type::iterator &it,
         defined_macros_type *scope = 0) const;
 
-// expects a token sequence as its parameters
+    // expects a token sequence as its parameters
     template <typename IteratorT>
     bool is_defined(IteratorT const &begin, IteratorT const &end) const;
 
-// expects an arbitrary string as its parameter
+    // expects an arbitrary string as its parameter
     bool is_defined(string_type const &str) const;
 
-//  Get the macro definition for the given macro scope
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+    // expects a token sequence as its parameters
+    template <typename IteratorT>
+    bool has_include(IteratorT const &begin, IteratorT const &end,
+                     bool is_quoted_filename, bool is_system) const;
+#endif
+
+    //  Get the macro definition for the given macro scope
     bool get_macro(string_type const &name, bool &has_parameters,
         bool &is_predefined, position_type &pos,
         parameter_container_type &parameters,
         definition_container_type &definition,
         defined_macros_type *scope = 0) const;
 
-//  Remove a macro name from the given macro scope
+    //  Remove a macro name from the given macro scope
     bool remove_macro(string_type const &name, position_type const& pos,
         bool even_predefined = false);
 
     template <typename IteratorT, typename ContainerT>
     token_type const &expand_tokensequence(IteratorT &first,
         IteratorT const &last, ContainerT &pending, ContainerT &expanded,
-        bool& seen_newline, bool expand_operator_defined);
+        bool& seen_newline, bool expand_operator_defined,
+        bool expand_operator_has_include);
 
-//  Expand all macros inside the given token sequence
+    //  Expand all macros inside the given token sequence
     template <typename IteratorT, typename ContainerT>
     void expand_whole_tokensequence(ContainerT &expanded,
         IteratorT &first, IteratorT const &last,
-        bool expand_operator_defined);
+        bool expand_operator_defined,
+        bool expand_operator_has_include);
 
-//  Init the predefined macros (add them to the given scope)
+    //  Init the predefined macros (add them to the given scope)
     void init_predefined_macros(char const *fname = "<Unknown>",
         defined_macros_type *scope = 0, bool at_global_scope = true);
     void predefine_macro(defined_macros_type *scope, string_type const &name,
         token_type const &t);
 
-//  Init the internal macro symbol namespace
+    //  Init the internal macro symbol namespace
     void reset_macromap();
 
     position_type &get_main_pos() { return main_pos; }
     position_type const& get_main_pos() const { return main_pos; }
 
-//  interface for macro name introspection
+    //  interface for macro name introspection
     typedef typename defined_macros_type::name_iterator name_iterator;
     typedef typename defined_macros_type::const_name_iterator const_name_iterator;
 
@@ -157,12 +170,14 @@ public:
         { return defined_macros_type::make_iterator(current_macros->end()); }
 
 protected:
-//  Helper functions for expanding all macros in token sequences
+    //  Helper functions for expanding all macros in token sequences
     template <typename IteratorT, typename ContainerT>
     token_type const &expand_tokensequence_worker(ContainerT &pending,
         unput_queue_iterator<IteratorT, token_type, ContainerT> &first,
         unput_queue_iterator<IteratorT, token_type, ContainerT> const &last,
-        bool& seen_newline, bool expand_operator_defined);
+        bool& seen_newline, bool expand_operator_defined,
+        bool expand_operator_has_include,
+        boost::optional<position_type> expanding_pos);
 
 //  Collect all arguments supplied to a macro invocation
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
@@ -179,51 +194,65 @@ protected:
         SizeT const &parameter_count, bool& seen_newline);
 #endif
 
-//  Expand a single macro name
+    //  Expand a single macro name
     template <typename IteratorT, typename ContainerT>
     bool expand_macro(ContainerT &pending, token_type const &name,
         typename defined_macros_type::iterator it,
         IteratorT &first, IteratorT const &last,
         bool& seen_newline, bool expand_operator_defined,
+        bool expand_operator_has_include,
+        boost::optional<position_type> expanding_pos,
         defined_macros_type *scope = 0, ContainerT *queue_symbol = 0);
 
-//  Expand a predefined macro (__LINE__, __FILE__ and __INCLUDE_LEVEL__)
+    //  Expand a predefined macro (__LINE__, __FILE__ and __INCLUDE_LEVEL__)
     template <typename ContainerT>
     bool expand_predefined_macro(token_type const &curr_token,
         ContainerT &expanded);
 
-//  Expand a single macro argument
+    //  Expand a single macro argument
     template <typename ContainerT>
     void expand_argument (typename std::vector<ContainerT>::size_type arg,
         std::vector<ContainerT> &arguments,
         std::vector<ContainerT> &expanded_args, bool expand_operator_defined,
+        bool expand_operator_has_include,
         std::vector<bool> &has_expanded_args);
 
-//  Expand the replacement list (replaces parameters with arguments)
+    //  Expand the replacement list (replaces parameters with arguments)
     template <typename ContainerT>
     void expand_replacement_list(
-        macro_definition_type const &macrodefinition,
+        typename macro_definition_type::const_definition_iterator_t cbeg,
+        typename macro_definition_type::const_definition_iterator_t cend,
         std::vector<ContainerT> &arguments,
-        bool expand_operator_defined, ContainerT &expanded);
+        bool expand_operator_defined,
+        bool expand_operator_has_include,
+        ContainerT &expanded);
 
-//  Rescans the replacement list for macro expansion
+    //  Rescans the replacement list for macro expansion
     template <typename IteratorT, typename ContainerT>
     void rescan_replacement_list(token_type const &curr_token,
         macro_definition_type &macrodef, ContainerT &replacement_list,
         ContainerT &expanded, bool expand_operator_defined,
+        bool expand_operator_has_include,
         IteratorT &nfirst, IteratorT const &nlast);
 
-//  Resolves the operator defined() and replces the token with "0" or "1"
+    //  Resolves the operator defined() and replaces the token with "0" or "1"
     template <typename IteratorT, typename ContainerT>
     token_type const &resolve_defined(IteratorT &first, IteratorT const &last,
         ContainerT &expanded);
 
-//  Resolve operator _Pragma or the #pragma directive
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+    //  Resolves the operator __has_include() and replaces the token with "0" or "1"
+    template <typename IteratorT, typename ContainerT>
+    token_type const &resolve_has_include(IteratorT &first, IteratorT const &last,
+        ContainerT &expanded);
+#endif
+
+    //  Resolve operator _Pragma or the #pragma directive
     template <typename IteratorT, typename ContainerT>
     bool resolve_operator_pragma(IteratorT &first,
         IteratorT const &last, ContainerT &expanded, bool& seen_newline);
 
-//  Handle the concatenation operator '##'
+    //  Handle the concatenation operator '##'
     template <typename ContainerT>
     bool concat_tokensequence(ContainerT &expanded);
 
@@ -231,6 +260,12 @@ protected:
     bool is_valid_concat(string_type new_value,
         position_type const &pos, ContainerT &rescanned);
 
+    static bool is_space(char);
+
+    // batch update tokens with a single expand position
+    template <typename ContainerT>
+    static void set_expand_positions(ContainerT &tokens, position_type pos);
+
 #if BOOST_WAVE_SERIALIZATION != 0
 public:
     BOOST_STATIC_CONSTANT(unsigned int, version = 0x10);
@@ -282,8 +317,8 @@ macromap<ContextT>::add_macro(token_type const &name, bool has_parameters,
     parameter_container_type &parameters, definition_container_type &definition,
     bool is_predefined, defined_macros_type *scope)
 {
-    if (!is_predefined && impl::is_special_macroname (name.get_value())) {
-    // exclude special macro names
+    if (!is_predefined && impl::is_special_macroname (ctx, name.get_value())) {
+        // exclude special macro names
         BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
             illegal_redefinition, name.get_value().c_str(), main_pos,
             name.get_value().c_str());
@@ -292,26 +327,46 @@ macromap<ContextT>::add_macro(token_type const &name, bool has_parameters,
     if (boost::wave::need_variadics(ctx.get_language()) &&
         "__VA_ARGS__" == name.get_value())
     {
-    // can't use __VA_ARGS__ as a macro name
+        // can't use __VA_ARGS__ as a macro name
         BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
             bad_define_statement_va_args, name.get_value().c_str(), main_pos,
             name.get_value().c_str());
         return false;
     }
+    if (boost::wave::need_variadics(ctx.get_language()) &&
+        "__VA_OPT__" == name.get_value())
+    {
+        // can't use __VA_OPT__ as a macro name
+        BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
+            bad_define_statement_va_opt, name.get_value().c_str(), main_pos,
+            name.get_value().c_str());
+        return false;
+    }
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+    if (boost::wave::need_has_include(ctx.get_language()) &&
+        "__has_include" == name.get_value())
+    {
+        // can't use __has_include as a macro name
+        BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
+            bad_define_statement_va_opt, name.get_value().c_str(), main_pos,
+            name.get_value().c_str());
+        return false;
+    }
+#endif
     if (AltExtTokenType == (token_id(name) & ExtTokenOnlyMask)) {
-    // exclude special operator names
+        // exclude special operator names
         BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
             illegal_operator_redefinition, name.get_value().c_str(), main_pos,
             name.get_value().c_str());
         return false;
     }
 
-// try to define the new macro
-defined_macros_type *current_scope = scope ? scope : current_macros;
-typename defined_macros_type::iterator it = current_scope->find(name.get_value());
+    // try to define the new macro
+    defined_macros_type* current_scope = scope ? scope : current_macros;
+    typename defined_macros_type::iterator it = current_scope->find(name.get_value());
 
     if (it != current_scope->end()) {
-    // redefinition, should not be different
+        // redefinition, should not be different
         macro_definition_type* macrodef = (*it).second.get();
         if (macrodef->is_functionlike != has_parameters ||
             !impl::parameters_equal(macrodef->macroparameters, parameters) ||
@@ -324,7 +379,7 @@ typename defined_macros_type::iterator it = current_scope->find(name.get_value()
         return false;
     }
 
-// test the validity of the parameter names
+    // test the validity of the parameter names
     if (has_parameters) {
         std::set<typename token_type::string_type> names;
 
@@ -339,7 +394,7 @@ typename defined_macros_type::iterator it = current_scope->find(name.get_value()
         name_iterator_type pit = names.find((*itp).get_value());
 
             if (pit != names.end()) {
-            // duplicate parameter name
+                // duplicate parameter name
                 BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
                     duplicate_parameter_name, (*pit).c_str(), main_pos,
                     name.get_value().c_str());
@@ -349,7 +404,52 @@ typename defined_macros_type::iterator it = current_scope->find(name.get_value()
         }
     }
 
-// insert a new macro node
+#if BOOST_WAVE_SUPPORT_VA_OPT != 0
+    // check that __VA_OPT__ is used as a function macro
+    if (boost::wave::need_va_opt(ctx.get_language())) {
+        // __VA_OPT__, if present, must be followed by an lparen
+        typedef typename macro_definition_type::const_definition_iterator_t iter_t;
+        iter_t mdit = definition.begin();
+        iter_t mdend = definition.end();
+        for (; mdit != mdend; ++mdit) {
+            // is this va_opt?
+            if ((IS_EXTCATEGORY((*mdit), OptParameterTokenType)) ||  // if params replaced
+                ("__VA_OPT__" == (*mdit).get_value())) {             // if not
+                iter_t va_opt_it = mdit;
+                // next must be lparen
+                if ((++mdit == mdend) ||                             // no further tokens
+                    (T_LEFTPAREN != token_id(*mdit))) {              // not lparen
+                    BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
+                        bad_define_statement_va_opt_parens,
+                        name.get_value().c_str(), main_pos,
+                        name.get_value().c_str());
+                    return false;
+                }
+                // check that no __VA_OPT__ appears inside
+                iter_t va_opt_end = va_opt_it;
+                if (!impl::find_va_opt_args(va_opt_end, mdend)) {
+                    BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
+                        improperly_terminated_macro, "missing ')' in __VA_OPT__",
+                        main_pos);
+                    return false;
+                }
+                // skip initial __VA_OPT__ and lparen
+                ++va_opt_it; ++va_opt_it;
+                for (;va_opt_it != va_opt_end; ++va_opt_it) {
+                    if ((IS_EXTCATEGORY((*va_opt_it), OptParameterTokenType)) ||
+                        ("__VA_OPT__" == (*va_opt_it).get_value())) {
+                        BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
+                            bad_define_statement_va_opt_recurse,
+                            name.get_value().c_str(), (*va_opt_it).get_position(),
+                            name.get_value().c_str());
+                    }
+                }
+            }
+        }
+    }
+#endif
+
+    // insert a new macro node
     std::pair<typename defined_macros_type::iterator, bool> p =
         current_scope->insert(
             typename defined_macros_type::value_type(
@@ -367,7 +467,7 @@ typename defined_macros_type::iterator it = current_scope->find(name.get_value()
         return false;
     }
 
-// add the parameters and the definition
+    // add the parameters and the definition
     std::swap((*p.first).second->macroparameters, parameters);
     std::swap((*p.first).second->macrodefinition, definition);
 
@@ -400,12 +500,20 @@ macromap<ContextT>::is_defined(typename token_type::string_type const &name,
     if ((it = scope->find(name)) != scope->end())
         return true;        // found in symbol table
 
-// quick pre-check
+    // quick pre-check
     if (name.size() < 8 || '_' != name[0] || '_' != name[1])
         return false;       // quick check failed
 
-    return name == "__LINE__" || name == "__FILE__" ||
-        name == "__INCLUDE_LEVEL__";
+    if (name == "__LINE__" || name == "__FILE__" ||
+        name == "__INCLUDE_LEVEL__")
+        return true;
+
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+    return (boost::wave::need_has_include(ctx.get_language()) &&
+            (name == "__has_include"));
+#else
+    return false;
+#endif
 }
 
 template <typename ContextT>
@@ -414,9 +522,9 @@ inline bool
 macromap<ContextT>::is_defined(IteratorT const &begin,
     IteratorT const &end) const
 {
-// in normal mode the name under inspection should consist of an identifier
-// only
-token_id id = token_id(*begin);
+    // in normal mode the name under inspection should consist of an identifier
+    // only
+    token_id id = token_id(*begin);
 
     if (T_IDENTIFIER != id &&
         !IS_CATEGORY(id, KeywordTokenType) &&
@@ -429,12 +537,12 @@ token_id id = token_id(*begin);
         return false;
     }
 
-IteratorT it = begin;
-string_type name ((*it).get_value());
-typename defined_macros_type::iterator cit;
+    IteratorT it = begin;
+    string_type name((*it).get_value());
+    typename defined_macros_type::iterator cit;
 
     if (++it != end) {
-    // there should be only one token as the inspected name
+        // there should be only one token as the inspected name
         std::string msg(impl::get_full_name(begin, end));
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, invalid_macroname,
             msg.c_str(), main_pos);
@@ -453,6 +561,51 @@ macromap<ContextT>::is_defined(string_type const &str) const
     return is_defined(str, cit, 0);
 }
 
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+///////////////////////////////////////////////////////////////////////////////
+//
+//  has_include(): returns whether a path expression is an valid include file
+//
+///////////////////////////////////////////////////////////////////////////////
+template <typename ContextT>
+template <typename IteratorT>
+inline bool
+macromap<ContextT>::has_include(
+    IteratorT const &begin, IteratorT const &end,
+    bool is_quoted_filename, bool is_system) const
+{
+    typename ContextT::token_sequence_type filetoks;
+
+    if (is_quoted_filename) {
+        filetoks = typename ContextT::token_sequence_type(begin, end);
+    } else {
+        IteratorT first = begin;
+        IteratorT last = end;
+        ctx.expand_whole_tokensequence(first, last, filetoks);
+    }
+
+    // extract tokens into string and trim whitespace
+    using namespace boost::wave::util::impl;
+    std::string fn(trim_whitespace(as_string(filetoks)).c_str());
+
+    // verify and remove initial and final delimiters
+    if (!((fn.size() >= 3) &&
+          (((fn[0] == '"') && (*fn.rbegin() == '"')) ||
+           ((fn[0] == '<') && (*fn.rbegin() == '>')))))
+        BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_has_include_expression,
+                             fn.c_str(), ctx.get_main_pos());
+
+    fn = fn.substr(1, fn.size() - 2);
+
+    // test header existence
+    std::string dir_path;
+    std::string native_path;
+    return ctx.get_hooks().locate_include_file(
+        ctx, fn, is_system, 0, dir_path, native_path);
+
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 //
 //  Get the macro definition for the given macro scope
@@ -470,7 +623,7 @@ macromap<ContextT>::get_macro(string_type const &name, bool &has_parameters,
     if (!is_defined(name, it, scope))
         return false;
 
-macro_definition_type &macro_def = *(*it).second.get();
+    macro_definition_type& macro_def = *(*it).second.get();
 
     has_parameters = macro_def.is_functionlike;
     is_predefined = macro_def.is_predefined;
@@ -494,7 +647,7 @@ macromap<ContextT>::remove_macro(string_type const &name,
 
     if (it != current_macros->end()) {
         if ((*it).second->is_predefined) {
-            if (!even_predefined || impl::is_special_macroname(name)) {
+            if (!even_predefined || impl::is_special_macroname(ctx, name)) {
                 BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                     bad_undefine_statement, name.c_str(), main_pos);
                 return false;
@@ -502,8 +655,8 @@ macromap<ContextT>::remove_macro(string_type const &name,
         }
         current_macros->erase(it);
 
-    // call the context supplied preprocessing hook function
-    token_type tok(T_IDENTIFIER, name, pos);
+        // call the context supplied preprocessing hook function
+        token_type tok(T_IDENTIFIER, name, pos);
 
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
         ctx.get_hooks().undefined_macro(tok);
@@ -512,7 +665,7 @@ macromap<ContextT>::remove_macro(string_type const &name,
 #endif
         return true;
     }
-    else if (impl::is_special_macroname(name)) {
+    else if (impl::is_special_macroname(ctx, name)) {
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_undefine_statement,
             name.c_str(), pos);
     }
@@ -536,7 +689,8 @@ template <typename IteratorT, typename ContainerT>
 inline typename ContextT::token_type const &
 macromap<ContextT>::expand_tokensequence(IteratorT &first,
     IteratorT const &last, ContainerT &pending, ContainerT &expanded,
-    bool& seen_newline, bool expand_operator_defined)
+    bool& seen_newline, bool expand_operator_defined,
+    bool expand_operator_has_include)
 {
     typedef impl::gen_unput_queue_iterator<IteratorT, token_type, ContainerT>
         gen_type;
@@ -545,10 +699,11 @@ macromap<ContextT>::expand_tokensequence(IteratorT &first,
     iterator_type first_it = gen_type::generate(expanded, first);
     iterator_type last_it = gen_type::generate(last);
 
-on_exit::assign<IteratorT, iterator_type> on_exit(first, first_it);
+    on_exit::assign<IteratorT, iterator_type> on_exit(first, first_it);
 
     return expand_tokensequence_worker(pending, first_it, last_it,
-        seen_newline, expand_operator_defined);
+        seen_newline, expand_operator_defined, expand_operator_has_include,
+        boost::none);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -570,26 +725,28 @@ macromap<ContextT>::expand_tokensequence_worker(
     ContainerT &pending,
     unput_queue_iterator<IteratorT, token_type, ContainerT> &first,
     unput_queue_iterator<IteratorT, token_type, ContainerT> const &last,
-    bool& seen_newline, bool expand_operator_defined)
+    bool& seen_newline, bool expand_operator_defined,
+    bool expand_operator_has_include,
+    boost::optional<position_type> expanding_pos)
 {
-// if there exist pending tokens (tokens, which are already preprocessed), then
-// return the next one from there
+    // if there exist pending tokens (tokens, which are already preprocessed), then
+    // return the next one from there
     if (!pending.empty()) {
-    on_exit::pop_front<definition_container_type> pop_front_token(pending);
+        on_exit::pop_front<definition_container_type> pop_front_token(pending);
 
         return act_token = pending.front();
     }
 
-//  analyze the next element of the given sequence, if it is an
-//  T_IDENTIFIER token, try to replace this as a macro etc.
+    //  analyze the next element of the given sequence, if it is an
+    //  T_IDENTIFIER token, try to replace this as a macro etc.
     using namespace boost::wave;
 
     if (first != last) {
-    token_id id = token_id(*first);
+        token_id id = token_id(*first);
 
-    // ignore placeholder tokens
+        // ignore placeholder tokens
         if (T_PLACEHOLDER == id) {
-        token_type placeholder = *first;
+            token_type placeholder = *first;
 
             ++first;
             if (first == last)
@@ -606,70 +763,83 @@ macromap<ContextT>::expand_tokensequence_worker(
             // resolve operator defined()
                 return resolve_defined(first, last, pending);
             }
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+            else if (boost::wave::need_has_include(ctx.get_language()) &&
+                     expand_operator_has_include &&
+                     (*first).get_value() == "__has_include") {
+                // resolve operator __has_include()
+                return resolve_has_include(first, last, pending);
+            }
+#endif
             else if (boost::wave::need_variadics(ctx.get_language()) &&
                 (*first).get_value() == "_Pragma")
             {
-            // in C99 mode only: resolve the operator _Pragma
-            token_type curr_token = *first;
+                // in C99 mode only: resolve the operator _Pragma
+                token_type curr_token = *first;
 
                 if (!resolve_operator_pragma(first, last, pending, seen_newline) ||
                     pending.size() > 0)
                 {
-                // unknown to us pragma or supplied replacement, return the
-                // next token
-                on_exit::pop_front<definition_container_type> pop_token(pending);
+                    // unknown to us pragma or supplied replacement, return the
+                    // next token
+                    on_exit::pop_front<definition_container_type> pop_token(pending);
 
                     return act_token = pending.front();
                 }
 
-            // the operator _Pragma() was eaten completely, continue
+                // the operator _Pragma() was eaten completely, continue
                 return act_token = token_type(T_PLACEHOLDER, "_",
                     curr_token.get_position());
             }
 
-        token_type name_token (*first);
-        typename defined_macros_type::iterator it;
+            token_type name_token(*first);
+            typename defined_macros_type::iterator it;
 
             if (is_defined(name_token.get_value(), it)) {
-            // the current token contains an identifier, which is currently
-            // defined as a macro
+                // the current token contains an identifier, which is currently
+                // defined as a macro
                 if (expand_macro(pending, name_token, it, first, last,
-                      seen_newline, expand_operator_defined))
+                                 seen_newline, expand_operator_defined,
+                                 expand_operator_has_include,
+                                 expanding_pos))
                 {
-                // the tokens returned by expand_macro should be rescanned
-                // beginning at the last token of the returned replacement list
+                    // the tokens returned by expand_macro should be rescanned
+                    // beginning at the last token of the returned replacement list
                     if (first != last) {
-                    // splice the last token back into the input queue
-                    typename ContainerT::reverse_iterator rit = pending.rbegin();
+                        // splice the last token back into the input queue
+                        typename ContainerT::reverse_iterator rit = pending.rbegin();
 
                         first.get_unput_queue().splice(
                             first.get_unput_queue().begin(), pending,
                             (++rit).base(), pending.end());
                     }
 
-                // fall through ...
+                    // fall through ...
                 }
                 else if (!pending.empty()) {
-                // return the first token from the pending queue
-                on_exit::pop_front<definition_container_type> pop_queue (pending);
+                    // return the first token from the pending queue
+                    on_exit::pop_front<definition_container_type> pop_queue(pending);
 
                     return act_token = pending.front();
                 }
                 else {
-                // macro expansion reached the eoi
+                    // macro expansion reached the eoi
                     return act_token = token_type();
                 }
 
-            // return the next preprocessed token
-                return expand_tokensequence_worker(pending, first, last,
-                    seen_newline, expand_operator_defined);
+                // return the next preprocessed token
+                if (!expanding_pos)
+                    expanding_pos = name_token.get_expand_position();
+
+                typename ContextT::token_type const & result =
+                    expand_tokensequence_worker(
+                        pending, first, last,
+                        seen_newline, expand_operator_defined,
+                        expand_operator_has_include,
+                        expanding_pos);
+
+                return result;
             }
-//            else if (expand_operator_defined) {
-//            // in preprocessing conditionals undefined identifiers and keywords
-//            // are to be replaced with '0' (see. C++ standard 16.1.4, [cpp.cond])
-//                return act_token =
-//                    token_type(T_INTLIT, "0", (*first++).get_position());
-//            }
             else {
                 act_token = name_token;
                 ++first;
@@ -677,11 +847,11 @@ macromap<ContextT>::expand_tokensequence_worker(
             }
         }
         else if (expand_operator_defined && IS_CATEGORY(*first, BoolLiteralTokenType)) {
-        // expanding a constant expression inside #if/#elif, special handling
-        // of 'true' and 'false'
+            // expanding a constant expression inside #if/#elif, special handling
+            // of 'true' and 'false'
 
-        // all remaining identifiers and keywords, except for true and false,
-        // are replaced with the pp-number 0 (C++ standard 16.1.4, [cpp.cond])
+            // all remaining identifiers and keywords, except for true and false,
+            // are replaced with the pp-number 0 (C++ standard 16.1.4, [cpp.cond])
             return act_token = token_type(T_INTLIT, T_TRUE != id ? "0" : "1",
                 (*first++).get_position());
         }
@@ -721,15 +891,15 @@ macromap<ContextT>::collect_arguments (token_type const curr_token,
 
     arguments.push_back(ContainerT());
 
-// collect the actual arguments
-typename std::vector<ContainerT>::size_type count_arguments = 0;
-int nested_parenthesis_level = 1;
-ContainerT *argument = &arguments[0];
-bool was_whitespace = false;
-token_type startof_argument_list = *next;
+    // collect the actual arguments
+    typename std::vector<ContainerT>::size_type count_arguments = 0;
+    int nested_parenthesis_level = 1;
+    ContainerT* argument = &arguments[0];
+    bool was_whitespace = false;
+    token_type startof_argument_list = *next;
 
     while (++next != end && nested_parenthesis_level) {
-    token_id id = token_id(*next);
+        token_id id = token_id(*next);
 
         if (0 == parameter_count &&
             !IS_CATEGORY((*next), WhiteSpaceTokenType) && id != T_NEWLINE &&
@@ -783,14 +953,14 @@ token_type startof_argument_list = *next;
 
         case T_COMMA:
             if (1 == nested_parenthesis_level) {
-            // next parameter
+                // next parameter
 //                trim_sequence(argument);
                 if (argument->empty() ||
                     impl::is_whitespace_only(*argument))
                 {
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
                     if (boost::wave::need_variadics(ctx.get_language())) {
-                    // store a placemarker as the argument
+                        // store a placemarker as the argument
                         argument->push_back(token_type(T_PLACEMARKER, "\xA7",
                             (*next).get_position()));
                         ++count_arguments;
@@ -804,7 +974,7 @@ token_type startof_argument_list = *next;
                 argument = &arguments[arguments.size()-1];
             }
             else {
-            // surrounded by parenthesises, so store to current argument
+                // surrounded by parenthesises, so store to current argument
                 argument->push_back(*next);
             }
             was_whitespace = false;
@@ -832,14 +1002,14 @@ token_type startof_argument_list = *next;
     }
 
     if (nested_parenthesis_level >= 1) {
-    // missing ')': improperly terminated macro invocation
+        // missing ')': improperly terminated macro invocation
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
             improperly_terminated_macro, "missing ')'", main_pos);
         return 0;
     }
 
-// if no argument was expected and we didn't find any, than remove the empty
-// element
+    // if no argument was expected and we didn't find any, than remove the empty
+    // element
     if (0 == parameter_count && 0 == count_arguments) {
         BOOST_ASSERT(1 == arguments.size());
         arguments.clear();
@@ -858,8 +1028,9 @@ template <typename ContextT>
 template <typename IteratorT, typename ContainerT>
 inline void
 macromap<ContextT>::expand_whole_tokensequence(ContainerT &expanded,
-    IteratorT &first, IteratorT const &last,
-    bool expand_operator_defined)
+                                               IteratorT &first, IteratorT const &last,
+                                               bool expand_operator_defined,
+                                               bool expand_operator_has_include)
 {
     typedef impl::gen_unput_queue_iterator<IteratorT, token_type, ContainerT>
         gen_type;
@@ -875,12 +1046,15 @@ macromap<ContextT>::expand_whole_tokensequence(ContainerT &expanded,
 
     while (!pending_queue.empty() || first_it != last_it) {
         expanded.push_back(
-            expand_tokensequence_worker(pending_queue, first_it,
-                    last_it, seen_newline, expand_operator_defined)
+            expand_tokensequence_worker(
+                pending_queue, first_it,
+                last_it, seen_newline, expand_operator_defined,
+                expand_operator_has_include,
+                boost::none)
         );
     }
 
-// should have returned all expanded tokens
+    // should have returned all expanded tokens
     BOOST_ASSERT(pending_queue.empty()/* && unput_queue.empty()*/);
 }
 
@@ -897,18 +1071,20 @@ inline void
 macromap<ContextT>::expand_argument (
     typename std::vector<ContainerT>::size_type arg,
     std::vector<ContainerT> &arguments, std::vector<ContainerT> &expanded_args,
-    bool expand_operator_defined, std::vector<bool> &has_expanded_args)
+    bool expand_operator_defined, bool expand_operator_has_include,
+    std::vector<bool> &has_expanded_args)
 {
     if (!has_expanded_args[arg]) {
-    // expand the argument only once
+        // expand the argument only once
         typedef typename std::vector<ContainerT>::value_type::iterator
             argument_iterator_type;
 
         argument_iterator_type begin_it = arguments[arg].begin();
         argument_iterator_type end_it = arguments[arg].end();
 
-        expand_whole_tokensequence(expanded_args[arg], begin_it, end_it,
-            expand_operator_defined);
+        expand_whole_tokensequence(
+            expanded_args[arg], begin_it, end_it,
+            expand_operator_defined, expand_operator_has_include);
         impl::remove_placeholders(expanded_args[arg]);
         has_expanded_args[arg] = true;
     }
@@ -927,34 +1103,34 @@ template <typename ContextT>
 template <typename ContainerT>
 inline void
 macromap<ContextT>::expand_replacement_list(
-    macro_definition_type const &macrodef,
+    typename macro_definition_type::const_definition_iterator_t cit,
+    typename macro_definition_type::const_definition_iterator_t cend,
     std::vector<ContainerT> &arguments, bool expand_operator_defined,
+    bool expand_operator_has_include,
     ContainerT &expanded)
 {
     using namespace boost::wave;
     typedef typename macro_definition_type::const_definition_iterator_t
         macro_definition_iter_t;
 
-std::vector<ContainerT> expanded_args(arguments.size());
-std::vector<bool> has_expanded_args(arguments.size());
-bool seen_concat = false;
-bool adjacent_concat = false;
-bool adjacent_stringize = false;
+    std::vector<ContainerT> expanded_args(arguments.size());
+    std::vector<bool> has_expanded_args(arguments.size());
+    bool seen_concat = false;
+    bool adjacent_concat = false;
+    bool adjacent_stringize = false;
 
-    macro_definition_iter_t cend = macrodef.macrodefinition.end();
-    for (macro_definition_iter_t cit = macrodef.macrodefinition.begin();
-        cit != cend; ++cit)
+    for (;cit != cend; ++cit)
     {
-    bool use_replaced_arg = true;
-    token_id base_id = BASE_TOKEN(token_id(*cit));
+        bool use_replaced_arg = true;
+        token_id base_id = BASE_TOKEN(token_id(*cit));
 
         if (T_POUND_POUND == base_id) {
-        // concatenation operator
+            // concatenation operator
             adjacent_concat = true;
             seen_concat = true;
         }
         else if (T_POUND == base_id) {
-        // stringize operator
+            // stringize operator
             adjacent_stringize = true;
         }
         else {
@@ -969,10 +1145,13 @@ bool adjacent_stringize = false;
         }
 
         if (IS_CATEGORY((*cit), ParameterTokenType)) {
-        // copy argument 'i' instead of the parameter token i
-        typename ContainerT::size_type i;
+            // copy argument 'i' instead of the parameter token i
+            typename ContainerT::size_type i;
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
-        bool is_ellipsis = false;
+            bool is_ellipsis = false;
+#if BOOST_WAVE_SUPPORT_VA_OPT != 0
+            bool is_va_opt = false;
+#endif
 
             if (IS_EXTCATEGORY((*cit), ExtParameterTokenType)) {
                 BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language()));
@@ -980,38 +1159,140 @@ bool adjacent_stringize = false;
                 is_ellipsis = true;
             }
             else
+#if BOOST_WAVE_SUPPORT_VA_OPT != 0
+
+            if (IS_EXTCATEGORY((*cit), OptParameterTokenType)) {
+                BOOST_ASSERT(boost::wave::need_va_opt(ctx.get_language()));
+                i = token_id(*cit) - T_OPTPARAMETERBASE;
+                is_va_opt = true;
+            }
+            else
+#endif
 #endif
             {
                 i = token_id(*cit) - T_PARAMETERBASE;
             }
 
-            BOOST_ASSERT(i < arguments.size());
+            BOOST_ASSERT(i <= arguments.size());
             if (use_replaced_arg) {
 
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
                 if (is_ellipsis) {
-                position_type const &pos = (*cit).get_position();
+                    position_type const& pos = (*cit).get_position();
 
                     BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language()));
 
-                // ensure all variadic arguments to be expanded
+                    // ensure all variadic arguments to be expanded
                     for (typename vector<ContainerT>::size_type arg = i;
                          arg < expanded_args.size(); ++arg)
                     {
-                        expand_argument(arg, arguments, expanded_args,
-                            expand_operator_defined, has_expanded_args);
+                        expand_argument(
+                            arg, arguments, expanded_args,
+                            expand_operator_defined, expand_operator_has_include,
+                            has_expanded_args);
                     }
                     impl::replace_ellipsis(expanded_args, i, expanded, pos);
                 }
                 else
+
+#if BOOST_WAVE_SUPPORT_VA_OPT != 0
+                if (is_va_opt) {
+                    position_type const &pos = (*cit).get_position();
+
+                    BOOST_ASSERT(boost::wave::need_va_opt(ctx.get_language()));
+
+                    // ensure all variadic arguments to be expanded
+                    for (typename vector<ContainerT>::size_type arg = i;
+                         arg < expanded_args.size(); ++arg)
+                    {
+                        expand_argument(
+                            arg, arguments, expanded_args,
+                            expand_operator_defined, expand_operator_has_include,
+                            has_expanded_args);
+                    }
+
+                    // locate the end of the __VA_OPT__ call
+                    typename macro_definition_type::const_definition_iterator_t cstart = cit;
+                    if (!impl::find_va_opt_args(cit, cend)) {
+                        BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
+                            improperly_terminated_macro, "missing '(' or ')' in __VA_OPT__",
+                            pos);
+                    }
+                    // cstart still points to __VA_OPT__; cit now points to the last rparen
+
+                    // locate the __VA_OPT__ arguments
+                    typename macro_definition_type::const_definition_iterator_t arg_start = cstart;
+                    ++arg_start;  // skip __VA_OPT__
+                    ++arg_start;  // skip lparen
+
+                    // create a synthetic macro definition for use with hooks
+                    token_type macroname(T_IDENTIFIER, "__VA_OPT__", position_type("<built-in>"));
+                    parameter_container_type macroparameters;
+                    macroparameters.push_back(token_type(T_ELLIPSIS, "...", position_type("<built-in>")));
+                    definition_container_type macrodefinition;
+
+                    bool suppress_expand = false;
+                    // __VA_OPT__ treats its arguments as an undifferentiated stream of tokens
+                    // for our purposes we can consider it as a single argument
+                    typename std::vector<ContainerT> va_opt_args(1, ContainerT(arg_start, cit));
+#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
+                    ctx.get_hooks().expanding_function_like_macro(
+                        macroname, macroparameters, macrodefinitions,
+                        *cstart, va_opt_args);
+#else
+                    suppress_expand = ctx.get_hooks().expanding_function_like_macro(
+                        ctx.derived(),
+                        macroname, macroparameters, macrodefinition,
+                        *cstart, va_opt_args,
+                        cstart, cit);
+#endif
+
+                    if (suppress_expand) {
+                        // leave the whole expression in place
+                        std::copy(cstart, cit, std::back_inserter(expanded));
+                        expanded.push_back(*cit);  // include the rparen
+                    } else {
+                        ContainerT va_expanded;
+                        if ((i == arguments.size()) ||                 // no variadic argument
+                            impl::is_whitespace_only(arguments[i])) {  // no visible tokens
+                            // no args; insert placemarker
+                            va_expanded.push_back(
+                                typename ContainerT::value_type(T_PLACEMARKER, "\xA7", pos));
+                        } else if (!impl::is_blank_only(arguments[i])) {
+                            // [cstart, cit) is now the args to va_opt
+                            // recursively process them
+                            expand_replacement_list(arg_start, cit, arguments,
+                                                    expand_operator_defined,
+                                                    expand_operator_has_include,
+                                                    va_expanded);
+                        }
+                        // run final hooks
+#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
+                        ctx.get_hooks().expanded_macro(va_expanded);
+#else
+                        ctx.get_hooks().expanded_macro(ctx.derived(), va_expanded);
+#endif
+
+                        // updated overall expansion with va_opt results
+                        expanded.splice(expanded.end(), va_expanded);
+                    }
+                    // continue from rparen
+                }
+                else
+
+#endif
 #endif
                 {
-                // ensure argument i to be expanded
-                    expand_argument(i, arguments, expanded_args,
-                        expand_operator_defined, has_expanded_args);
+                    BOOST_ASSERT(i < arguments.size());
+                    // ensure argument i to be expanded
+                    expand_argument(
+                        i, arguments, expanded_args,
+                        expand_operator_defined, expand_operator_has_include,
+                        has_expanded_args);
 
-                // replace argument
-                ContainerT const &arg = expanded_args[i];
+                    // replace argument
+                    BOOST_ASSERT(i < expanded_args.size());
+                    ContainerT const& arg = expanded_args[i];
 
                     std::copy(arg.begin(), arg.end(),
                         std::inserter(expanded, expanded.end()));
@@ -1020,11 +1301,11 @@ bool adjacent_stringize = false;
             else if (adjacent_stringize &&
                     !IS_CATEGORY(*cit, WhiteSpaceTokenType))
             {
-            // stringize the current argument
+                // stringize the current argument
                 BOOST_ASSERT(!arguments[i].empty());
 
-            // safe a copy of the first tokens position (not a reference!)
-            position_type pos ((*arguments[i].begin()).get_position());
+                // safe a copy of the first tokens position (not a reference!)
+                position_type pos((*arguments[i].begin()).get_position());
 
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
                 if (is_ellipsis && boost::wave::need_variadics(ctx.get_language())) {
@@ -1043,20 +1324,33 @@ bool adjacent_stringize = false;
                 adjacent_stringize = false;
             }
             else {
-            // simply copy the original argument (adjacent '##' or '#')
+                // simply copy the original argument (adjacent '##' or '#')
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
                 if (is_ellipsis) {
-                position_type const &pos = (*cit).get_position();
+                    position_type const& pos = (*cit).get_position();
+#if BOOST_WAVE_SUPPORT_CPP2A != 0
+                    if (i < arguments.size())
+#endif
+                    {
 
-                    impl::trim_sequence_left(arguments[i]);
-                    impl::trim_sequence_right(arguments.back());
-                    BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language()));
-                    impl::replace_ellipsis(arguments, i, expanded, pos);
+                        impl::trim_sequence_left(arguments[i]);
+                        impl::trim_sequence_right(arguments.back());
+                        BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language()));
+                        impl::replace_ellipsis(arguments, i, expanded, pos);
+                    }
+#if BOOST_WAVE_SUPPORT_CPP2A != 0
+                    else if (boost::wave::need_cpp2a(ctx.get_language())) {
+                        BOOST_ASSERT(i == arguments.size());
+                        // no argument supplied; insert placemarker
+                        expanded.push_back(
+                            typename ContainerT::value_type(T_PLACEMARKER, "\xA7", pos));
+                    }
+#endif
                 }
                 else
 #endif
                 {
-                ContainerT &arg = arguments[i];
+                    ContainerT& arg = arguments[i];
 
                     impl::trim_sequence(arg);
                     std::copy(arg.begin(), arg.end(),
@@ -1065,19 +1359,19 @@ bool adjacent_stringize = false;
             }
         }
         else if (!adjacent_stringize || T_POUND != base_id) {
-        // insert the actual replacement token (if it is not the '#' operator)
+            // insert the actual replacement token (if it is not the '#' operator)
             expanded.push_back(*cit);
         }
     }
 
     if (adjacent_stringize) {
-    // error, '#' should not be the last token
+        // error, '#' should not be the last token
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_operator,
             "stringize ('#')", main_pos);
         return;
     }
 
-// handle the cpp.concat operator
+    // handle the cpp.concat operator
     if (seen_concat)
         concat_tokensequence(expanded);
 }
@@ -1095,20 +1389,22 @@ template <typename IteratorT, typename ContainerT>
 inline void
 macromap<ContextT>::rescan_replacement_list(token_type const &curr_token,
     macro_definition_type &macro_def, ContainerT &replacement_list,
-    ContainerT &expanded, bool expand_operator_defined,
+    ContainerT &expanded,
+    bool expand_operator_defined,
+    bool expand_operator_has_include,
     IteratorT &nfirst, IteratorT const &nlast)
 {
     if (!replacement_list.empty()) {
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
-    // remove the placemarkers
+        // remove the placemarkers
         if (boost::wave::need_variadics(ctx.get_language())) {
-        typename ContainerT::iterator end = replacement_list.end();
-        typename ContainerT::iterator it = replacement_list.begin();
+            typename ContainerT::iterator end = replacement_list.end();
+            typename ContainerT::iterator it = replacement_list.begin();
 
             while (it != end) {
                 using namespace boost::wave;
                 if (T_PLACEMARKER == token_id(*it)) {
-                typename ContainerT::iterator placemarker = it;
+                    typename ContainerT::iterator placemarker = it;
 
                     ++it;
                     replacement_list.erase(placemarker);
@@ -1120,22 +1416,23 @@ macromap<ContextT>::rescan_replacement_list(token_type const &curr_token,
         }
 #endif
 
-    // rescan the replacement list, during this rescan the current macro under
-    // expansion isn't available as an expandable macro
-    on_exit::reset<bool> on_exit(macro_def.is_available_for_replacement, false);
-    typename ContainerT::iterator begin_it = replacement_list.begin();
-    typename ContainerT::iterator end_it = replacement_list.end();
+        // rescan the replacement list, during this rescan the current macro under
+        // expansion isn't available as an expandable macro
+        on_exit::reset<bool> on_exit(macro_def.is_available_for_replacement, false);
+        typename ContainerT::iterator begin_it = replacement_list.begin();
+        typename ContainerT::iterator end_it = replacement_list.end();
 
-        expand_whole_tokensequence(expanded, begin_it, end_it,
-            expand_operator_defined);
+        expand_whole_tokensequence(
+            expanded, begin_it, end_it,
+            expand_operator_defined, expand_operator_has_include);
 
-    // trim replacement list, leave placeholder tokens untouched
+        // trim replacement list, leave placeholder tokens untouched
         impl::trim_replacement_list(expanded);
     }
 
     if (expanded.empty()) {
-    // the resulting replacement list should contain at least a placeholder
-    // token
+        // the resulting replacement list should contain at least a placeholder
+        // token
         expanded.push_back(token_type(T_PLACEHOLDER, "_", curr_token.get_position()));
     }
 }
@@ -1156,6 +1453,8 @@ macromap<ContextT>::expand_macro(ContainerT &expanded,
     token_type const &curr_token, typename defined_macros_type::iterator it,
     IteratorT &first, IteratorT const &last,
     bool& seen_newline, bool expand_operator_defined,
+    bool expand_operator_has_include,
+    boost::optional<position_type> expanding_pos,
     defined_macros_type *scope, ContainerT *queue_symbol)
 {
     using namespace boost::wave;
@@ -1170,11 +1469,11 @@ macromap<ContextT>::expand_macro(ContainerT &expanded,
     if (it == scope->end()) {
         ++first;    // advance
 
-    // try to expand a predefined macro (__FILE__, __LINE__ or __INCLUDE_LEVEL__)
+        // try to expand a predefined macro (__FILE__, __LINE__ or __INCLUDE_LEVEL__)
         if (expand_predefined_macro(curr_token, expanded))
             return false;
 
-    // not defined as a macro
+        // not defined as a macro
         if (0 != queue_symbol) {
             expanded.splice(expanded.end(), *queue_symbol);
         }
@@ -1184,15 +1483,15 @@ macromap<ContextT>::expand_macro(ContainerT &expanded,
         return false;
     }
 
-// ensure the parameters to be replaced with special parameter tokens
-macro_definition_type &macro_def = *(*it).second.get();
+    // ensure the parameters to be replaced with special parameter tokens
+    macro_definition_type& macro_def = *(*it).second.get();
 
-    macro_def.replace_parameters();
+    macro_def.replace_parameters(ctx);
 
-// test if this macro is currently available for replacement
+    // test if this macro is currently available for replacement
     if (!macro_def.is_available_for_replacement) {
-    // this macro is marked as non-replaceable
-    // copy the macro name itself
+        // this macro is marked as non-replaceable
+        // copy the macro name itself
         if (0 != queue_symbol) {
             queue_symbol->push_back(token_type(T_NONREPLACABLE_IDENTIFIER,
                 curr_token.get_value(), curr_token.get_position()));
@@ -1206,11 +1505,11 @@ macro_definition_type &macro_def = *(*it).second.get();
         return false;
     }
 
-// try to replace the current identifier as a function-like macro
-ContainerT replacement_list;
+    // try to replace the current identifier as a function-like macro
+    ContainerT replacement_list;
 
     if (T_LEFTPAREN == impl::next_token<IteratorT>::peek(first, last)) {
-    // called as a function-like macro
+        // called as a function-like macro
         impl::skip_to_token(ctx, first, last, T_LEFTPAREN, seen_newline);
 
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0
@@ -1219,32 +1518,45 @@ ContainerT replacement_list;
 #endif
 
         if (macro_def.is_functionlike) {
-        // defined as a function-like macro
+            // defined as a function-like macro
 
-        // collect the arguments
-        std::vector<ContainerT> arguments;
+            // collect the arguments
+            std::vector<ContainerT> arguments;
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
-        typename std::vector<ContainerT>::size_type count_args =
-            collect_arguments (curr_token, arguments, first, last,
-                macro_def.macroparameters.size(), seen_newline);
+            typename std::vector<ContainerT>::size_type count_args =
+                collect_arguments(curr_token, arguments, first, last,
+                    macro_def.macroparameters.size(), seen_newline);
 #else
-        typename std::vector<ContainerT>::size_type count_args =
-            collect_arguments (curr_token, arguments, first, seqend, last,
-                macro_def.macroparameters.size(), seen_newline);
+            typename std::vector<ContainerT>::size_type count_args =
+                collect_arguments(curr_token, arguments, first, seqend, last,
+                    macro_def.macroparameters.size(), seen_newline);
 #endif
 
-        // verify the parameter count
-            if (count_args < macro_def.macroparameters.size() ||
-                arguments.size() < macro_def.macroparameters.size())
+            std::size_t parm_count_required = macro_def.macroparameters.size();
+#if BOOST_WAVE_SUPPORT_CPP2A
+            if (boost::wave::need_cpp2a(ctx.get_language())) {
+                // Starting with C++20, variable arguments may be left out
+                // entirely, so reduce the mandatory argument count by one
+                // if the last parameter is ellipsis:
+                if ((parm_count_required > 0) &&
+                    (T_ELLIPSIS == token_id(macro_def.macroparameters.back()))) {
+                    --parm_count_required;
+                }
+            }
+#endif
+
+            // verify the parameter count
+            if (count_args < parm_count_required ||
+                arguments.size() < parm_count_required)
             {
                 if (count_args != arguments.size()) {
-                // must been at least one empty argument in C++ mode
+                    // must been at least one empty argument in C++ mode
                     BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                         empty_macroarguments, curr_token.get_value().c_str(),
                         main_pos);
                 }
                 else {
-                // too few macro arguments
+                    // too few macro arguments
                     BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                         too_few_macroarguments, curr_token.get_value().c_str(),
                         main_pos);
@@ -1259,7 +1571,7 @@ ContainerT replacement_list;
                 if (!macro_def.has_ellipsis)
 #endif
                 {
-                // too many macro arguments
+                    // too many macro arguments
                     BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                         too_many_macroarguments,
                         curr_token.get_value().c_str(), main_pos);
@@ -1267,7 +1579,7 @@ ContainerT replacement_list;
                 }
             }
 
-        // inject tracing support
+            // inject tracing support
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
             ctx.get_hooks().expanding_function_like_macro(
                 macro_def.macroname, macro_def.macroparameters,
@@ -1290,12 +1602,19 @@ ContainerT replacement_list;
             }
 #endif
 
-        // expand the replacement list of this macro
-            expand_replacement_list(macro_def, arguments, expand_operator_defined,
+            // expand the replacement list of this macro
+            expand_replacement_list(macro_def.macrodefinition.begin(),
+                macro_def.macrodefinition.end(),
+                arguments, expand_operator_defined,
+                expand_operator_has_include,
                 replacement_list);
+
+            if (!expanding_pos)
+                expanding_pos = curr_token.get_expand_position();
+            set_expand_positions(replacement_list, *expanding_pos);
         }
         else {
-        // defined as an object-like macro
+            // defined as an object-like macro
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
             ctx.get_hooks().expanding_object_like_macro(
                 macro_def.macroname, macro_def.macrodefinition, curr_token);
@@ -1309,23 +1628,23 @@ ContainerT replacement_list;
             }
 #endif
 
-        bool found = false;
-        impl::find_concat_operator concat_tag(found);
+            bool found = false;
+            impl::find_concat_operator concat_tag(found);
 
             std::remove_copy_if(macro_def.macrodefinition.begin(),
                 macro_def.macrodefinition.end(),
                 std::inserter(replacement_list, replacement_list.end()),
                 concat_tag);
 
-        // handle concatenation operators
+            // handle concatenation operators
             if (found && !concat_tokensequence(replacement_list))
                 return false;
         }
     }
     else {
-    // called as an object like macro
+        // called as an object like macro
         if ((*it).second->is_functionlike) {
-        // defined as a function-like macro
+            // defined as a function-like macro
             if (0 != queue_symbol) {
                 queue_symbol->push_back(curr_token);
                 expanded.splice(expanded.end(), *queue_symbol);
@@ -1337,7 +1656,7 @@ ContainerT replacement_list;
             return false;           // no further preprocessing required
         }
         else {
-        // defined as an object-like macro (expand it)
+            // defined as an object-like macro (expand it)
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
             ctx.get_hooks().expanding_object_like_macro(
                 macro_def.macroname, macro_def.macrodefinition, curr_token);
@@ -1352,15 +1671,15 @@ ContainerT replacement_list;
             }
 #endif
 
-        bool found = false;
-        impl::find_concat_operator concat_tag(found);
+            bool found = false;
+            impl::find_concat_operator concat_tag(found);
 
             std::remove_copy_if(macro_def.macrodefinition.begin(),
                 macro_def.macrodefinition.end(),
                 std::inserter(replacement_list, replacement_list.end()),
                 concat_tag);
 
-        // handle concatenation operators
+            // handle concatenation operators
             if (found && !concat_tokensequence(replacement_list))
                 return false;
 
@@ -1368,8 +1687,8 @@ ContainerT replacement_list;
         }
     }
 
-// rescan the replacement list
-ContainerT expanded_list;
+    // rescan the replacement list
+    ContainerT expanded_list;
 
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
     ctx.get_hooks().expanded_macro(replacement_list);
@@ -1377,14 +1696,24 @@ ContainerT expanded_list;
     ctx.get_hooks().expanded_macro(ctx.derived(), replacement_list);
 #endif
 
-    rescan_replacement_list(curr_token, macro_def, replacement_list,
-        expanded_list, expand_operator_defined, first, last);
+    rescan_replacement_list(
+        curr_token, macro_def, replacement_list,
+        expanded_list, expand_operator_defined,
+        expand_operator_has_include, first, last);
 
 #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
     ctx.get_hooks().rescanned_macro(expanded_list);
 #else
     ctx.get_hooks().rescanned_macro(ctx.derived(), expanded_list);
 #endif
+
+    if (!expanding_pos)
+        // set the expanding position for rescan
+        expanding_pos = curr_token.get_expand_position();
+
+    // record the location where all the tokens were expanded from
+    set_expand_positions(expanded_list, *expanding_pos);
+
     expanded.splice(expanded.end(), expanded_list);
     return true;        // rescan is required
 }
@@ -1404,41 +1733,77 @@ macromap<ContextT>::expand_predefined_macro(token_type const &curr_token,
 {
     using namespace boost::wave;
 
-string_type const &value = curr_token.get_value();
+    string_type const& value = curr_token.get_value();
 
-    if (value.size() < 8 || '_' != value[0] || '_' != value[1])
-        return false;       // quick check failed
+    if ((value != "__LINE__") && (value != "__FILE__") && (value != "__INCLUDE_LEVEL__"))
+        return false;
+
+    // construct a fake token for the macro's definition point
+    token_type deftoken(T_IDENTIFIER, value, position_type("<built-in>"));
+
+#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
+    ctx.get_hooks().expanding_object_like_macro(
+        deftoken, Container(), curr_token);
+#else
+    if (ctx.get_hooks().expanding_object_like_macro(ctx.derived(),
+            deftoken, ContainerT(), curr_token))
+    {
+        // do not expand this macro, just copy the whole sequence
+        expanded.push_back(curr_token);
+        return false;           // no further preprocessing required
+    }
+#endif
+
+    token_type replacement;
 
     if (value == "__LINE__") {
-    // expand the __LINE__ macro
-        std::string buffer = lexical_cast<std::string>(main_pos.get_line());
+        // expand the __LINE__ macro
+        std::string buffer = lexical_cast<std::string>(curr_token.get_expand_position().get_line());
 
-        expanded.push_back(token_type(T_INTLIT, buffer.c_str(), curr_token.get_position()));
-        return true;
+        replacement = token_type(T_INTLIT, buffer.c_str(), curr_token.get_position());
     }
     else if (value == "__FILE__") {
-    // expand the __FILE__ macro
+        // expand the __FILE__ macro
         namespace fs = boost::filesystem;
 
-    std::string file("\"");
-    fs::path filename(wave::util::create_path(main_pos.get_file().c_str()));
+        std::string file("\"");
+        fs::path filename(
+            wave::util::create_path(curr_token.get_expand_position().get_file().c_str()));
 
         using boost::wave::util::impl::escape_lit;
         file += escape_lit(wave::util::native_file_string(filename)) + "\"";
-        expanded.push_back(token_type(T_STRINGLIT, file.c_str(),
-            curr_token.get_position()));
-        return true;
+        replacement = token_type(T_STRINGLIT, file.c_str(),
+            curr_token.get_position());
     }
     else if (value == "__INCLUDE_LEVEL__") {
-    // expand the __INCLUDE_LEVEL__ macro
-    char buffer[22];    // 21 bytes holds all NUL-terminated unsigned 64-bit numbers
+        // expand the __INCLUDE_LEVEL__ macro
+        char buffer[22]; // 21 bytes holds all NUL-terminated unsigned 64-bit numbers
 
         using namespace std;    // for some systems sprintf is in namespace std
         sprintf(buffer, "%d", (int)ctx.get_iteration_depth());
-        expanded.push_back(token_type(T_INTLIT, buffer, curr_token.get_position()));
-        return true;
+        replacement = token_type(T_INTLIT, buffer, curr_token.get_position());
     }
-    return false;   // no predefined token
+
+    // post-expansion hooks
+    ContainerT replacement_list;
+    replacement_list.push_back(replacement);
+
+#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
+    ctx.get_hooks().expanded_macro(replacement_list);
+#else
+    ctx.get_hooks().expanded_macro(ctx.derived(), replacement_list);
+#endif
+
+    expanded.push_back(replacement);
+
+#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
+    ctx.get_hooks().rescanned_macro(expanded);
+#else
+    ctx.get_hooks().rescanned_macro(ctx.derived(), expanded);
+#endif
+
+    return true;
+
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1468,23 +1833,86 @@ boost::spirit::classic::parse_info<IteratorT> hit =
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_expression,
             msg.c_str(), main_pos);
 
-    // insert a dummy token
+        // insert a dummy token
         pending.push_back(token_type(T_INTLIT, "0", main_pos));
     }
     else {
         impl::assign_iterator<IteratorT>::do_(first, hit.stop);
 
-    // insert a token, which reflects the outcome
+        // insert a token, which reflects the outcome
         pending.push_back(token_type(T_INTLIT,
             is_defined(result.begin(), result.end()) ? "1" : "0",
             main_pos));
     }
 
-on_exit::pop_front<definition_container_type> pop_front_token(pending);
+    on_exit::pop_front<definition_container_type> pop_front_token(pending);
 
     return act_token = pending.front();
 }
 
+#if BOOST_WAVE_SUPPORT_HAS_INCLUDE != 0
+///////////////////////////////////////////////////////////////////////////////
+//
+//  resolve_has_include(): resolve the operator __has_include() and replace
+//                      it with the correct T_INTLIT token
+//
+///////////////////////////////////////////////////////////////////////////////
+template <typename ContextT>
+template <typename IteratorT, typename ContainerT>
+inline typename ContextT::token_type const &
+macromap<ContextT>::resolve_has_include(IteratorT &first,
+    IteratorT const &last, ContainerT &pending)
+{
+    using namespace boost::wave;
+    using namespace boost::wave::grammars;
+
+    ContainerT result;
+    bool is_quoted_filename;
+    bool is_system;
+
+    // to simplify the parser we check for the trailing right paren first
+    // scan from the beginning because unput_queue_iterator is Forward
+    IteratorT end_find_it = first;
+    ++end_find_it;
+    IteratorT rparen_it = first;
+    while (end_find_it != last) {
+        ++end_find_it;
+        ++rparen_it;
+    }
+
+    boost::spirit::classic::parse_info<IteratorT> hit(first);
+    if ((rparen_it != first) && (T_RIGHTPAREN == *rparen_it)) {
+        IteratorT start = first;
+        hit = has_include_grammar_gen<typename ContextT::lexer_type>::
+            parse_operator_has_include(start, rparen_it, result, is_quoted_filename, is_system);
+    }
+
+    if (!hit.hit) {
+        string_type msg ("__has_include(): ");
+        msg = msg + util::impl::as_string<string_type>(first, last);
+        BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_expression,
+            msg.c_str(), main_pos);
+
+        // insert a dummy token
+        pending.push_back(token_type(T_INTLIT, "0", main_pos));
+    }
+    else {
+        impl::assign_iterator<IteratorT>::do_(first, last);
+
+        // insert a token, which reflects the outcome
+        pending.push_back(
+            token_type(T_INTLIT,
+                       has_include(result.begin(), result.end(),
+                                   is_quoted_filename, is_system) ? "1" : "0",
+                       main_pos));
+    }
+
+    on_exit::pop_front<definition_container_type> pop_front_token(pending);
+
+    return act_token = pending.front();
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 //
 //  resolve_operator_pragma(): resolve the operator _Pragma() and dispatch to
@@ -1502,11 +1930,11 @@ inline bool
 macromap<ContextT>::resolve_operator_pragma(IteratorT &first,
     IteratorT const &last, ContainerT &pending, bool& seen_newline)
 {
-// isolate the parameter of the operator _Pragma
+    // isolate the parameter of the operator _Pragma
     token_type pragma_token = *first;
 
     if (!impl::skip_to_token(ctx, first, last, T_LEFTPAREN, seen_newline)) {
-    // illformed operator _Pragma
+        // illformed operator _Pragma
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_expression,
             "operator _Pragma()", pragma_token.get_position());
         return false;
@@ -1523,33 +1951,33 @@ macromap<ContextT>::resolve_operator_pragma(IteratorT &first,
             seen_newline);
 #endif
 
-// verify the parameter count
+    // verify the parameter count
     if (pragma_token.get_position().get_file().empty())
         pragma_token.set_position(act_token.get_position());
 
     if (count_args < 1 || arguments.size() < 1) {
-    // too few macro arguments
+        // too few macro arguments
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_few_macroarguments,
             pragma_token.get_value().c_str(), pragma_token.get_position());
         return false;
     }
     if (count_args > 1 || arguments.size() > 1) {
-    // too many macro arguments
+        // too many macro arguments
         BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_many_macroarguments,
             pragma_token.get_value().c_str(), pragma_token.get_position());
         return false;
     }
 
-// preprocess the pragma token body
+    // preprocess the pragma token body
     typedef typename std::vector<ContainerT>::value_type::iterator
         argument_iterator_type;
 
     ContainerT expanded;
     argument_iterator_type begin_it = arguments[0].begin();
     argument_iterator_type end_it = arguments[0].end();
-    expand_whole_tokensequence(expanded, begin_it, end_it, false);
+    expand_whole_tokensequence(expanded, begin_it, end_it, false, false);
 
-// un-escape the parameter of the operator _Pragma
+    // un-escape the parameter of the operator _Pragma
     typedef typename token_type::string_type string_type;
 
     string_type pragma_cmd;
@@ -1563,29 +1991,29 @@ macromap<ContextT>::resolve_operator_pragma(IteratorT &first,
             continue;
 
         if (T_STRINGLIT != token_id(*it_exp)) {
-        // ill formed operator _Pragma
+            // ill formed operator _Pragma
             BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                 ill_formed_pragma_option, "_Pragma",
                 pragma_token.get_position());
             return false;
         }
         if (pragma_cmd.size() > 0) {
-        // there should be exactly one string literal (string literals are to
-        // be concatenated at translation phase 6, but _Pragma operators are
-        // to be executed at translation phase 4)
+            // there should be exactly one string literal (string literals are to
+            // be concatenated at translation phase 6, but _Pragma operators are
+            // to be executed at translation phase 4)
             BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                 ill_formed_pragma_option, "_Pragma",
                 pragma_token.get_position());
             return false;
         }
 
-    // remove the '\"' and concat all given string literal-values
+        // remove the '\"' and concat all given string literal-values
         string_type token_str = (*it_exp).get_value();
         pragma_cmd += token_str.substr(1, token_str.size() - 2);
     }
     string_type pragma_cmd_unesc = impl::unescape_lit(pragma_cmd);
 
-// tokenize the pragma body
+    // tokenize the pragma body
     typedef typename ContextT::lexer_type lexer_type;
 
     ContainerT pragma;
@@ -1596,15 +2024,15 @@ macromap<ContextT>::resolve_operator_pragma(IteratorT &first,
     for (/**/; it != end; ++it)
         pragma.push_back(*it);
 
-// analyze the preprocessed token sequence and eventually dispatch to the
-// associated action
+    // analyze the preprocessed token sequence and eventually dispatch to the
+    // associated action
     if (interpret_pragma(ctx, pragma_token, pragma.begin(), pragma.end(),
         pending))
     {
         return true;    // successfully recognized a wave specific pragma
     }
 
-// unknown pragma token sequence, push it back and return to the caller
+    // unknown pragma token sequence, push it back and return to the caller
     pending.push_front(token_type(T_SPACE, " ", pragma_token.get_position()));
     pending.push_front(token_type(T_RIGHTPAREN, ")", pragma_token.get_position()));
     pending.push_front(token_type(T_STRINGLIT, string_type("\"") + pragma_cmd + "\"",
@@ -1628,7 +2056,7 @@ inline bool
 macromap<ContextT>::is_valid_concat(string_type new_value,
     position_type const &pos, ContainerT &rescanned)
 {
-// re-tokenize the newly generated string
+    // re-tokenize the newly generated string
     typedef typename ContextT::lexer_type lexer_type;
 
     std::string value_to_test(new_value.c_str());
@@ -1654,10 +2082,29 @@ macromap<ContextT>::is_valid_concat(string_type new_value,
         return true;       // in variadics mode token pasting is well defined
 #endif
 
-// test if the newly generated token sequence contains more than 1 token
+    // test if the newly generated token sequence contains more than 1 token
     return 1 == rescanned.size();
 }
 
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Bulk update expand positions
+//
+///////////////////////////////////////////////////////////////////////////////
+// batch update tokens with a single expand position
+template <typename ContextT>
+template <typename ContainerT>
+void macromap<ContextT>::set_expand_positions(ContainerT &tokens, position_type pos)
+{
+    typename ContainerT::iterator ex_end = tokens.end();
+    for (typename ContainerT::iterator it = tokens.begin();
+         it != ex_end; ++it) {
+        // expand positions are only used for __LINE__, __FILE__, and macro names
+        if (token_id(*it) == T_IDENTIFIER)
+            it->set_expand_position(pos);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 //
 //  Handle all occurrences of the concatenation operator '##' inside the given
@@ -1670,7 +2117,7 @@ inline void report_invalid_concatenation(Context& ctx,
     typename Context::token_type const& next,
     typename Context::position_type const& main_pos)
 {
-typename Context::string_type error_string("\"");
+    typename Context::string_type error_string("\"");
 
     error_string += prev.get_value();
     error_string += "\" and \"";
@@ -1693,22 +2140,22 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
     for (iterator_type it = expanded.begin(); it != end; /**/)
     {
         if (T_POUND_POUND == BASE_TOKEN(token_id(*it))) {
-        iterator_type next = it;
+            iterator_type next = it;
 
             ++next;
             if (prev == end || next == end) {
-            // error, '##' should be in between two tokens
+                // error, '##' should be in between two tokens
                 BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                     ill_formed_operator, "concat ('##')", main_pos);
                 return false;
             }
 
-        // replace prev##next with the concatenated value, skip whitespace
-        // before and after the '##' operator
+            // replace prev##next with the concatenated value, skip whitespace
+            // before and after the '##' operator
             while (IS_CATEGORY(*next, WhiteSpaceTokenType)) {
                 ++next;
                 if (next == end) {
-                // error, '##' should be in between two tokens
+                    // error, '##' should be in between two tokens
                     BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
                         ill_formed_operator, "concat ('##')", main_pos);
                     return false;
@@ -1718,16 +2165,16 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
             if (boost::wave::need_variadics(ctx.get_language())) {
                 if (T_PLACEMARKER == token_id(*next)) {
-                // remove the '##' and the next tokens from the sequence
-                iterator_type first_to_delete = prev;
+                    // remove the '##' and the next tokens from the sequence
+                    iterator_type first_to_delete = prev;
 
                     expanded.erase(++first_to_delete, ++next);
                     it = next;
                     continue;
                 }
                 else if (T_PLACEMARKER == token_id(*prev)) {
-                // remove the '##' and the next tokens from the sequence
-                iterator_type first_to_delete = prev;
+                    // remove the '##' and the next tokens from the sequence
+                    iterator_type first_to_delete = prev;
 
                     *prev = *next;
                     expanded.erase(++first_to_delete, ++next);
@@ -1737,14 +2184,14 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
             }
 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
 
-        // test if the concat operator has to concatenate two unrelated
-        // tokens i.e. the result yields more then one token
-        string_type concat_result;
-        ContainerT rescanned;
+            // test if the concat operator has to concatenate two unrelated
+            // tokens i.e. the result yields more then one token
+            string_type concat_result;
+            ContainerT rescanned;
 
             concat_result = ((*prev).get_value() + (*next).get_value());
 
-        // analyze the validity of the concatenation result
+            // analyze the validity of the concatenation result
             if (!is_valid_concat(concat_result, (*prev).get_position(),
                     rescanned) &&
                 !IS_CATEGORY(*prev, WhiteSpaceTokenType) &&
@@ -1756,20 +2203,20 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
 
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
             if (boost::wave::need_variadics(ctx.get_language())) {
-            // remove the prev, '##' and the next tokens from the sequence
-                expanded.erase(prev, ++next);       // remove not needed tokens
+                // remove the prev, '##' and the next tokens from the sequence
+                expanded.erase(prev, ++next); // remove not needed tokens
 
-            // some stl implementations clear() the container if we erased all
-            // the elements, which orphans all iterators. we re-initialize these
-            // here
+                // some stl implementations clear() the container if we erased all
+                // the elements, which orphans all iterators. we re-initialize these
+                // here
                 if (expanded.empty())
                     end = next = expanded.end();
 
-            // replace the old token (pointed to by *prev) with the re-tokenized
-            // sequence
+                // replace the old token (pointed to by *prev) with the re-tokenized
+                // sequence
                 expanded.splice(next, rescanned);
 
-            // the last token of the inserted sequence is the new previous
+                // the last token of the inserted sequence is the new previous
                 prev = next;
                 if (next != expanded.end())
                     --prev;
@@ -1777,14 +2224,14 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
             else
 #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
             {
-            // we leave the token_id unchanged, but unmark the token as
-            // disabled, if appropriate
+                // we leave the token_id unchanged, but unmark the token as
+                // disabled, if appropriate
                 (*prev).set_value(concat_result);
                 if (T_NONREPLACABLE_IDENTIFIER == token_id(*prev))
                     (*prev).set_token_id(T_IDENTIFIER);
 
-            // remove the '##' and the next tokens from the sequence
-            iterator_type first_to_delete = prev;
+                // remove the '##' and the next tokens from the sequence
+                iterator_type first_to_delete = prev;
 
                 expanded.erase(++first_to_delete, ++next);
             }
@@ -1792,7 +2239,7 @@ macromap<ContextT>::concat_tokensequence(ContainerT &expanded)
             continue;
         }
 
-    // save last non-whitespace token position
+        // save last non-whitespace token position
         if (!IS_CATEGORY(*it, WhiteSpaceTokenType))
             prev = it;
 
@@ -1829,15 +2276,15 @@ inline void
 macromap<ContextT>::init_predefined_macros(char const *fname,
     defined_macros_type *scope, bool at_global_scope)
 {
-// if no scope is given, use the current one
-defined_macros_type *current_scope = scope ? scope : current_macros;
+    // if no scope is given, use the current one
+    defined_macros_type* current_scope = scope ? scope : current_macros;
 
-// first, add the static macros
-position_type pos("<built-in>");
+    // first, add the static macros
+    position_type pos("<built-in>");
 
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
     if (boost::wave::need_c99(ctx.get_language())) {
-    // define C99 specifics
+        // define C99 specifics
         for (int i = 0; 0 != predef.static_data_c99(i).name; ++i) {
             predefined_macros::static_macros const& m = predef.static_data_c99(i);
             predefine_macro(current_scope, m.name,
@@ -1849,7 +2296,7 @@ position_type pos("<built-in>");
     {
 #if BOOST_WAVE_SUPPORT_CPP0X != 0
         if (boost::wave::need_cpp0x(ctx.get_language())) {
-        // define C++11 specifics
+            // define C++11 specifics
             for (int i = 0; 0 != predef.static_data_cpp0x(i).name; ++i) {
                 predefined_macros::static_macros const& m = predef.static_data_cpp0x(i);
                 predefine_macro(current_scope, m.name,
@@ -1857,9 +2304,20 @@ position_type pos("<built-in>");
             }
         }
         else
+#endif
+#if BOOST_WAVE_SUPPORT_CPP2A != 0
+            if (boost::wave::need_cpp2a(ctx.get_language())) {
+            // define C++20 specifics
+            for (int i = 0; 0 != predef.static_data_cpp2a(i).name; ++i) {
+                predefined_macros::static_macros const& m = predef.static_data_cpp2a(i);
+                predefine_macro(current_scope, m.name,
+                    token_type(m.token_id, m.value, pos));
+            }
+        }
+        else
 #endif
         {
-        // define C++ specifics
+            // define C++ specifics
             for (int i = 0; 0 != predef.static_data_cpp(i).name; ++i) {
                 predefined_macros::static_macros const& m = predef.static_data_cpp(i);
                 predefine_macro(current_scope, m.name,
@@ -1867,7 +2325,7 @@ position_type pos("<built-in>");
             }
 
 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
-        // define __WAVE_HAS_VARIADICS__, if appropriate
+            // define __WAVE_HAS_VARIADICS__, if appropriate
             if (boost::wave::need_variadics(ctx.get_language())) {
                 predefine_macro(current_scope, "__WAVE_HAS_VARIADICS__",
                     token_type(T_INTLIT, "1", pos));
@@ -1876,10 +2334,10 @@ position_type pos("<built-in>");
         }
     }
 
-// predefine the __BASE_FILE__ macro which contains the main file name
+    // predefine the __BASE_FILE__ macro which contains the main file name
     namespace fs = boost::filesystem;
     if (string_type(fname) != "<Unknown>") {
-    fs::path filename(create_path(fname));
+        fs::path filename(create_path(fname));
 
         using boost::wave::util::impl::escape_lit;
         predefine_macro(current_scope, "__BASE_FILE__",
@@ -1888,7 +2346,7 @@ position_type pos("<built-in>");
         base_name = fname;
     }
     else if (!base_name.empty()) {
-    fs::path filename(create_path(base_name.c_str()));
+        fs::path filename(create_path(base_name.c_str()));
 
         using boost::wave::util::impl::escape_lit;
         predefine_macro(current_scope, "__BASE_FILE__",
@@ -1896,7 +2354,7 @@ position_type pos("<built-in>");
                 escape_lit(native_file_string(filename)).c_str() + "\"", pos));
     }
 
-// now add the dynamic macros
+    // now add the dynamic macros
     for (int j = 0; 0 != predef.dynamic_data(j).name; ++j) {
         predefined_macros::dynamic_macros const& m = predef.dynamic_data(j);
         predefine_macro(current_scope, m.name,
@@ -1941,4 +2399,4 @@ struct version<boost::wave::util::macromap<ContextT> >
 #include BOOST_ABI_SUFFIX
 #endif
 
-#endif // !defined(CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED)
+#endif // !defined(BOOST_CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED)