理想情况下,我希望能够将(非常重复的)C / C ++代码添加到我的实际代码中,但是在编译时,代码可以来自,例如,python脚本的stdout,与之相同的方式宏。
例如,假设我想拥有依赖于给定类的公共属性的函数,能够在我的C ++代码中编写以下内容将是一件幸事:
generate_boring_functions(FooBarClass,"FooBarClass.cpp")
使用传统方法可行吗?或者我必须破解Makefile和临时源文件吗?
感谢。
答案 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__
)。而且这将取决于用户记得在其功能过滤器存根中添加特定的宏调用,这只是添加了不必要的工作,并要求引入拼写错误等。