亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在多個 C 擴展之間共享 C 單例

如何在多個 C 擴展之間共享 C 單例

冉冉說 2023-08-03 17:06:59
我有一個靜態庫(或一堆 c/cpp 文件),其中包含一個單例,并由/鏈接到兩個不同的 C 擴展使用。然而,C 庫中的單例不再表現得像單例:import getterimport setter# set singleton:setter.set(21)# get singleton:print("singleton: ", getter.get()) #prints the old value:42為了簡單起見,下面是一個使用 Cython 說明此問題的最小示例(所有文件都位于同一文件夾中):C 庫://lib.h:int get_singleton(void);void set_singleton(int new_val);//lib.c:#include "lib.h"static int singleton=42;int get_singleton(void){    return singleton;}void set_singleton(int new_val){    singleton=new_val;}兩個 Cython 擴展:# getter.pyx:#cython: language_level=3cdef extern from "lib.h":    int get_singleton()def get():    return get_singleton()# setter.pyx:#cython: language_level=3cdef extern from "lib.h":    void set_singleton(int new_val);def set(new_val):    set_singleton(new_val)此SO-post之后的安裝文件:#setup.pyfrom setuptools import setup, find_packages, Extensionsetup(      name='singleton_example',      ext_modules=[Extension('getter', sources=['getter.pyx']),                    Extension('setter', sources=['setter.pyx']),],      # will be build as static libraries and automatically passed to linker for all extensions:      libraries = [('static_lib', {'sources': ["lib.c"]}) ]      )通過 構建后python setup.py build_clib build_ext --inplace,可以運行上面的 python 腳本。在多個 (Cython)-C-擴展之間共享 C-單例的正確方法是什么?
查看完整描述

1 回答

?
湖上湖

TA貢獻2003條經驗 獲得超2個贊

目前的問題是該變量singleton存在兩次:一次在擴展中setter,一次在擴展中getter(也可以運行get_singletonset_singleton存在兩次,即每個有兩個不同的地址),這或多或少違反了一個定義規則(ODR),甚至如果這個規則只存在于C++中。違反 ODR 并不是世界末日,但在大多數情況下,該行為變得不可移植,因為不同的鏈接器/編譯器/操作系統以不同的方式處理這種情況。

例如,對于 Linux 上的共享庫,我們有符號插入。然而,Python 使用ldopenwithout?RTLD_GLOBAL(隱含 with?RTLD_LOCAL)來加載 C 擴展,從而防止符號插入。我們可以在 Python 中強制使用RTLD_GLOBAL

import?sys;?import?ctypes;
sys.setdlopenflags(sys.getdlopenflags()?|?ctypes.RTLD_GLOBAL)

getter在導入并setter再次恢復單例屬性之前。然而,這在 Windows 上不起作用,因為 dll 不支持符號插入。

確?!皢卫龑傩浴钡目梢浦卜椒ㄊ潜苊膺`反 ODR,并且為了實現這一點,應該使靜態庫/文件束動態化。該動態庫只會被進程加載一次,從而確保我們只有一個singleton.

根據具體情況,可以選擇如何使用此 dll:

  1. 這些擴展僅在本地使用,而不是使用共享對象

  2. 擴展僅分布在某些平臺上,那么可以預構建共享對象/dll 并像第三方庫一樣分布它們,例如參見此 SO-?post

  3. 可以覆蓋setuptools的build_clib命令,因此它將構建一個共享對象/dll而不是靜態庫,當擴展被鏈接并復制到安裝時將使用該庫。然而,添加更多的擴展,這將使用這個 dll 是相當麻煩的(即使不是不可能)。

  4. 可以編寫 dll 的 cython 包裝器,它的優點是使用 Python 的機制來加載和推遲符號的結果直到運行時,而不是底層操作系統的鏈接器/加載器,這使得例如,稍后可以更輕松地根據動態庫創建進一步的擴展。

我認為默認情況下應該使用最后一種方法。這是一個可能的實現:

  1. 創建靜態庫的包裝并通過pxd-file 公開其功能:

# lib_wrapper.pxd

cdef int get_singleton()

cdef void set_singleton(int new_value)


#lib_wrapper.pyx

cdef extern from "lib.h":

? ? int c_get_singleton "get_singleton" ()

? ? void c_set_singleton "set_singleton" (int new_val)


cdef int get_singleton():

? ? return c_get_singleton()


cdef void set_singleton(int new_val):

? ? c_set_singleton(new_val)

一個重要的部分是:包裝器引入了一定程度的間接性(從而導致大量應該自動化的樣板代碼編寫),因此當在其他模塊中使用它時,既不需要頭文件也不需要 c 文件/庫。


調整其他模塊,它們只需要cimport包裝器:

# getter.pyx:

#cython: language_level=3

cimport lib_wrapper

def get():

? ? return lib_wrapper.get_singleton()


# setter.pyx:

#cython: language_level=3

cimport lib_wrapper

def set(new_val):

? ? lib_wrapper.set_singleton(new_val)

安裝程序不再需要build_clib-step:

from setuptools import setup, find_packages, Extension


setup(

? ? ? name='singleton_example',

? ? ? ext_modules=[Extension('lib_wrapper', sources=['lib_wrapper.pyx', 'lib.c']),

? ? ? ? ? ? ? ? ? ?Extension('getter', sources=['getter.pyx']),?

? ? ? ? ? ? ? ? ? ?Extension('setter', sources=['setter.pyx']),],

? ? ?)

構建 via 后python setup.py build_ext --inplace(在源代碼分發中,即python setup.py build sdisth 文件將丟失,但對于此問題可能有許多不同的解決方案),示例將set/get相同的單例(因為只有一個)。


查看完整回答
反對 回復 2023-08-03
  • 1 回答
  • 0 關注
  • 148 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號