2

I am developing a 'C' macro-expander with Visual Studio 2022 & Boost.Wave (through vcpkg). I based the application on the advanced_hooks sample that ships with boost. The application works perfectly until I attempt to include a system header (for ex: #include <stdio.h>, at which point boost wave throws the assertion error below. I'm not sure how I can work around this as I will need to handle system includes like this in my real application.

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

The offending code from corecrt_stdio_config.h is as follows:

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 allows me to specify preprocessor defines in the application (see my code below) - so I added RC_INVOKED=1 to the preprocessor defines in the hopes that it might bybass the above assertion error, however it still causes problems.

Here is the code I use to configure 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()));

Input C code

//#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;
}

Output

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*/))'

The full application:

/**
*/
#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;
    }
}
2
  • 1
    RC probably stands for Resource Compiler, which would explain why the architecture would be irrelevant. Commented Aug 21, 2024 at 20:28
  • Thanks - I saw that, also if I defined that as part of the visual studio settings - the compiler goes nuts, I had to add it in the std::vector<std::string> preprocessorDefines = { - however it got ingored somewhere under the covers Commented Aug 21, 2024 at 21:07

1 Answer 1

0

You don't define some of the expected compiler/platform detection preprocessor symbols.

Looking at the particular code location the simplest solution would be to provide a definition for RC_INVOKED.

In the general case, look at the platform's predefined macros, e.g. https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170. More pragmatically, you could refer to common identifying macros like here How do I check OS with a preprocessor directive?

Sign up to request clarification or add additional context in comments.

10 Comments

I did add a definition for RC_INVOKED as you can see in std::vector<std::string> preprocessorDefines = {.... I had a bit of success since posting my question by overloading the hook bool found_error_directive returning true instead of the default false to continue and I got my expected output, however I still get all the other include garbage in my output stream associated with <stdio.h> in this case.
Oh. In that case I don't get what the question was. It was the only problem I could see in the question. I reproduced the program locally and it completes fine: coliru.stacked-crooked.com/a/6acb45630998987e
thanks, btw how do I edit the test file in coliru? Its a great trick you use to pass a separate file for the argument. The thing is that you need to remove the comment before the first line //#include <stdio.h> in order to reproduce the problem, also I am using Visual Studio and not GCC
I was able to make the above coliru link work - due to compilation timeouts - not sure if you had to do something special to compile the above as I always get "execution expired" in the output. If for some reason you can get it to work again, any chance you could modify the test file and remove the comments before the #include <stdio.h> - which was the problem I was trying to work around. Thanks
Seems to run into more Wave limitations, e.g. coliru.stacked-crooked.com/a/15380432a531a4a9 - basically, there's no easy way to "define" some things (like __GNUC_PREREQ(x, y)=0 for this problem). Now, obviously there should be ways to intercept programmatically, but really, should you even care about the contents of system headers? It seems to me you might just skip those entirely.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.