主頁 > 軟體工程 > 任何可以為pybind11匯出結構的所有成員變數的C 宏

任何可以為pybind11匯出結構的所有成員變數的C 宏

2021-12-20 05:52:16 軟體工程

我有一個簡單的結構,如:

struct Config {
  bool option1;
  bool option2;
  int arg1;
};

使用 pybind11,我必須匯出成員變數,例如:

py::class_<Config>(m, "Config")
    .def_readwrite("option1", &Config::option1)
    .def_readwrite("option2", &Config::option2)
    .def_readwrite("arg1", &Config::arg1);

當這些結構體很少的時候,寫上面的就可以了。但是當我有大量簡單的結構時,它變得乏味。

是否有一個方便的宏,我可以這樣寫:

PYBIND_EXPORT_STRUCT(Config1);
PYBIND_EXPORT_STRUCT(Config2);
...

每個都掃描并匯出所有給定結構的成員變數?

如果我已經以這種形式撰寫結構會有所幫助嗎:

struct Config {
    ADD_PROPERTY(bool, option1);
    ADD_PROPERTY(bool, option2);
    ADD_PROPERTY(int, arg1);
};

我的問題包括兩部分:

  1. 反映一個成員變數回到它的名字的字串。
  2. 迭代通過struct成員。

我知道解決第一部分內省typeid(arg1).name()用于檢索名稱字串。

對于第二部分,C 不直接支持。但是,我試圖通過這里的一些答案來弄清楚

剩下的問題是如何融合上述兩部分以獲得我想象中的PYBIND_EXPORT_STRUCT()功能的作業實作

也就是說,我不介意以完全不同的表示形式(例如使用宏或元組)來表達我的結構。只要我在使用 pybind11 匯出結構成員時不必再次列舉它們,任何都可以,并且我仍然可以像config1.option1=true在 C 代碼中一樣使用變數

uj5u.com熱心網友回復:

對于數字 2,您可以嘗試查看我的pod_reflection庫:

// main.cpp

struct Config {
  bool option1;
  bool option2;
  int arg1;
};

#include <pod_reflection/pod_reflection.h>
#include <iostream>

int main()
{
  std::cout << "Config size: " << eld::pod_size<Config>() << std::endl;
  std::cout << std::boolalpha;
  Config conf{true, false, 815};
  eld::for_each(conf, [](const auto& i){ std::cout << i << std::endl; });
  return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.7.2 FATAL_ERROR)

project(pod_example)

add_subdirectory(pod_reflection)

add_executable(main main.cpp)
target_link_libraries(main eld::pod_reflection)

它可以遍歷 pod 的基本型別元素。還可以通過以下方式使用用戶定義的型別擴展一組型別template<typename ... ArgsT> using extend_feed

using my_feed = extend_feed<std::string, foo>;
eld::for_each<my_feed>(pod, callableVisitor);

您可能會使用eld::deduced& get<I, TupleFeed>(POD& pod)來填充py::class_<Config>. 但是,由于庫不可能知道 pod 成員的名稱,因此您必須想辦法從I. 如果沒有適當的編譯時反射,幾乎不可能實作自動化。請注意get用于reinterpret_cast通過偏移量獲取指向成員的指標。

uj5u.com熱心網友回復:

1.如何不解決問題

您想到的方法都不可行也不實用。

我知道解決第一部分的內省,typeid(arg1).name()用于檢索名稱字串。

這是不正確的。C 有 RTTI,即運行時型別資訊,但與 C#、Java 或 Python 所具有的“反射”相去甚遠。特別是,成員函式std::type_info::name()“回傳一個實作定義的包含型別名稱的空終止字串。不提供任何保證;特別是,回傳的字串對于多種型別可以是相同的,并且在同一程式的呼叫之間會發生變化。” 【亮點是我的-kkm】其實這個程式

#include <iostream>
#include <typeinfo>
struct Config { int option; };
int main() { std::cout << typeid(&Config::option).name() << "\n"; }

列印,如果在 Linux x64 上使用 GCC 11 編譯,

M6Configi

這是完全符合標準的。這里是你的第 1 部分。不包含成員的名字,它被稱為運行時型別,情報,而不是運行時名稱資訊的一個原因。您甚至可以進行有根據的猜測并解碼列印的字串:M= 指向成員的指標,6= 接下來的 6 個字符命名結構型別,Config= 明顯,i= int指向 type 成員的指標Config,型別int本身。但是另一個編譯器會以不同的方式對型別進行編碼(“mangle”,如何稱呼)。

關于第 2 部分,請觀看CppCon 視頻演示(來自您正在鏈接的答案)的真實情況:它證明了 C 14 元編程功能強大到足以提取有關 POD 型別的資訊。如您所見,presenter 為您可能遇到的每個成員型別宣告了兩個函式(int, volatile int, const int, const volatile int, short, ...)。讓我們就此打住吧。所有這些型別都是不同的。事實上,當我volatile int option;在上面的小測驗程式中將單獨結構成員的宣告更改為時,它列印了一個不同的重整型別名稱:M6ConfigVi.

CppCon 演示文稿展示了機器的功能,而不是它應該用于什么。打個比方,這就是飛機在航展上的滾桶對常規客運航空公司運營的影響。如果我是你,我會避免在生產代碼中使用桶滾……

實際上,這對編譯器來說是一個很好的測驗。我過去常常使用更溫和的元編程結構導致編譯器崩潰。此外,您可能不會喜歡所有這些 kaboodle 的編譯時間。坐等 10 分鐘直到編譯器完成編譯,請不要感到驚訝,四種方式之一:崩潰;內部錯誤報告;成功生成錯誤代碼;或者,手指交叉,成功生成正確的代碼。另外,你需要一個深,我的意思是真的,真的很深理解元編程,編譯器如何選擇不同的模板多載,什么是未評估的背景關系和 SFINAE,等等。簡單地說,就是不要。它可能作業,但不值得讓會議演示作業所需的大量“框架”代碼以及編譯器正確性的不確定性 wrt 這樣一個極其復雜的元程式。

2.如何解決問題

有一種非常傳統的方式來做你想做的事情,依賴于普通的舊 C 前處理器宏。核心思想是這樣的:您將結構的定義作為類函式前處理器宏撰寫在一個單獨的檔案中,該檔案不包含這些宏的定義(我們稱其為“抽象定義檔案”或 ADF,對于需要一個公認的術語)。第二個檔案,您包含的普通頭檔案,用于獲取結構的具體宣告,定義這些特殊的宏以擴展為普通的 C 構造,然后包含 ADF,然后(重要!)對#undef它們進行處理。創建 Python 系結的第三個檔案首先包含頭檔案,然后定義相同但不同的宏(這就是為什么#undefs 很重要!),這一次它們擴展到 pybind11 語法結構;然后在同一編譯單元中第二次包含 ADF 現在讓我們把整個事情放在一起。

第一個檔案是自動進稿器,structs.absdef我不會給它傳統的.h擴展名,以防止它與“普通”頭檔案混淆。擴展可以是你想要的任何東西,但在專案中選擇一個獨一無二的會很有幫助。

/* structs.absdef -- abstract definition of data structures */

#ifndef BEGIN_STRUCT_DEF
#error "This file should be included only from structs.h or pybind.cc"
#endif

BEGIN_STRUCT_DEF(Config)
  STRUCT_MEMBER(Config, bool, option1)
  STRUCT_MEMBER(Config, bool, option2)
  STRUCT_MEMBER(Config, int, arg1)
END_STRUCT_DEF()

/* ... and then structs, structs and more structs ... */

#ifndef/ #error/#endif只是為了立即如果前處理器宏不包括檔案之前定義停止匯編; 否則,你會得到一堆編譯錯誤,更可能是誤導而不是對診斷問題有幫助。

This file will be included into the second file, which is your normal C header which defines all the structs in C syntax. This is the file that you include as a normal, plain and boring C header into your C source and/or other include files, where you want the declaration of these structs structs be visible.

/* structs.h -- C   concrete definitions of data structures */

#ifndef MYPROJECT_STRUCTS__H
#define MYPROJECT_STRUCTS__H

#define BEGIN_STRUCT_DEF(stype)            struct stype {
#define STRUCT_MEMBER(stype, mtype, name)    mtype name;
#define END_STRUCT_DEF()                   };

#include "structs.absdef"

#undef BEGIN_STRUCT_DEF
#undef STRUCT_MEMBER
#undef END_STRUCT_DEF

#endif  // MYPROJECT_STRUCTS__H

One thing to note here is that this file has include guards but the ADT doesn't. This is so because it is included twice in the compilation unit with the pybind calls. This C file is special: it transforms the same ADT definitions into pybind syntax. I have no idea how pybind works; I'm blindly copying your example.

/* pybind.cc -- Generate pybind11 Python bindings */

#include "pybind11.h" // All these #include   ...
#include "other.h"    // ... directives stand ...
#include "stuff.h"    // ... for the real McCoy.

#include "structs.h"  /* You need "normal" C   definitions, too! */

// We rely here on the ADF having had #undef'd it's definition of these.
// The preprocessor does not allow silently redefining macros.
#define BEGIN_STRUCT_DEF(stype)            py::class_<stype>(m, #stype)
#define STRUCT_MEMBER(stype, mtype, name)   .def_readwrite(#name, &stype::name)
#define END_STRUCT_DEF()                   ;

void create_pybind_bindings() {
  // The ADF is included the second time in the CU.
  #include "structs.absdef"
}

// Not necessary, but customary to avoid polluting the preprocessor
// namespace, unless the C   source ends right here.
#undef BEGIN_STRUCT_DEF
#undef STRUCT_MEMBER
#undef END_STRUCT_DEF

Two points that you should pay attention to.

First, there is no space between function-like macro and the opening parenthesis:

// Correct:
#define FOO(x) ((x)   42)
// This
int j = FOO(1);
// `FOO(1)' expands into by replacing `x' with `1':
int j = ((1)   42);

// Incorrect:
//         v--- A feral space attacks!!! Everyone seek shelter!!!
#define BAR (x) ((x)   42)
// Since BAR is not a function-like macro, it expands literally
// as defined into `(x) ((x)   42)', such that this:
int j = BAR(1);
// expands into:
int j = (x) ((x)   42)(1);

i.e. BAR is substituted literally and exactly where it appears. What your compiler will have to say when it tries to digest the result is a load of garbage errors, and certainly not "error: you inserted a space between BAR and (", so be careful.

Second point is the use of the preprocessor's stringizing operator #, which expands the following function-like macro argument into a double-quoted string: #sname turns into "Config", in quotes, which is just what you need to pass to the pybind API.

3. Bonus: a peek under the hood

Obviously, we don't have the files "pybind11.h", "other.h" and "stuff.h": they are just placeholder names, so I'll simply create empty ones. The 3 other files I have literally copied from this answer. When you compile pybind.cc, the C preprocessor is first invoked by the compiler driver. We'll invoke it alone and examine its output. The c -E <filename.cc> command tells the compiler to call the preprocessor, but instead of ingesting the resulting file, just print it to stdout and stop.

我通過壓縮多個空行來壓縮輸出:前處理器去除注釋行和帶有它接受和處理的指令的行,但仍然列印空行以維護正確的診斷行號,可能由下一個處理階段輸出。以 開頭的額外行#也用于下一次傳遞和相同的目的:它們只是確定正在處理的行號和檔案名。忽略它們是很好的衡量標準。

$ touch "pybind11.h" "other.h" "stuff.h"

$ ls *.{cc,h,absdef}
other.h  pybind.cc  pybind11.h  structs.absdef  structs.h  stuff.h

$ c   -E pybind.cc
# 1 "pybind.cc"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "pybind.cc"

# 1 "pybind11.h" 1
# 4 "pybind.cc" 2
# 1 "other.h" 1
# 5 "pybind.cc" 2
# 1 "stuff.h" 1
# 6 "pybind.cc" 2

# 1 "structs.h" 1
# 10 "structs.h"
# 1 "structs.absdef" 1

struct Config {
  bool option1;
  bool option2;
  int arg1;
};
# 11 "structs.h" 2
# 8 "pybind.cc" 2

void create_pybind_bindings() {
# 1 "structs.absdef" 1

py::class_<Config>(m, "Config")
  .def_readwrite("option1", &Config::option1)
  .def_readwrite("option2", &Config::option2)
  .def_readwrite("arg1", &Config::arg1)
;
# 15 "pybind.cc" 2
}

或者,沒有# number file flags僅編譯器列印正確診斷背景關系所需的表單提示(如“structs.absdef:5 包含從 structs.h:10: error: ...”),漂亮干凈的精確副本實際編譯器處理的編譯單元所需的代碼是:

struct Config {
  bool option1;
  bool option2;
  int arg1;
};

void create_pybind_bindings() {
py::class_<Config>(m, "Config")
  .def_readwrite("option1", &Config::option1)
  .def_readwrite("option2", &Config::option2)
  .def_readwrite("arg1", &Config::arg1)
;
}

4. Colophon,或一點聰明和一點歷史

  • 并非每一項新技術都適用于一切事物,僅僅因為它是新的。
  • 前處理器實際上比 C 語言本身稍早。 49歲,準確的說。 C 采用了貝爾實驗室內部用于其他語言的前處理器。

轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/386443.html

標籤:C C 11 绑定11

上一篇:對sizeof的無效寫入

下一篇:如何有效地找到從每個值到下一個較低/較高值的距離?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • Git本地庫既關聯GitHub又關聯Gitee

    創建代碼倉庫 使用gitee舉例(github和gitee差不多) 1.在gitee右上角點擊+,選擇新建倉庫 ? 2.選擇填寫倉庫資訊,然后進行創建 ? 3.服務端已經準備好了,本地開始作準備 (1)Git 全域設定 git config --global user.name "成鈺" git c ......

    uj5u.com 2020-09-10 05:04:14 more
  • CODING DevOps 代碼質量實戰系列第二課,相約周三

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。**《DevOps 代碼質量實戰(PHP 版)》**為 CODING DevOps 代碼質量實戰系列的第二課,同時也是本系列的 PHP ......

    uj5u.com 2020-09-10 05:07:43 more
  • 推薦Scrum書籍

    推薦Scrum書籍 直接上干貨,推薦書籍清單如下(推薦有順序的哦) Scrum指南 Scrum精髓 Scrum敏捷軟體開發 Scrum捷徑 硝煙中的Scrum和XP : 我們如何實施Scrum 敏捷軟體開發:Scrum實戰指南 Scrum要素 大規模Scrum:大規模敏捷組織的設計 用戶故事地圖 用 ......

    uj5u.com 2020-09-10 05:07:45 more
  • CODING DevOps 代碼質量實戰系列最后一課,周四發車

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。 **《DevOps 代碼質量實戰(Java 版)》**為 CODING DevOps 代碼質量實戰系列的最后一課,同時也是本系列的 ......

    uj5u.com 2020-09-10 05:07:52 more
  • 敏捷軟體工程實踐書籍

    Scrum轉型想要做好,第一步先了解并真正落實Scrum,那么我推薦的Scrum書籍是要看懂并實踐的。第二步是團隊的工程實踐要做扎實。 下面推薦工程實踐書單: 重構:改善既有代碼的設計 決議極限編程 : 擁抱變化 代碼整潔代碼 程式員的職業素養 修改代碼的藝術 撰寫可讀代碼的藝術 測驗驅動開發 : ......

    uj5u.com 2020-09-10 05:07:55 more
  • Jenkins+svn+nginx實作windows環境自動部署vue前端專案

    前面文章介紹了Jenkins+svn+tomcat實作自動化部署,現在終于有空抽時間出來寫下Jenkins+svn+nginx實作自動部署vue前端專案。 jenkins的安裝和配置已經在前面文章進行介紹,下面介紹實作vue前端專案需要進行的哪些額外的步驟。 注意:在安裝jenkins和nginx的 ......

    uj5u.com 2020-09-10 05:08:49 more
  • CODING DevOps 微服務專案實戰系列第一課,明天等你

    CODING DevOps 微服務專案實戰系列第一課**《DevOps 微服務專案實戰:DevOps 初體驗》**將由 CODING DevOps 開發工程師 王寬老師 向大家介紹 DevOps 的基本理念,并探討為什么現代開發活動需要 DevOps,同時將以 eShopOnContainers 項 ......

    uj5u.com 2020-09-10 05:09:14 more
  • CODING DevOps 微服務專案實戰系列第二課來啦!

    近年來,工程專案的結構越來越復雜,需要接入合適的持續集成流水線形式,才能滿足更多變的需求,那么如何優雅地使用 CI 能力提升生產效率呢?CODING DevOps 微服務專案實戰系列第二課 《DevOps 微服務專案實戰:CI 進階用法》 將由 CODING DevOps 全堆疊工程師 何晨哲老師 向 ......

    uj5u.com 2020-09-10 05:09:33 more
  • CODING DevOps 微服務專案實戰系列最后一課,周四開講!

    隨著軟體工程越來越復雜化,如何在 Kubernetes 集群進行灰度發布成為了生產部署的”必修課“,而如何實作安全可控、自動化的灰度發布也成為了持續部署重點關注的問題。CODING DevOps 微服務專案實戰系列最后一課:**《DevOps 微服務專案實戰:基于 Nginx-ingress 的自動 ......

    uj5u.com 2020-09-10 05:10:00 more
  • CODING 儀表盤功能正式推出,實作作業資料可視化!

    CODING 儀表盤功能現已正式推出!該功能旨在用一張張統計卡片的形式,統計并展示使用 CODING 中所產生的資料。這意味著無需額外的設定,就可以收集歸納寶貴的作業資料并予之量化分析。這些海量的資料皆會以圖表或串列的方式躍然紙上,方便團隊成員隨時查看各專案的進度、狀態和指標,云端協作迎來真正意義上 ......

    uj5u.com 2020-09-10 05:11:01 more
最新发布
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:41:12 more
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:35:34 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:05:44 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:00:18 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:20:31 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:55 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:18:51 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:00 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:17:55 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:12:06 more