Boost Wave 生成运行时断言

问题描述 投票:0回答:1

我正在使用 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()));

输入C代码

//#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;
    }
}
c++ boost macros c-preprocessor preprocessor
1个回答
0
投票

您没有定义一些预期的编译器/平台检测预处理器符号。

查看特定的代码位置,最简单的解决方案是提供

RC_INVOKED
的定义。

一般情况下,查看平台预定义的宏,例如https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170。更务实的是,您可以参考常见的识别宏,如下所示How do I check OS with a preprocessordirective?

© www.soinside.com 2019 - 2024. All rights reserved.