使用脚本在编译时生成代码

时间:2010-06-17 17:22:29

标签: c++ c gcc

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

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

generate_boring_functions(FooBarClass,"FooBarClass.cpp")

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

感谢。

7 个答案:

答案 0 :(得分:4)

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

该死的,现在我想做这样的事情。

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

generate_boring_functions.py

答案 1 :(得分:3)

如果一个makefile对你来说不够常规,你可以用巧妙编写的宏来完成。

class FooBarClass
{
    DEFINE_BORING_METHODS( FooBarClass )

    /* interesting functions begin here */
}

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

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

答案 2 :(得分:2)

makefile(或等效的)是“常规”的意思!

答案 3 :(得分:2)

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

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

你看过PythoidC了吗?它可以用来生成C代码。

答案 6 :(得分:0)

我多次遇到这个完全相同的问题。

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

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

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

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

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

这些函数既可以输出到一个文件,也可以输出到一个整体文件以进行第二次编译。

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

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

pure-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__)。而且这将取决于用户记得在其功能过滤器存根中添加特定的宏调用,这只是添加了不必要的工作,并要求引入拼写错误等。