2
0
mirror of https://github.com/boostorg/wave.git synced 2026-01-19 04:42:16 +00:00

Implement C++20 features (#75)

* Introduce support for C++20 preprocessor features

- add __VA_OPT__ feature to variadic macros
- allow supplying 0 variadic arguments in more cases
- add related unit tests
This commit is contained in:
Jeff Trull
2020-03-06 11:48:50 -08:00
committed by GitHub
parent 785dfd0eee
commit ec0c8c8603
31 changed files with 756 additions and 30 deletions

View File

@@ -1391,7 +1391,7 @@ Wed May 7 22:44:21 2003
- Introduced the wave::language_support enum for convenient switching of the
supported language features throughout the library.
- Fixed a bug, which prevented the definition of the predefined macro
__WAVE_HAS_VARRIADICS__, if --variadics were given on the command line.
__WAVE_HAS_VARIADICS__, if --variadics were given on the command line.
Tue May 6 15:49:45 2003
- Made predefined macros available at every macro scope without qualification.

View File

@@ -381,8 +381,7 @@
<p>This functions allows to specify the language mode, in which
the <tt>Wave</tt> library should work. The possible
language modes are defined by the enumerated type <tt>language_support</tt>:</p>
<pre> <span class="keyword">enum</span> language_support {<br> <span class="comment">// support flags for C++98</span><br> support_normal = 0x01,<br> support_cpp = support_normal,<br><span class="comment"><br> // support flags for C99</span><br> support_option_long_long = 0x02,<br> support_option_variadics = 0x04,<br> support_c99 = support_option_variadics | support_option_long_long | 0x08,<br><span class="comment"><br> // the mask for the main language settings</span><br> support_option_mask = 0xFFB0,<br><br><span class="comment"> // additional fine tuning of the general behavior</span>
support_option_emit_contline = 0x0040,<br> support_option_insert_whitespace = 0x0080,<br> support_option_preserve_comments = 0x0100,<br> support_option_no_character_validation = 0x0200,<br> support_option_convert_trigraphs = 0x0400,<br> support_option_single_line = 0x0800,<br> support_option_prefer_pp_numbers = 0x1000,<br> support_option_emit_line_directives = 0x2000,<br> support_option_include_guard_detection = 0x4000,<br> support_option_emit_pragma_directives = 0x8000<br> };</pre>
<pre> <span class="keyword">enum</span> language_support {<br> <span class="comment">// support flags for C++98</span><br> support_normal = 0x01,<br> support_cpp = support_normal,<br><span class="comment"><br> // support flags for C99</span><br> support_option_long_long = 0x02,<br> support_option_variadics = 0x04,<br> support_c99 = support_option_variadics | support_option_long_long | 0x08,<br><span class="comment"><br> // support flags for C++11</span><br> support_option_no_newline_at_end_of_file = 0x20,<br> support_cpp0x = support_option_variadics | support_option_long_long |<br> support_option_no_newline_at_end_of_file | 0x10,<br> support_cpp11 = support_cpp0x,<br><br><span class="comment"> // support flags for C++20</span><br> support_option_va_opt = 0x10000,<br><br> support_cpp2a = support_option_variadics | support_option_long_long |<br> support_option_no_newline_at_end_of_file | support_option_va_opt | 0x20000,<br> support_cpp20 = support_cpp2a,<br><span class="comment"><br> // the mask for the main language settings</span><br> support_option_mask = 0xFFC0,<br><br><span class="comment"> // additional fine tuning of the general behavior</span><br> support_option_emit_contline = 0x0040,<br> support_option_insert_whitespace = 0x0080,<br> support_option_preserve_comments = 0x0100,<br> support_option_no_character_validation = 0x0200,<br> support_option_convert_trigraphs = 0x0400,<br> support_option_single_line = 0x0800,<br> support_option_prefer_pp_numbers = 0x1000,<br> support_option_emit_line_directives = 0x2000,<br> support_option_include_guard_detection = 0x4000,<br> support_option_emit_pragma_directives = 0x8000<br> };</pre>
<p>When used with <tt>support_option_variadics</tt> the support for variadics, placemarkers and the <tt>operator&nbsp;_Pragma()</tt> is enabled in normal C++ mode. When used with the <tt>support_option_long_long</tt> the support for long long suffixes is enabled in C++ mode. </p>
<p>The <tt>support_c99</tt> switches on the C99
language support, which enables variadics, placemarkers, the <tt>operator&nbsp;_Pragma</tt> and long long suffixes by default. Additionally it disables the C++

View File

@@ -88,6 +88,11 @@
<td class="table_cells"><code>BOOST_WAVE_SUPPORT_CPP0X</code></td>
<td class="table_cells"><p>If defined, then the preprocessor library supports
C++0x keywords and C++0x specific features, such as variadics, placemarkers, extended character and string literals. This implies the definitions of the <code>BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS</code> constant.</p></td>
</tr> <tr>
<td class="table_cells"><code>BOOST_WAVE_SUPPORT_CPP2A</code></td>
<td class="table_cells"><p>If defined, then the preprocessor library supports
C++2A keywords, specifically __VA_OPT__. It implies BOOST_WAVE_SUPPORT_CPP0X.</p></td>
</tr>
</tr> <tr>
<td class="table_cells"><code>BOOST_WAVE_MAX_INCLUDE_LEVEL_DEPTH</code></td>
<td class="table_cells"><p>If defined, it will determine the initial maximal

View File

@@ -133,7 +133,7 @@
<td class="table_cells"><code>__WAVE_HAS_VARIADICS__</code></td>
<td class="table_cells"><p>1 (a decimal constant), this is defined in C++
mode only if variadics and placemarkers are enabled, and it is defined
in the C99 and C++0x modes</p></td>
in the C99, C++0x, and C++2A modes</p></td>
<td class="table_cells"><p>no</p></td>
</tr>
<tr>

View File

@@ -56,7 +56,9 @@
--long_long: enable long long support if C++ mode
--variadics: enable variadics and placemarkers in C++ mode
--c99: enable C99 mode (implies variadics and placemarkers)
--c++11 enable C++11 mode (implies --variadics and --long_long)
--c++11: enable C++11 mode (implies --variadics and --long_long)
--c++20: enable C++20 mode (adds __VA_OPT__ to variadics)
(implies --variadics and --long_long)
-l [ --listincludes ] arg: list included file to a file [arg] or to stdout [-]
-m [ --macronames ] arg: list names of all defined macros to a file [arg] or
to stdout [-]

View File

@@ -115,6 +115,9 @@ public:
ill_formed_operator,
bad_define_statement,
bad_define_statement_va_args,
bad_define_statement_va_opt,
bad_define_statement_va_opt_parens,
bad_define_statement_va_opt_recurse,
too_few_macroarguments,
too_many_macroarguments,
empty_macroarguments,
@@ -204,6 +207,9 @@ public:
case preprocess_exception::unbalanced_if_endif:
case preprocess_exception::bad_define_statement:
case preprocess_exception::bad_define_statement_va_args:
case preprocess_exception::bad_define_statement_va_opt:
case preprocess_exception::bad_define_statement_va_opt_parens:
case preprocess_exception::bad_define_statement_va_opt_recurse:
case preprocess_exception::bad_line_statement:
case preprocess_exception::bad_line_number:
case preprocess_exception::bad_line_filename:
@@ -256,6 +262,11 @@ public:
"ill formed #define directive", // bad_define_statement
"__VA_ARGS__ can only appear in the "
"expansion of a C99 variadic macro", // bad_define_statement_va_args
"__VA_OPT__ can only appear in the "
"expansion of a C++20 variadic macro", // bad_define_statement_va_opt
"__VA_OPT__ must be followed by a left "
"paren in a C++20 variadic macro", // bad_define_statement_va_opt_parens
"__VA_OPT__() may not contain __VA_OPT__", // bad_define_statement_va_opt_recurse
"too few macro arguments", // too_few_macroarguments
"too many macro arguments", // too_many_macroarguments
"empty macro arguments are not supported in pure C++ mode, "
@@ -319,6 +330,9 @@ public:
util::severity_error, // ill_formed_operator
util::severity_error, // bad_define_statement
util::severity_error, // bad_define_statement_va_args
util::severity_error, // bad_define_statement_va_opt
util::severity_error, // bad_define_statement_va_opt_parens
util::severity_error, // bad_define_statement_va_opt_recurse
util::severity_warning, // too_few_macroarguments
util::severity_warning, // too_many_macroarguments
util::severity_warning, // empty_macroarguments

View File

@@ -35,11 +35,20 @@ enum language_support {
support_c99 = support_option_variadics | support_option_long_long | 0x08,
#endif
#if BOOST_WAVE_SUPPORT_CPP0X != 0
// support flags for C++11
support_option_no_newline_at_end_of_file = 0x20,
support_cpp0x = support_option_variadics | support_option_long_long |
support_option_no_newline_at_end_of_file | 0x10,
support_cpp11 = support_cpp0x,
#if BOOST_WAVE_SUPPORT_CPP2A != 0
// support flags for C++20
support_option_va_opt = 0x10000,
support_cpp2a = support_option_variadics | support_option_long_long |
support_option_no_newline_at_end_of_file | support_option_va_opt | 0x20000,
support_cpp20 = support_cpp2a,
#endif
#endif
support_option_mask = 0xFFC0,
@@ -93,6 +102,31 @@ need_cpp0x(language_support language)
#endif
///////////////////////////////////////////////////////////////////////////////
//
// need_cpp2a
//
// Extract if the language to support is C++20
//
///////////////////////////////////////////////////////////////////////////////
#if BOOST_WAVE_SUPPORT_CPP2A != 0
inline bool
need_cpp2a(language_support language)
{
return (language & ~support_option_mask) == support_cpp2a;
}
#else
inline bool
need_cpp2a(language_support language)
{
return false;
}
#endif
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
///////////////////////////////////////////////////////////////////////////////
//
@@ -197,6 +231,9 @@ BOOST_WAVE_OPTION(include_guard_detection) // support_option_include_guard_det
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
BOOST_WAVE_OPTION(variadics) // support_option_variadics
#endif
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
BOOST_WAVE_OPTION(va_opt) // support_option_va_opt
#endif
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
BOOST_WAVE_OPTION(emit_pragma_directives) // support_option_emit_pragma_directives
#endif

View File

@@ -47,6 +47,7 @@ enum token_category {
IdentifierTokenType = 0x08040000,
ParameterTokenType = 0x08840000,
ExtParameterTokenType = 0x088C0000,
OptParameterTokenType = 0x08940000,
KeywordTokenType = 0x10040000,
OperatorTokenType = 0x18040000,
LiteralTokenType = 0x20040000,
@@ -311,7 +312,8 @@ enum token_id {
T_PLACEHOLDER = TOKEN_FROM_ID(T_LAST_TOKEN+2, WhiteSpaceTokenType),
T_PLACEMARKER = TOKEN_FROM_ID(T_LAST_TOKEN+3, InternalTokenType),
T_PARAMETERBASE = TOKEN_FROM_ID(T_LAST_TOKEN+4, ParameterTokenType),
T_EXTPARAMETERBASE = TOKEN_FROM_ID(T_LAST_TOKEN+4, ExtParameterTokenType)
T_EXTPARAMETERBASE = TOKEN_FROM_ID(T_LAST_TOKEN+4, ExtParameterTokenType),
T_OPTPARAMETERBASE = TOKEN_FROM_ID(T_LAST_TOKEN+4, OptParameterTokenType)
};
///////////////////////////////////////////////////////////////////////////////

View File

@@ -1789,6 +1789,17 @@ position_type pos(act_token.get_position());
macroname.get_value().c_str(), (*pit).get_position());
return;
}
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
// can't use __VA_OPT__ either
if (boost::wave::need_va_opt(ctx.get_language()) &&
("__VA_OPT__" == (*pit).get_value())) {
BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
bad_define_statement_va_opt,
macroname.get_value().c_str(), (*pit).get_position());
return;
}
#endif
}
// if there wasn't an ellipsis, then there shouldn't be a __VA_ARGS__
@@ -1797,6 +1808,9 @@ position_type pos(act_token.get_position());
typedef typename token_sequence_type::iterator definition_iterator_t;
bool seen_va_args = false;
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
bool seen_va_opt = false;
#endif
definition_iterator_t pend = macrodefinition.end();
for (definition_iterator_t dit = macrodefinition.begin();
dit != pend; ++dit)
@@ -1806,6 +1820,13 @@ position_type pos(act_token.get_position());
{
seen_va_args = true;
}
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
if (T_IDENTIFIER == token_id(*dit) &&
"__VA_OPT__" == (*dit).get_value())
{
seen_va_opt = true;
}
#endif
}
if (seen_va_args) {
// must not have seen __VA_ARGS__ placeholder
@@ -1814,6 +1835,14 @@ position_type pos(act_token.get_position());
macroname.get_value().c_str(), act_token.get_position());
return;
}
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
if (seen_va_opt) {
BOOST_WAVE_THROW_CTX(ctx, preprocess_exception,
bad_define_statement_va_opt,
macroname.get_value().c_str(), act_token.get_position());
return;
}
#endif
}
}
else

View File

@@ -202,7 +202,8 @@ protected:
// 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);
@@ -298,6 +299,15 @@ macromap<ContextT>::add_macro(token_type const &name, bool has_parameters,
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 (AltExtTokenType == (token_id(name) & ExtTokenOnlyMask)) {
// exclude special operator names
BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception,
@@ -349,6 +359,51 @@ typename defined_macros_type::iterator it = current_scope->find(name.get_value()
}
}
#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(
@@ -927,7 +982,8 @@ 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,
ContainerT &expanded)
{
@@ -941,9 +997,7 @@ 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));
@@ -973,6 +1027,9 @@ bool adjacent_stringize = false;
typename ContainerT::size_type i;
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
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,12 +1037,21 @@ 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
@@ -1004,13 +1070,56 @@ bool adjacent_stringize = false;
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, has_expanded_args);
}
// locate the __VA_OPT__ arguments
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
if ((i == arguments.size()) || // no variadic argument
impl::is_whitespace_only(arguments[i])) { // no visible tokens
// no args; insert placemarker if one is not already present
expanded.push_back(
typename ContainerT::value_type(T_PLACEMARKER, "\xA7", pos));
} else if (!impl::is_blank_only(arguments[i])) {
++cstart; ++cstart;
// [cstart, cit) is now the args to va_opt
// recursively process them
expand_replacement_list(cstart, cit, arguments,
expand_operator_defined, expanded);
}
// continue from rparen
}
else
#endif
#endif
{
BOOST_ASSERT(i < arguments.size());
// ensure argument i to be expanded
expand_argument(i, arguments, expanded_args,
expand_operator_defined, has_expanded_args);
// replace argument
BOOST_ASSERT(i < expanded_args.size());
ContainerT const &arg = expanded_args[i];
std::copy(arg.begin(), arg.end(),
@@ -1046,12 +1155,25 @@ bool adjacent_stringize = false;
// 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
@@ -1187,7 +1309,7 @@ macromap<ContextT>::expand_macro(ContainerT &expanded,
// 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
if (!macro_def.is_available_for_replacement) {
@@ -1233,9 +1355,22 @@ ContainerT replacement_list;
macro_def.macroparameters.size(), seen_newline);
#endif
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 < macro_def.macroparameters.size() ||
arguments.size() < macro_def.macroparameters.size())
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
@@ -1291,7 +1426,9 @@ ContainerT replacement_list;
#endif
// expand the replacement list of this macro
expand_replacement_list(macro_def, arguments, expand_operator_defined,
expand_replacement_list(macro_def.macrodefinition.begin(),
macro_def.macrodefinition.end(),
arguments, expand_operator_defined,
replacement_list);
}
else {
@@ -1857,6 +1994,17 @@ 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

View File

@@ -240,6 +240,23 @@ namespace util {
}
#endif
#if BOOST_WAVE_SUPPORT_CPP2A != 0
// C++20 mode
static_macros const& static_data_cpp2a(std::size_t i) const
{
static static_macros data[] = {
{ "__STDC__", T_INTLIT, "1" },
{ "__cplusplus", T_INTLIT, "202002L" },
{ "__STDC_VERSION__", T_INTLIT, "199901L" },
{ "__STDC_HOSTED__", T_INTLIT, "0" },
{ "__WAVE_HAS_VARIADICS__", T_INTLIT, "1" },
{ 0, T_EOF, 0 }
};
BOOST_ASSERT(i < sizeof(data)/sizeof(data[0]));
return data[i];
}
#endif
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
// C99 mode
static_macros const& static_data_c99(std::size_t i) const

View File

@@ -343,6 +343,27 @@ is_whitespace_only (ContainerT const &argument)
return true;
}
///////////////////////////////////////////////////////////////////////////////
//
// Tests whether the given token sequence consists only of whitespace
// and placemarkers
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline bool
is_blank_only (ContainerT const &argument)
{
typename ContainerT::const_iterator end = argument.end();
for (typename ContainerT::const_iterator it = argument.begin();
it != end; ++it)
{
if (!IS_CATEGORY(*it, WhiteSpaceTokenType) &&
(T_PLACEMARKER != token_id(*it)))
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
//
// Remove all placeholder tokens from the given token sequence

View File

@@ -73,7 +73,8 @@ struct macro_definition {
// with special parameter tokens to simplify later macro replacement.
// Additionally mark all occurrences of the macro name itself throughout
// the macro definition
void replace_parameters()
template<typename ContextT>
void replace_parameters(ContextT const & ctx)
{
using namespace boost::wave;
@@ -100,13 +101,24 @@ struct macro_definition {
break;
}
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
else if (T_ELLIPSIS == token_id(*cit) &&
else if (need_variadics(ctx.get_language()) &&
T_ELLIPSIS == token_id(*cit) &&
"__VA_ARGS__" == (*it).get_value())
{
// __VA_ARGS__ requires special handling
(*it).set_token_id(token_id(T_EXTPARAMETERBASE+i));
break;
}
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
else if (need_va_opt(ctx.get_language()) &&
T_ELLIPSIS == token_id(*cit) &&
"__VA_OPT__" == (*it).get_value())
{
// __VA_OPT__ also requires related special handling
(*it).set_token_id(token_id(T_OPTPARAMETERBASE+i));
break;
}
#endif
#endif
}
}

View File

@@ -252,6 +252,48 @@ namespace impl {
expanded.push_back(comma);
}
}
#if BOOST_WAVE_SUPPORT_VA_OPT != 0
///////////////////////////////////////////////////////////////////////////
//
// Finds the token range inside __VA_OPT__.
// Updates mdit to the position of the final rparen.
// If the parenthesis do not match up, or there are none, returns false
// and leaves mdit unchanged.
//
template <typename MDefIterT>
bool find_va_opt_args (
MDefIterT & mdit, // VA_OPT
MDefIterT mdend)
{
if ((std::distance(mdit, mdend) < 3) ||
(T_LEFTPAREN != next_token<MDefIterT>::peek(mdit, mdend))) {
return false;
}
MDefIterT mdstart_it = mdit;
++mdit; // skip to lparen
std::size_t scope = 0;
// search for final rparen, leaving iterator there
for (; (mdit != mdend) && !((scope == 1) && (T_RIGHTPAREN == token_id(*mdit)));
++mdit) {
// count balanced parens
if (T_RIGHTPAREN == token_id(*mdit)) {
scope--;
} else if (T_LEFTPAREN == token_id(*mdit)) {
scope++;
}
}
if ((mdit == mdend) && ((scope != 1) || (T_RIGHTPAREN != token_id(*mdit)))) {
// arrived at end without matching rparen
mdit = mdstart_it;
return false;
}
return true;
}
#endif
#endif
// Skip all whitespace characters and queue the skipped characters into the

View File

@@ -40,6 +40,14 @@
#define BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS 1
#endif
///////////////////////////////////////////////////////////////////////////////
// Decide whether to support the C++20 __VA_OPT__ variadics feature
//
//
#if !defined(BOOST_WAVE_SUPPORT_VA_OPT) && BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS
#define BOOST_WAVE_SUPPORT_VA_OPT 1
#endif
///////////////////////////////////////////////////////////////////////////////
// Decide, whether to implement a #warning directive as an extension to the
// C++ Standard (same as #error, but emits a warning, not an error)
@@ -94,6 +102,25 @@
#define BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS 1
#endif
///////////////////////////////////////////////////////////////////////////////
// Decide whether to support C++20
//
#if !defined(BOOST_WAVE_SUPPORT_CPP2A)
# define BOOST_WAVE_SUPPORT_CPP2A 1
# undef BOOST_WAVE_SUPPORT_CPP0X
# define BOOST_WAVE_SUPPORT_CPP0X 1
# undef BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS
# define BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS 1
# if !defined(BOOST_WAVE_SUPPORT_VA_OPT)
# undef BOOST_WAVE_SUPPORT_VA_OPT
# define BOOST_WAVE_SUPPORT_VA_OPT 1
# endif
#elif BOOST_WAVE_SUPPORT_CPP2A == 0
# undef BOOST_WAVE_SUPPORT_VA_OPT
# define BOOST_WAVE_SUPPORT_VA_OPT 0
#endif
///////////////////////////////////////////////////////////////////////////////
// Undefine the following, to enable some MS specific language extensions:
// __int8, __int16, __int32, __int64, __based, __declspec, __cdecl,

View File

@@ -69,9 +69,10 @@ test-suite wave
:
# arguments
$(TESTWAVE_ARGUMENTS)
--config-file $(TESTWAVE_DIR)/$(TESTWAVE_FILES)
--config-file
:
# input files
$(TESTWAVE_DIR)/$(TESTWAVE_FILES)
:
# requirements
<threading>multi

View File

@@ -0,0 +1,72 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// __VA_OPT__ examples from the proposal doc P00306
// Those examples have a few weird typos. I've tried to match the
// behavior of gcc in the expected results - JET
#define LOG(msg, ...) printf(msg __VA_OPT__(,) __VA_ARGS__)
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
#define LOG2(...) \
printf("at line=%d" __VA_OPT__(": "), __LINE__); \
__VA_OPT__(printf(__VA_ARGS__);) \
printf("\n")
//R #line 28 "t_8_001.cpp"
//R printf("hello world\n" );
//R printf("hello world\n" );
//R printf("hello %d\n" , n);
LOG("hello world\n");
LOG("hello world\n", );
LOG("hello %d\n", n);
//R #line 35 "t_8_001.cpp"
//R S foo;
//R S bar = { 1, 2, 3 };
SDEF(foo);
SDEF(bar, 1, 2, 3);
//R #line 43 "t_8_001.cpp"
//R printf("at line=%d" , 43); printf("\n");
// P00306 example suggests the adjacent string literals are concatenated in
// this test case but that's the *compiler's* job:
//R printf("at line=%d" ": ", 44); printf("All well in zone %d", n); printf("\n");
LOG2();
LOG2("All well in zone %d", n);
//H 10: t_8_001.cpp(17): #define
//H 08: t_8_001.cpp(17): LOG(msg, ...)=printf(msg __VA_OPT__(,) __VA_ARGS__)
//H 10: t_8_001.cpp(18): #define
//H 08: t_8_001.cpp(18): SDEF(sname, ...)=S sname __VA_OPT__(= { __VA_ARGS__ })
//H 10: t_8_001.cpp(19): #define
//H 08: t_8_001.cpp(19): LOG2(...)=printf("at line=%d" __VA_OPT__(": "), __LINE__); __VA_OPT__(printf(__VA_ARGS__);) printf("\n")
//H 00: t_8_001.cpp(28): LOG("hello world\n"), [t_8_001.cpp(17): LOG(msg, ...)=printf(msg __VA_OPT__(,) __VA_ARGS__)]
//H 02: printf("hello world\n" § )
//H 03: printf("hello world\n" )
//H 00: t_8_001.cpp(29): LOG("hello world\n", §), [t_8_001.cpp(17): LOG(msg, ...)=printf(msg __VA_OPT__(,) __VA_ARGS__)]
//H 02: printf("hello world\n" )
//H 03: printf("hello world\n" )
//H 00: t_8_001.cpp(30): LOG("hello %d\n", n), [t_8_001.cpp(17): LOG(msg, ...)=printf(msg __VA_OPT__(,) __VA_ARGS__)]
//H 02: printf("hello %d\n" , n)
//H 03: printf("hello %d\n" , n)
//H 00: t_8_001.cpp(35): SDEF(foo), [t_8_001.cpp(18): SDEF(sname, ...)=S sname __VA_OPT__(= { __VA_ARGS__ })]
//H 02: S foo §
//H 03: S foo
//H 00: t_8_001.cpp(36): SDEF(bar, 1, 2, 3), [t_8_001.cpp(18): SDEF(sname, ...)=S sname __VA_OPT__(= { __VA_ARGS__ })]
//H 02: S bar = { 1, 2, 3 }
//H 03: S bar = { 1, 2, 3 }
//H 00: t_8_001.cpp(43): LOG2(§), [t_8_001.cpp(19): LOG2(...)=printf("at line=%d" __VA_OPT__(": "), __LINE__); __VA_OPT__(printf(__VA_ARGS__);) printf("\n")]
//H 02: printf("at line=%d" , __LINE__); printf("\n")
//H 03: printf("at line=%d" , 43); printf("\n")
//H 00: t_8_001.cpp(44): LOG2("All well in zone %d", n), [t_8_001.cpp(19): LOG2(...)=printf("at line=%d" __VA_OPT__(": "), __LINE__); __VA_OPT__(printf(__VA_ARGS__);) printf("\n")]
//H 02: printf("at line=%d" ": ", __LINE__); printf("All well in zone %d", n); printf("\n")
//H 03: printf("at line=%d" ": ", 44); printf("All well in zone %d", n); printf("\n")

View File

@@ -0,0 +1,24 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// tests for changes to variadics handling
// specifically, that arg list after named args can be empty
#define ok_to_be_empty(BASE, ...) __VA_ARGS__##BASE##__VA_ARGS__
//R #line 20 "t_8_002.cpp"
//R notemptyfillernotempty
ok_to_be_empty(filler, notempty)
//R #line 24 "t_8_002.cpp"
//R filler
ok_to_be_empty(filler)

View File

@@ -0,0 +1,22 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++11
//O -Werror
// test that arg list after named args CANNOT be empty (in c++11 mode)
#define ok_to_be_empty(BASE, ...) __VA_ARGS__##BASE##__VA_ARGS__
//R #line 19 "t_8_003.cpp"
//R notemptyfillernotempty
ok_to_be_empty(filler, notempty)
//E t_8_003.cpp(22): warning: too few macro arguments: ok_to_be_empty
ok_to_be_empty(filler)

View File

@@ -0,0 +1,24 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test using __VA_ARGS__ inside __VA_OPT__
// JET: Wave seems to inject a space after some lparens
#define sizedvec(TYPE, NAME, SIZE, ...) std::vector<TYPE> NAME(SIZE __VA_OPT__(, __VA_ARGS__))
//R #line 20 "t_8_004.cpp"
//R std::vector<int> foo( 12 );
sizedvec(int, foo, 12);
//R #line 24 "t_8_004.cpp"
//R std::vector<double> bar( 3 , 42.0);
sizedvec(double, bar, 3, 42.0);

View File

@@ -0,0 +1,16 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test that __VA_OPT__ cannot be used as a object-like macro
//E t_8_005.cpp(16): error: __VA_OPT__ can only appear in the expansion of a C++20 variadic macro: __VA_OPT__
#define __VA_OPT__ something somethingelse

View File

@@ -0,0 +1,16 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test that __VA_OPT__ cannot be used as a function-like macro
//E t_8_006.cpp(16): error: __VA_OPT__ can only appear in the expansion of a C++20 variadic macro: __VA_OPT__
#define __VA_OPT__(FOO, BAR) FOO BAR

View File

@@ -0,0 +1,16 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test that __VA_OPT__ cannot be used in a macro without variable args
//E t_8_007.cpp(16): error: __VA_OPT__ can only appear in the expansion of a C++20 variadic macro: FOO
#define FOO(BAR) BAR __VA_OPT__(,) BAR

View File

@@ -0,0 +1,62 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
// Tests predefined macros
//O --c++20
//O -Werror
//R #line 16 "t_8_008.cpp"
__STDC__ //R 1
__STDC_VERSION__ //R 199901L
__cplusplus //R 202002L
__STDC_HOSTED__ //R 0
__LINE__ //R 20
__FILE__ //R "$P"
__BASE_FILE__ //R "$F"
__WAVE_HAS_VARIADICS__ //R 1
__INCLUDE_LEVEL__ //R 0
//R #line 53 "test.cpp"
#line 53 "test.cpp"
__LINE__ //R 53
__FILE__ //R "test.cpp"
__BASE_FILE__ //R "$F"
//R #line 59 "test.cpp"
__LINE__ //R 59
__FILE__ //R "test.cpp"
__BASE_FILE__ //R "$F"
//H 01: <built-in>(1): __STDC__
//H 02: 1
//H 03: 1
//H 01: <built-in>(1): __STDC_VERSION__
//H 02: 199901L
//H 03: 199901L
//H 01: <built-in>(1): __cplusplus
//H 02: 202002L
//H 03: 202002L
//H 01: <built-in>(1): __STDC_HOSTED__
//H 02: 0
//H 03: 0
//H 01: <built-in>(1): __BASE_FILE__
//H 02: "$F"
//H 03: "$F"
//H 01: <built-in>(1): __WAVE_HAS_VARIADICS__
//H 02: 1
//H 03: 1
//H 10: t_8_008.cpp(26): #line
//H 17: 53 "test.cpp" (53, "test.cpp")
//H 01: <built-in>(1): __BASE_FILE__
//H 02: "$F"
//H 03: "$F"
//H 01: <built-in>(1): __BASE_FILE__
//H 02: "$F"
//H 03: "$F"

View File

@@ -0,0 +1,17 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test that __VA_OPT__ cannot be used in a macro definition without parens
// gcc detects this at definition time
//E t_8_009.cpp(17): error: __VA_OPT__ must be followed by a left paren in a C++20 variadic macro: FOO
#define FOO(...) BAR __VA_OPT__ BAR

View File

@@ -0,0 +1,18 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++20
//O -Werror
// test that __VA_OPT__ cannot be used inside itself
// gcc detects this at definition time
//E t_8_010.cpp(17): error: __VA_OPT__() may not contain __VA_OPT__: FOO
#define FOO(...) BAR __VA_OPT__(__VA_OPT__(,) BAZ) BAR

View File

@@ -0,0 +1,19 @@
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2020 Jeff Trull. Distributed under the Boost
Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
//O --c++11
//O -Werror
// test that __VA_OPT__ is ignored when not in c++20 mode
#define FOO(BAR, ...) BAR __VA_OPT__(,) __VA_ARGS__
//R #line 19 "t_8_011.cpp"
//R something __VA_OPT__(,) other
FOO(something, other)

View File

@@ -218,6 +218,21 @@ t_7_004.cpp
t_7_005.cpp
t_7_006.cpp
#
# t_8: C++2a testing
#
t_8_001.cpp
t_8_002.cpp
t_8_003.cpp
t_8_004.cpp
t_8_005.cpp
t_8_006.cpp
t_8_007.cpp
t_8_008.cpp
t_8_009.cpp
t_8_010.cpp
t_8_011.cpp
#
# t_9: General preprocessing problems
#

View File

@@ -418,6 +418,9 @@ testwave_app::testwave_app(po::variables_map const& vm)
("skipped_token_hooks", "record skipped_token hook calls")
#if BOOST_WAVE_SUPPORT_CPP0X != 0
("c++11", "enable C++11 mode (implies --variadics and --long_long)")
#endif
#if BOOST_WAVE_SUPPORT_CPP2A != 0
("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __VA_OPT__)")
#endif
("warning,W", po::value<std::vector<std::string> >()->composing(),
"Warning settings.")
@@ -971,12 +974,9 @@ testwave_app::initialise_options(Context& ctx, po::variables_map const& vm,
#if BOOST_WAVE_SUPPORT_CPP0X
if (vm.count("c++11")) {
if (9 == debuglevel) {
std::cerr << "initialise_options: option: c++11" << std::endl;
}
ctx.set_language(
boost::wave::language_support(
boost::wave::support_cpp0x
boost::wave::support_cpp0x
| boost::wave::support_option_convert_trigraphs
| boost::wave::support_option_long_long
| boost::wave::support_option_emit_line_directives
@@ -988,6 +988,34 @@ testwave_app::initialise_options(Context& ctx, po::variables_map const& vm,
#endif
| boost::wave::support_option_insert_whitespace
));
} else {
if (9 == debuglevel) {
std::cerr << "initialise_options: option: c++11" << std::endl;
}
}
#endif
#if BOOST_WAVE_SUPPORT_CPP2A
if (vm.count("c++20")) {
ctx.set_language(
boost::wave::language_support(
boost::wave::support_cpp2a
| boost::wave::support_option_va_opt
| boost::wave::support_option_convert_trigraphs
| boost::wave::support_option_long_long
| boost::wave::support_option_emit_line_directives
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
| boost::wave::support_option_include_guard_detection
#endif
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
| boost::wave::support_option_emit_pragma_directives
#endif
| boost::wave::support_option_insert_whitespace
));
if (9 == debuglevel) {
std::cerr << "initialise_options: option: c++20" << std::endl;
}
}
#endif

View File

@@ -897,6 +897,26 @@ const bool treat_warnings_as_error = vm.count("warning") &&
}
#endif // BOOST_WAVE_SUPPORT_CPP0X != 0
#if BOOST_WAVE_SUPPORT_CPP2A != 0
if (vm.count("c++20")) {
ctx.set_language(
boost::wave::language_support(
boost::wave::support_cpp2a
| boost::wave::support_option_va_opt
| boost::wave::support_option_convert_trigraphs
| boost::wave::support_option_long_long
| boost::wave::support_option_emit_line_directives
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
| boost::wave::support_option_include_guard_detection
#endif
#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0
| boost::wave::support_option_emit_pragma_directives
#endif
| boost::wave::support_option_insert_whitespace
));
}
#endif // BOOST_WAVE_SUPPORT_CPP2A != 0
// enable long long support, if appropriate
if (vm.count("long_long")) {
ctx.set_language(
@@ -1329,6 +1349,9 @@ main (int argc, char *argv[])
#endif
#if BOOST_WAVE_SUPPORT_CPP0X != 0
("c++11", "enable C++11 mode (implies --variadics and --long_long)")
#endif
#if BOOST_WAVE_SUPPORT_CPP2A != 0
("c++20", "enable C++20 mode (implies --variadics and --long_long, adds __VA_OPT__)")
#endif
("listincludes,l", po::value<std::string>(),
"list names of included files to a file [arg] or to stdout [-]")

View File

@@ -1207,9 +1207,7 @@ protected:
using namespace boost::wave;
typedef typename ContextT::token_type token_type;
typedef typename token_type::string_type string_type;
typedef typename ContainerT::const_iterator const_iterator;
typedef typename ContainerT::iterator iterator;
token_type* current = 0;