【问题标题】:Generating code at compile-time using scripts在编译时使用脚本生成代码
【发布时间】:2020-05-06 06:13:01
【问题描述】:

理想情况下,我希望能够将(非常重复的)C/C++ 代码添加到我的实际代码中,但在编译时,代码可能来自 python 脚本的标准输出,与使用相同的方式宏。

例如,假设我想要拥有依赖于给定类的公共属性的函数,能够在我的 C++ 代码中编写以下内容将是一件好事:

generate_boring_functions(FooBarClass,"FooBarClass.cpp")

使用传统方法可行吗?还是我必须使用 Makefile 和临时源文件来破解?

谢谢。

【问题讨论】:

  • 我认为 Makefiles 是传统的方法。你想到了什么?
  • 无聊的功能有哪些?它们是否可以通过基于模板的解决方案来解决?
  • 不,它不是模板可解决的,而且我知道 Makefile 是传统的方法,但我的意思只是通过执行愚蠢的简单“g++ foo.cpp -o foo”就可以工作,我猜那是问的太多了;)
  • 这个问题看起来很有趣,但有点不清楚你想要什么。更详细的输入/输出示例会有所帮助。
  • 我加入这个话题可能有点晚了,但您可能会发现以下相关问题:stackoverflow.com/questions/39273219/…

标签: c++ c gcc


【解决方案1】:

您很可能需要稍微调整一下 Makefile。编写一个(Python)脚本来读取每个源文件作为额外的预处理步骤很容易,用正确的代码替换generate_boring_functions(或任何其他脚本宏)的实例,可能只需调用generate_boring_functions.py使用正确的参数,并通过标准输入将源代码发送到编译器来绕过对临时文件的需求。

该死的,现在我想做这样的东西。

编辑:像这样的规则,卡在 makefile 中,可用于处理额外的构建步骤。这是未经测试的,只是为了完整性而添加的。

%.o : %.cpp
    python macros.py $< | g++ -x cpp -c - -o $@

【讨论】:

  • 如何通过标准输入将源代码发送到编译器(不使用临时文件)?
  • 确实,但我可以让 python 解析所有文件,然后以正确的顺序连接所有必要的代码(首先是标题等......),然后只需调用类似“python generate_all_code.py | gcc -x cpp -o out -",就好像我编译了一个大的 c++ 文件...坏主意?
  • @Manux,当您需要调试 python 输出时,直接通过管道连接到 gcc 可能会让您陷入困境。我建议你保存到一个临时文件,然后将临时文件 cat 到 gcc。
【解决方案2】:

如果 makefile 对您来说不够传统,您可以使用编写巧妙的宏。

class FooBarClass
{
    DEFINE_BORING_METHODS( FooBarClass )

    /* interesting functions begin here */
}

我经常看到这样做是为了实现 COM 类的样板部分。

但如果你想要的东西既不是 make 也不是宏,那么我不知道你可能是什么意思。

【讨论】:

    【解决方案3】:

    makefile(或等效文件)是一种“常规”方式!

    【讨论】:

      【解决方案4】:

      我从未使用过这种特殊技术,但听起来您好像在寻找类似Ned Batchelder's Cog 的工具。

      Python 脚本嵌入到 C++ 源文件中,这样当通过 cog 工具运行时,会生成额外的 C++ 代码供 C++ 编译器使用。因此,您的构建过程将包括一个额外的步骤,让 cog 在调用 C++ 编译器之前生成实际的 C++ 源文件。

      【讨论】:

        【解决方案5】:

        您可以尝试使用 Boost 预处理器库。它只是常规预处理器的扩展,但如果您有创意,您几乎可以在其中实现任何目标。

        【讨论】:

          【解决方案6】:

          你看过PythoidC 吗?可用于生成C代码。

          【讨论】:

            【解决方案7】:

            我多次遇到同样的问题。

            我完全按照您描述的方式使用它——(即为一组文件运行 "boringFunction(filename.cpp, "filename.cpp"))。

            它用于生成将特定文件集中包含的代码“注册”到 std::map 的代码,以处理将用户编写的函数添加到库中而无需动态重新编译整个库或依赖 (可能是新手程序员)用户编写语法正确的 C++ 代码,例如实现类函数。

            我已经通过两种方式解决了(基本上是等价的)

            1) 一种纯粹的 C++“引导”方法,在编译过程中,make 编译一个简单的 C++ 程序,生成必要的文件,然后调用第二个 makefile,编译临时文件中生成的实际代码。

            2) 一种基于 shell 的方法,它使用 bash 完成相同的事情(即使用简单的 shell 命令遍历文件并将新文件输出到临时位置,然后在输出上调用 make)。

            函数可以分别输出到一个文件中,也可以输出到一个整体文件中进行第二次编译。

            然后,函数可以动态加载(即它们被编译为共享库),或者我可以重新编译所有其余代码,并包含生成的函数。

            唯一困难的部分是 (a) 找出唯一注册函数名称的方法(例如,使用预处理器 __COUNTER__ 仅在它是单个整体文件时才有效),以及 (b) 弄清楚如何可靠地调用在主makefile运行之前在makefile中生成函数。

            纯 C++ 方法(与例如 bash 相比)的优势在于它可能在默认情况下不具有相同 bash linux shell 的系统上工作(例如 windows 或 macOS),在这种情况下当然会更复杂cmake方法是必要的..

            我已经包含了 makefile 的困难部分以供后代使用:

            第一个调用的makefile是:

            # Dummy to compile filters first
            $(MAKECMDGOALS): SCRIPTCOMPILE
                make -f Makefile2 $(MAKECMDGOALS)
            
            SCRIPTCOMPILE:
                @sh scripts/filter_compiler_single.sh filter_stubs
            
            .PHONY: SCRIPTCOMPILE
            

            scripts/filter_compilr_single.sh 在哪里,例如:

            BUILD_DIR="build/COMPILED_FILTERS";
            rm -r $BUILD_DIR
            mkdir -p $BUILD_DIR
            
            ARGSET="( localmapdict& inputmaps, localmapdict& outputmaps, void*& userdata, scratchmats& scratch, const std::map<std::string,std::string>& params, const uint64_t& curr_time , const std::string& nickname, const std::string& desc )"
            
            compfname=$BUILD_DIR"/COMPILED_FILTERS.cpp"
            
            echo "//// START OF GENERATED FILE (this file will be overwritten!) ////" > $compfname  #REV: first overwrites
            echo "#include <salmap_rv/include/salmap_rv_filter_includes.hpp>" >> $compfname
            echo "using namespace salmap_rv;" >> $compfname
            
            flist=$(find $1 -maxdepth 1 -type f) #REV: add constraint to only find .cpp files?
            for f in $flist;
            do
            
                compfnamebase=$(basename $f) #REV: includes .cpp
                alg=${compfnamebase%.cpp}
                echo $f "  >>  " $compfname
                echo "void ""$alg""$ARGSET""{" >> $compfname
                echo "DEBUGPRINTF(stdout, \"Inside algo funct "$alg"\");" >> $compfname; #REV: debug...
                cat $f >> $compfname
                echo "}""REGISTER_SAL_FILT_FUNC(""$alg"")" >> $compfname
            done
            
            echo "//// END OF GENERATED FILE ////" >> $compfname
            

            第二个makefile Makefile2是正常的编译指令。

            它并不漂亮,我很想找到一种更好的方法来做到这一点,但事实上,即使使用模板或 constexpr(例如一些宏函数接受__FILE__)。这将依赖于用户记住将特定的宏调用添加到他们的函数过滤存根中,这只是增加了额外的不必要的工作并要求引入拼写错误等。

            【讨论】:

              猜你喜欢
              • 2023-03-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-01-27
              • 1970-01-01
              • 2017-06-06
              • 1970-01-01
              相关资源
              最近更新 更多