簡體   English   中英

使用腳本在編譯時生成代碼

[英]Generating code at compile-time using scripts

理想情況下,我希望能夠將(非常重復的)C/C++ 代碼添加到我的實際代碼中,但在編譯時,代碼將來自 Python 腳本的標准輸出,就像使用宏一樣。

例如,假設我想要擁有依賴於給定類的公共屬性的函數,能夠在我的 C++ 代碼中編寫以下內容將是一件幸事:

generate_boring_functions(FooBarClass,"FooBarClass.cpp")

使用傳統方法可行嗎? 或者我必須使用 Makefile 和臨時源文件進行破解?

謝謝。

您很可能需要稍微調整 Makefile。 編寫一個 (Python) 腳本來讀取每個源文件作為額外的預處理步驟將很容易,用正確的代碼替換generate_boring_functions (或任何其他腳本宏)的實例,可能只需調用generate_boring_functions.py正確的參數,並通過通過標准輸入將源發送到編譯器來繞過對臨時文件的需求。

該死的,現在我想做這樣的事情。

編輯:像這樣的規則,卡在生成文件中,可用於處理額外的構建步驟。 這是未經測試的,只是為了完整性而添加。

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

如果 makefile 對您來說不夠傳統,您可以使用巧妙編寫的宏。

class FooBarClass
{
    DEFINE_BORING_METHODS( FooBarClass )

    /* interesting functions begin here */
}

我經常看到這樣做是為了實現 COM 類的樣板部分。

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

生成文件(或等效文件)是“常規”手段!

我從未使用過這種特殊技術,但聽起來好像您正在尋找類似Ned Batchelder 的 Cog工具的東西。

Python 腳本被嵌入到 C++ 源文件中,這樣當通過 cog 工具運行時,會生成額外的 C++ 代碼供 C++ 編譯器使用。 因此,您的構建過程將包括一個額外的步驟,以便在調用 C++ 編譯器之前讓 cog 生成實際的 C++ 源文件。

您可以嘗試使用 Boost 預處理器庫。 它只是常規預處理器的擴展,但如果您有創造力,您幾乎可以在其中實現任何目標。

你看過PythoidC嗎? 它可用於生成 C 代碼。

我多次遇到這個完全相同的問題。

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

它用於生成將特定文件集中包含的代碼“注冊”到 std::map 的代碼,以處理將用戶編寫的函數添加到庫中,而無需動態重新編譯整個庫或依賴(可能是新手程序員) ) 用戶編寫語法正確的 C++ 代碼來實現類函數。

我用兩種方式解決了(基本上是等價的)

1) 一種純 C++“引導”方法,其中在編譯期間,make 編譯一個生成必要文件的簡單 C++ 程序,然后調用第二個 makefile 編譯臨時文件中生成的實際代碼。

2)一種基於shell的方法,它使用bash來完成同樣的事情(即使用簡單的shell命令遍歷文件並將新文件輸出到一個臨時位置,然后在輸出上調用make)。

這些函數可以分別輸出到一個文件,也可以輸出到一個整體文件以進行第二次編譯。

然后,這些函數可以動態加載(即它們被編譯為共享庫),或者我可以重新編譯所有其余代碼並包含生成的函數。

唯一困難的部分是 (a) 找出唯一注冊函數名稱的方法(例如,使用預處理器__COUNTER__僅在它是單個整體文件時才有效),以及 (b) 找出如何可靠地調用 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__宏函數),在編譯期間甚至從每個文件中提取基本文件名也很困難. 這將依賴於用戶記得將特定的宏調用添加到他們的函數過濾器存根中,這只是添加額外的不必要的工作並要求引入拼寫錯誤等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM