我正在使用 Visual Studio 2022 和 Boost.Wave(通过 vcpkg)开发“C”宏扩展器。 我的应用程序基于随 boost 一起提供的 advanced_hooks 示例。 该应用程序工作完美,直到我尝试包含系统标头(例如:
#include <stdio.h>
,此时升压波会抛出下面的断言错误。我不确定如何解决这个问题,因为我需要处理系统包含在我的实际应用中就像这样。
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt\corecrt_stdio_config.h(35): fatal error: encountered #error directive or #pragma wave stop(): Unsupported architecture
来自
corecrt_stdio_config.h
的违规代码如下:
29 #if !defined RC_INVOKED // RC has no target architecture
30 #if defined _M_IX86
31 #define _CRT_INTERNAL_STDIO_SYMBOL_PREFIX "_"
32 #elif defined _M_X64 || defined _M_ARM || defined _M_ARM64
33 #define _CRT_INTERNAL_STDIO_SYMBOL_PREFIX ""
34 #else
35 #error Unsupported architecture <<< THIS IS THE ASSERTION ROOT CAUSE
36 #endif
37 #endif
Boost wave 允许我在应用程序中指定预处理器定义(请参阅下面的代码) - 因此我将 RC_INVOKED=1 添加到预处理器定义中,希望它可以绕过上述断言错误,但它仍然会导致问题。
这是我用来配置Boost Wave的代码:
// This is where we add the project include paths
std::vector<std::string> includePaths = {
fs::current_path().string(), // for COLIRU
};
// These include paths are part of the compiler toolchain, note that these
// include paths allow for either VS2022 preview or Community to be present.
// Also, the apex folder is added here as it should be on the system
// include path list.
std::vector<std::string> systemIncludePaths = {
"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/include",
"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/include",
"C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.41.33923/include",
"C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.41.33923/atlmfc/include",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt",
"C:/Users/johnc/main/tcdu-cdu/include/apex",
};
// Copied from visual studio preprocessor settings.
// Not sure why RC_INVOKED is required.
std::vector<std::string> preprocessorDefines = {
"_UNICODE",
"UNICODE",
"_WIN32_WINNT=0x0601",
"_CRT_SECURE_NO_WARNINGS",
"WIN32_LEAN_AND_MEAN",
"UNIT_TEST=1",
"RC_INVOKED=1",
};
// set various options
for (auto const& next : includePaths)
ctx.add_include_path(next.data());
for (auto const& next : systemIncludePaths)
ctx.add_sysinclude_path(next.data());
for (auto const& next : preprocessorDefines)
ctx.add_macro_definition(next.data());
ctx.set_language(boost::wave::support_cpp2a);
ctx.set_language(enable_preserve_comments(ctx.get_language()));
ctx.set_language(enable_prefer_pp_numbers(ctx.get_language()));
ctx.set_language(enable_single_line(ctx.get_language()));
//#include <stdio.h>
#define TWO (2) // object like macro
#define THREE() (3) // function like macro with 0 args
#define FOUR() (4) // function like macro with 0 args
#define NUMSQUARED(x) ((x)*(x)) // function like macro with 1 arg
#define MIN(a, b) (((a) <= (b/*COMMENTb1*/)) ? (a) : (b/*COMMENTb2*/))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define FUNC_MACRO(x) ((x) + 1)
#define NESTED_MACRO(a, b) (FUNC_MACRO(a) + NUMSQUARED(b) + FUNC_MACRO(FOUR()) + TWO + THREE())
#define DEFINE_FUNC(name) \
int name(int val) { \
int v1 = val + 1; \
return v1; \
}
// stamp out 'macroFooFn' functionDecl.
DEFINE_FUNC(macroFooFn)
// stamp out 'macroBarFn(..)' functionDecl.
DEFINE_FUNC(macroBarFn)
/* Main function comment */
int main() {
// test1
int a = NESTED_MACRO(1, 2);
// test2
int b = MIN(1, TWO); // trailing comment
// test3
int c = MIN(1, 2);
// test4
int d = MIN(1, THREE());
// test5
int f = MIN(1, NUMSQUARED(3));
// test6
int g = MIN(MAX(1, 2), 3);
// test7
int h = (a > 3) ? 4 : 5 /*comment*/;
// test functionDecl macro expansion
int nine = macroFooFn(8);
// test functionDecl macro expansion
int ten = macroBarFn(9);
// return from main.
return 1;
}
location macro call -> expanded macro text
====================================================================================
test2.c:18:1 22 bytes DEFINE_FUNC(macroFooFn) -> 'int macroFooFn(int val) { int v1 = val + 1; return v1; }'
test2.c:21:1 22 bytes DEFINE_FUNC(macroBarFn) -> 'int macroBarFn(int val) { int v1 = val + 1; return v1; }'
test2.c:26:13 17 bytes NESTED_MACRO(1, 2) -> '(FUNC_MACRO(1) + NUMSQUARED( 2) + FUNC_MACRO(FOUR()) + TWO + THREE())'
test2.c:10:34 12 bytes FUNC_MACRO(1) -> '((1) + 1)'
test2.c:10:50 12 bytes NUMSQUARED( 2) -> '(( 2)*( 2))'
test2.c:10:66 17 bytes FUNC_MACRO(FOUR()) -> '(((4)) + 1)'
test2.c:10:87 3 bytes TWO -> '(2)'
test2.c:10:93 6 bytes THREE() -> '(((1) + 1) + (( 2)*( 2)) + (((4)) + 1) + (2) + (3))'
test2.c:29:13 10 bytes MIN(1, TWO) -> '(((1) <= ( (2)/*COMMENTb1*/)) ? (1) : ( (2)/*COMMENTb2*/))'
test2.c:32:13 8 bytes MIN(1, 2) -> '(((1) <= ( 2/*COMMENTb1*/)) ? (1) : ( 2/*COMMENTb2*/))'
test2.c:35:13 14 bytes MIN(1, THREE()) -> '(((1) <= ( (3)/*COMMENTb1*/)) ? (1) : ( (3)/*COMMENTb2*/))'
test2.c:38:13 20 bytes MIN(1, NUMSQUARED(3)) -> '(((1) <= ( ((3)*(3))/*COMMENTb1*/)) ? (1) : ( ((3)*(3))/*COMMENTb2*/))'
test2.c:41:13 16 bytes MIN(MAX(1, 2), 3) -> '((((((1) > ( 2)) ? (1) : ( 2))) <= ( 3/*COMMENTb1*/)) ? ((((1) > ( 2)) ? (1) : ( 2))) : ( 3/*COMMENTb2*/))'
/**
*/
#include <format>
#include <fstream>
#include <iostream>
#include <filesystem>
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
namespace wave = boost::wave;
namespace fs = std::filesystem;
using Position = wave::util::file_position_type;
using PositionRange = std::pair<Position, Position>;
//! Template partial specialization for use with std::formatter.
template<>
struct std::formatter<Position> : std::formatter<std::string_view> {
auto format(
const Position& arg, std::format_context& ctx) const {
return std::formatter<std::string_view>::format(std::format(
"{}:{}:{}"
, fs::path(arg.get_file().c_str()).filename().string()
, arg.get_line()
, arg.get_column()), ctx);
}
};
//! Template partial specialization for use with std::formatter.
template<>
struct std::formatter<PositionRange> : std::formatter<std::string_view> {
auto format(
const PositionRange& arg, std::format_context& ctx) const {
const auto length = arg.second.get_column() - arg.first.get_column();
return std::formatter<std::string_view>::format(std::format(
"{}:{}:{} - len {} bytes"
, fs::path(arg.first.get_file().c_str()).filename().string()
, arg.first.get_line()
, arg.first.get_column()
, length)
, ctx);
}
};
namespace {
//! Spaceship operator allowing us to order in order of descending position.
auto operator<=>(Position const& lhs, Position const& rhs) {
return std::make_tuple(lhs.get_file(), lhs.get_line(), lhs.get_column()) <=>
std::make_tuple(rhs.get_file(), rhs.get_line(), rhs.get_column());
}
// The template wave::cpplexer::lex_token<> is
// the token type to be used by the Wave library.
using token_type = wave::cpplexer::lex_token<>;
// The template wave::cpplexer::lex_iterator<> is the
// iterator type to be used by the Wave library.
using lex_iterator_type = wave::cpplexer::lex_iterator<token_type>;
// This is the resulting context type to use. The first template parameter
// should match the iterator type to be used during construction of the
// corresponding context object (see below).
struct MyHooks;
using context_type = wave::context<std::string::const_iterator, lex_iterator_type,
wave::iteration_context_policies::load_file_to_string, MyHooks>;
struct MyHooks : public wave::context_policies::default_preprocessing_hooks {
MyHooks(const MyHooks& other) = default;
MyHooks(MyHooks&& other) noexcept = default;
MyHooks& operator=(const MyHooks& other) = default;
MyHooks& operator=(MyHooks&& other) noexcept = default;
/**
* Explicit constructor.
*
* @param sourcePath [in] fully qualified path of the C input
* file that we wish to parse.
*/
explicit MyHooks(fs::path sourcePath)
: mSourcePath{ std::move(sourcePath) }
{}
/** Destructor - prints out the macro expansions */
~MyHooks() {
if (!mExpansions.empty()) {
std::cout << std::format("{:<25} {:<25} -> {}\n", "location", "macro call", "expanded macro text");
std::cout << "====================================================================================\n";
for (auto const& [macro, start, end,
macrocall, expanded] : mExpansions) {
auto length = end.get_column() - start.get_column();
std::string posWithLength = std::format("{} {} bytes", start, length);
std::cout << std::format("{:<25} {:<25} -> '{}'\n", posWithLength, macrocall, expanded);
}
}
}
/**
* Callback when an objet like macro has been expanded.
*
* @param <ContextT> [in]
* @param <TokenT> [in] Token type, defaults to
* <code>wave::cpplexer::lex_token<></code>
* @param <ContainerT> [in]
* @param ctx [in]
* @param macro [in]
* @param macrodef [in]
* @param macrocall [in]
*
* @return 'true' to continue processing as normal, 'false' to
* terminate processing.
*/
template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_object_like_macro(
[[maybe_unused]] ContextT& ctx,
TokenT const& macro,
ContainerT const& macrodef,
TokenT const& macrocall) {
mCurrentMacro = macrocall;
const auto macroName = std::string(
macro.get_value().c_str());
const auto file = fs::path(
macrocall.get_position().
get_file().c_str());
// only interested in macros from the current file
if (mSourcePath == file) {
auto const& callPos = macrocall.get_position();
std::string rowCol = std::to_string(
callPos.get_line()) + ':' + std::to_string(
callPos.get_column());
std::string const key = macroName + ":" +
file.string() + ':' + rowCol;
// adjust the ending position
auto endPos = callPos;
endPos.set_column(endPos.get_column() +
mCurrentMacro.get_value().size());
// I don't really know what to do with the macrodef.
// Only interested in final expansion text in rescanned_macro.
//std::string expandedDefinition;
//for (auto const& token : macrodef) {
// expandedDefinition += token.get_value().c_str();
//}
registerExpansion(macroName, callPos, endPos, macroName);
// continue with default processing
return false;
}
// do not process further as the macro was not
// called directly from mSourcePath.
return true;
}
/**
* Callback when a function like macro has been expanded.
*
* @param <ContextT> [in] Context type.
* @param <TokenT> [in] Context type. Defaults to
* wave::cpplexer::lex_token<>
* @param <ContainerT> [in]
* @param <IteratorT> [in] Token iterator. Defaults to
* <code>wave::cpplexer::lex_iterator<></code>
* @param ctx [in]
* @param macrodef [in]
* @param formal_args [in]
* @param definition [in]
* @param macrocall [in]
* @param arguments [in]
* @param seqstart [in] Token iterator -> '('.
* @param seqend [in] Token iterator -> ')'.
*
* @return 'true' to continue processsing as normal, 'false' to
* terminate processing.
*/
template <typename ContextT, typename TokenT, typename ContainerT, typename IteratorT>
bool expanding_function_like_macro(
[[maybe_unused]] ContextT const& ctx,
[[maybe_unused]] TokenT const& macrodef,
[[maybe_unused]] std::vector<TokenT> const& formal_args,
[[maybe_unused]] ContainerT const& definition,
TokenT const& macrocall,
[[maybe_unused]] std::vector<ContainerT> const& arguments,
[[maybe_unused]] IteratorT const& seqstart,
[[maybe_unused]] IteratorT const& seqend) {
mCurrentMacro = macrocall;
const auto macroName = macrocall.get_value().c_str();
const auto file = fs::path(macrocall.
get_position().get_file().c_str());
// only interested in macros originating in mSourcePath
if (mSourcePath == file) {
auto const& callPos = macrocall.get_position();
std::string unexpanded = macroName;
for (auto it = seqstart; it != seqend; ++it) {
unexpanded += it->get_value().c_str();
}
unexpanded += ")";
// register expansion - later when expanded_macro or
// rescanned_macro is called we will fill in the expanded text.
registerExpansion(macroName, callPos,
seqend->get_position(), unexpanded);
// continue with default processing
return false;
}
// do not process further as the macro was not
// called directly from mSourcePath.
return true;
}
/**
*
* @param <ContextT> [in]
* @param <ContainerT> [in]
* @param ctx [in]
* @param tokens [in]
*/
template <typename ContextT, typename ContainerT>
void expanded_macro(
[[maybe_unused]] ContextT const& ctx,
ContainerT const& tokens) {
std::string expanded;
for (auto const& token : tokens) {
expanded += token.get_value().c_str();
}
// clean up the macro expansion text - removing
// multiple lines & extra unnecessary whitespace
std::erase(expanded, '\n');
auto end = std::unique(
expanded.begin(), expanded.end(), [](auto lhs, auto rhs) {
return (lhs == rhs) && ((lhs == ' ') || (lhs == '\t'));
});
expanded.erase(end, expanded.end());
// Search for and update the just registered expansion
// macro (it must be at the end of the vector) or something
// went wrong otherwise.
if (auto it = mExpansions.rbegin(); it != mExpansions.rend()) {
it->expanded = expanded;
} else {
std::cerr << "Error: expanded_macro called without a registered expansion\n";
}
}
/**
* Callback when the macro expansion has fully completed.
*
* @param <ContextT> [in]
* @param <ContainerT> [in]
* @param ctx [in]
* @param tokens [in]
*/
template <typename ContextT, typename ContainerT>
void rescanned_macro([[maybe_unused]] ContextT const& ctx, ContainerT const& tokens) {
auto const& expansionPos = tokens.begin()->get_position();
// only interested in macros originating in mSourcePath
if (mSourcePath == expansionPos.get_file().c_str()) {
std::string expanded;
for (auto iter = tokens.begin(); iter != tokens.end(); ++iter) {
expanded += iter->get_value().c_str();
}
// clean up the macro expansion text - removing
// multiple lines & extra unnecessary whitespace
std::erase(expanded, '\n');
auto end = std::unique(
expanded.begin(), expanded.end(), [](auto lhs, auto rhs) {
return (lhs == rhs) && ((lhs == ' ') || (lhs == '\t'));
});
expanded.erase(end, expanded.end());
// Search for and update the just registered expansion
// macro (it must be at the end of the vector) or something
// went wrong otherwise.
if (auto it = mExpansions.rbegin(); it != mExpansions.rend()) {
it->expanded = expanded;
} else {
std::cerr << "Error: rescanned_macro called without a registered expansion\n";
}
}
}
private:
/**
* Adds an entry to the macro cache recording the starting
* and ending positions in the source file where the macro is
* invoked.
*
* @param macroName [in]
* @param callPos [in]
* @param endPos [in]
* @param macroText [in]
*/
void registerExpansion(
const std::string& macroName,
const Position& callPos,
const Position& endPos,
const std::string& macroText) {
// make sure this is a surrounding expansion - not a nested argument expansion
auto surrounding = std::ranges::find_if(
mExpansions, [&](auto const& exp) {
return (exp.start <= callPos) && (exp.end >= endPos);
});
// we are only interested in the outer expansions
if (surrounding == mExpansions.cend()) {
// expanded text is always empty when we register the macro
// the actual expanded text will be put in later when the
// appropriate hook (rescanned_macro or macro_expanded) is called.
mExpansions.emplace_back(
macroName, callPos, endPos,
macroText, "");
} else {
#if 0
std::cout
<< "Note: " << name << " at "
<< callPos << " nested in "
<< surrounding->name
<< " at " << surrounding->start
<< "\n";
#endif
}
}
// this path represents the path of the input file
// we need this to filter just the macros written in this path
// others are irrelevant from an instrumentation perspective.
fs::path mSourcePath;
// Expansion key consists of macro name and the
// starting location where it is invoked in the sourcePath
using Token = wave::cpplexer::lex_token<>;
struct Expansion {
std::string name;
Position start; // start position of the macro call
Position end; // end position of the macro call
std::string macrocall; // original macro call text
std::string expanded; // expanded macro text corresponding to macrocall
};
Token mCurrentMacro;
std::vector<Expansion> mExpansions;
};
} // namespace
int main(int argc, char* argv[]) {
using namespace wave;
if (argc < 2) {
std::cerr << "Usage: expand_macros [input file]" << '\n';
return -1;
}
// current file position is saved for exception handling
Position current_position;
try {
// Open and read in the specified input file.
std::ifstream instream(argv[1], std::ios::binary);
if (!instream.is_open()) {
std::cerr << "Could not open input file: " << argv[1] << '\n';
return -2;
}
const auto source = std::string(
std::istreambuf_iterator<char>(
instream), {});
// The preprocessor iterator shouldn't be constructed directly. It is
// to be generated through a wave::context<> object. This wave:context<>
// object additionally may be used to initialize and define different
// parameters of the actual preprocessing (not done here).
//
// The preprocessing of the input stream is done on the fly behind the
// scenes during iteration over the context_type::iterator_type stream.
context_type ctx(source.begin(), source.end(), argv[1], MyHooks{argv[1]});
// This is where we add the project include paths
std::vector<std::string> includePaths = {
fs::current_path().string(), // for COLIRU
"C:/Users/johnc/main/tcdu-cdu/include",
"C:/Users/johnc/main/tcdu-cdu/src/cdu/include",
};
// These include paths are part of the compiler toolchain, note that these
// include paths allow for either VS2022 preview or Community to be present.
// Also, the apex folder is added here as it should be on the system
// include path list.
std::vector<std::string> systemIncludePaths = {
"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/include",
"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.41.34120/atlmfc/include",
"C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.41.33923/include",
"C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.41.33923/atlmfc/include",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/winrt",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/cppwinrt",
"C:/Users/johnc/main/tcdu-cdu/include/apex",
};
// Copied from visual studio preprocessor settings.
// Not sure why RC_INVOKED is required.
std::vector<std::string> preprocessorDefines = {
"_UNICODE",
"UNICODE",
"_WIN32_WINNT=0x0601",
"_CRT_SECURE_NO_WARNINGS",
"WIN32_LEAN_AND_MEAN",
"UNIT_TEST=1",
"RC_INVOKED=1",
};
// set various options
for (auto const& next : includePaths)
ctx.add_include_path(next.data());
for (auto const& next : systemIncludePaths)
ctx.add_sysinclude_path(next.data());
for (auto const& next : preprocessorDefines)
ctx.add_macro_definition(next.data());
ctx.set_language(boost::wave::support_cpp2a);
ctx.set_language(enable_preserve_comments(ctx.get_language()));
ctx.set_language(enable_prefer_pp_numbers(ctx.get_language()));
ctx.set_language(enable_single_line(ctx.get_language()));
// Analyze the input file
for (auto first = ctx.begin(), last = ctx.end(); first != last; ++first) {
current_position = first->get_position();
// std::cout << first->get_value();
}
} catch (boost::wave::cpp_exception const& e) {
// some preprocessing error
std::cerr << e.file_name() << "(" << e.line_no() << "): " << e.description() << '\n';
return 2;
} catch (std::exception const& e) {
// Use last recognized token to retrieve the error position
std::cerr << current_position << ": exception caught: " << e.what() << '\n';
return 3;
} catch (...) {
// use last recognized token to retrieve the error position
std::cerr << current_position << "): unexpected exception caught." << '\n';
return 4;
}
}
您没有定义一些预期的编译器/平台检测预处理器符号。
查看特定的代码位置,最简单的解决方案是提供
RC_INVOKED
的定义。
一般情况下,查看平台预定义的宏,例如https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170。更务实的是,您可以参考常见的识别宏,如下所示How do I check OS with a preprocessordirective?