visibility屬性在cpython中的應用方法是什么

本篇內(nèi)容介紹了“visibility屬性在cpython中的應用方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比英吉沙網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式英吉沙網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋英吉沙地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。

GCC attribute 語法

GCC 的attribute語法可以為函數(shù),結構體,類,枚舉, 變量,標簽等添加屬性。 例如為函數(shù)添加屬性__attribute__((noreturn)),可以讓編譯器知道該函數(shù)不會返回給調(diào)用者,并作出相應優(yōu)化。

在Windows專用修飾符中也有類似gcc attribute語法的關鍵字 —— __declspec. 在追求跨平臺通用性的時候,通常會同時加入gcc __attribute__ 和windows __declspec,在編譯時判斷平臺,決定使用哪一種方式

__declspec( 屬性 ) 聲明語句

// 舉例
__declspec( dllimport ) int i;
__declspec( dllexport ) void func();

Q: __attribute__((屬性列表)) 到底寫在什么位置?

A: 相同作用對象添加不同屬性,或相同屬性作用于不同對象,有的緊跟關鍵字,有的在整個聲明之后。具體可查閱官方文檔

//可以緊跟在 struct, class, union, enum 關鍵字之后,也可以在右括號之后,首選前者
struct __attribute__ ((aligned (8))) S { short f[3]; };    //為結構體S添加屬性 aligned (8)

//在typdef聲明中使用
typedef int T1 __attribute__ ((deprecated)); //T1在任意地方被使用時,deprecated屬性會產(chǎn)生告警

//函數(shù)
__attribute__((屬性列表)) void foo1 (void);
void foo2 (void) __attribute__((屬性列表));

Q: 有哪些屬性可以用?

A: 對于函數(shù),結構體,類等不同的作用對象,都有各自支持的屬性,其中又分為最常用的公共屬性和針對特定CPU架構的特殊屬性,詳情請查閱官方文檔。

visibility 屬性

visibility 屬性用于指定可見性,可以用于 函數(shù), class, struct, union, enum,使用語法一致。

void __attribute__ ((visibility ("protected"))) f () { /* Do something. */; }
int i __attribute__ ((visibility ("hidden")));

屬性值:

  • default:具有外部鏈接性 (external linkage),可以被外部其它模塊引用,并且有可能被重寫

  • hidden:只能在同一共享對象(可簡單理解為庫文件)中被引用

  • internal:無法被其它模塊直接引用,但是可以通過指針間接引用

  • protected:可以被引用,但無法被重寫

visibility 屬性使用方法

在講visibility屬性用法之前 ,我們先了解一下,

__declspec( 屬性 ) 聲明語句
    
// 舉例
__declspec( dllimport ) int i;
__declspec( dllexport ) void func();

此處我們看gcc wiki中的一例經(jīng)典模板,可以用于定義共享庫

/* 定義宏:
標識符: FOX_HELPER_DLL_IMPORT   default,指定函數(shù), class, struct 等為公開可重寫的。不編譯,直接導入。
標識符: FOX_HELPER_DLL_EXPORT   default,指定函數(shù), class, struct 等為公開可重寫的。編譯并導出為動態(tài)庫(DLL或so)
標識符: FOX_HELPER_DLL_LOCAL    hidden,指定函數(shù), class, struct 等只能在同一共享對象中被引用 */

/* 對于win32和Cygwin,使用__declspec()指定屬性 */
#if defined _WIN32 || defined __CYGWIN__
  #define FOX_HELPER_DLL_IMPORT __declspec(dllimport)	//從dll導入。其它模塊可見
  #define FOX_HELPER_DLL_EXPORT __declspec(dllexport)	//導出到dll。其它模塊可見
  #define FOX_HELPER_DLL_LOCAL
#else
  /*對于gcc,使用__attribute__ ((visibility ("屬性值"))) 指定可見性*/
  #if __GNUC__ >= 4
    #define FOX_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
    #define FOX_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) 
    #define FOX_HELPER_DLL_LOCAL  __attribute__ ((visibility ("hidden"))) 
  #else
  /*代碼中已經(jīng)引用了宏,即使什么屬性都不加,也要定義宏*/
    #define FOX_HELPER_DLL_IMPORT
    #define FOX_HELPER_DLL_EXPORT
    #define FOX_HELPER_DLL_LOCAL
  #endif
#endif

/*定義宏 FOX_API 與 FOX_LOCAL
標識符: FOX_API    default,指定函數(shù), class, struct 等為公開可重寫的,要么編譯導出,要么直接導入
標識符: FOX_LOCAL    hidden,指定函數(shù), class, struct 等只能在同一共享對象中被引用 */

#ifdef FOX_DLL // 如果FOX需要被編譯為動態(tài)庫
  #ifdef FOX_DLL_EXPORTS // 如果是正在構建 (而非使用) FOX
    #define FOX_API FOX_HELPER_DLL_EXPORT //導出動態(tài)庫
  #else
    #define FOX_API FOX_HELPER_DLL_IMPORT  //不構建,直接導入動態(tài)庫
  #endif // FOX_DLL_EXPORTS
  #define FOX_LOCAL FOX_HELPER_DLL_LOCAL //定義FOX_LOCAL
#else // 沒有定義 FOX_DLL 宏,則說明FOX是靜態(tài)庫,不添加任何屬性
  #define FX_API
  #define FOX_LOCAL
#endif // FOX_DLL

引用方式:

//聲明一個公開可重寫(default)的函數(shù)
extern FOX_API PublicFunc1();   //extern __attribute__ ((visibility ("default"))) PublicFunc1(); 

//聲明一個僅內(nèi)部使用(hidden)的函數(shù)
extern FOX_LOCAL PublicFunc2();  //extern __attribute__ ((visibility ("hidden"))) PublicFunc2();

//聲明一個公開可重寫(default)的類
class FOX_API PublicClass1;  //class __attribute__ ((visibility ("default"))) PublicClass1;

 //聲明一個僅內(nèi)部使用(hidden)的類
class FOX_LOCAL PublicClass2; //class __attribute__ ((visibility ("hidden"))) PublicClass2;

實際應用 - cpython

研究過cpython的朋友看到以上demo可能會有點眼熟。是的,cpython中有一段相似的代碼。當然,cpython中還兼容了clang。

#ifndef Py_EXPORTS_H
#define Py_EXPORTS_H

/* 定義宏:
標識符: Py_IMPORTED_SYMBOL    default,指定函數(shù), class, struct 等為公開可重寫的。不編譯,直接導入
標識符: Py_EXPORTED_SYMBOL    default,指定函數(shù), class, struct 等為公開可重寫的。編譯導出到動態(tài)庫(DLL或so)
標識符: Py_LOCAL_SYMBOL    hidden,指定函數(shù), class, struct 等只能在同一共享對象中被引用 */

/* 對于win32和Cygwin,使用__declspec()指定屬性 */
#if defined(_WIN32) || defined(__CYGWIN__)
    #define Py_IMPORTED_SYMBOL __declspec(dllimport)	//從dll導入。其它模塊可見	
    #define Py_EXPORTED_SYMBOL __declspec(dllexport)	//導出到dll。其它模塊可見
    #define Py_LOCAL_SYMBOL
#else
    //__has_attribute參數(shù)為屬性名,可以評估當前編譯目標是否支持該屬性,支持為1,不支持為0
    //下面三行代碼用于兼容non-clang編譯器。對于non-clang編譯器,__has_attribute(x)表達式直接轉0
    #ifndef __has_attribute
      #define __has_attribute(x) 0
    #endif
    //如果 gcc版本>=4 或 clang判定編譯目標支持visibility屬性,則添加屬性
    #if (defined(__GNUC__) && (__GNUC__ >= 4)) ||
        (defined(__clang__) && __has_attribute(visibility))
        #define Py_IMPORTED_SYMBOL __attribute__ ((visibility ("default")))
        #define Py_EXPORTED_SYMBOL __attribute__ ((visibility ("default")))
        #define Py_LOCAL_SYMBOL  __attribute__ ((visibility ("hidden")))
    #else
        #define Py_IMPORTED_SYMBOL
        #define Py_EXPORTED_SYMBOL
        #define Py_LOCAL_SYMBOL
    #endif
#endif

#endif /* Py_EXPORTS_H */
  • Py_LOCAL_SYMBOL :在源碼之中暫未發(fā)現(xiàn)引用之處

  • Py_IMPORTED_SYMBOL:用于在Windows編譯器或cygwin中,構建非核心模塊時定義宏 PyAPI_FUNCPyAPI_DATA,意義是直接導入核心模塊,防止編譯器再次編譯。原因參考此處。

  • Py_EXPORTED_SYMBOL :定義宏 PyAPI_FUNC,PyAPI_DATA, PyMODINIT_FUNC

以下代碼中會用到的一些宏標識符,先進行一下說明:

  • Py_ENABLE_SHARED 值為1 ,windows平臺下,Python核默認在DLL中,允許外部鏈接性

  • HAVE_DECLSPEC_DLL 所有windows編譯器和cygwin均會定義,用于支持__declspec().

  • Py_BUILD_CORE 構建Python內(nèi)核。提供對Python內(nèi)部構件的訪問權,但不應被第三方模塊使用。

  • Py_BUILD_CORE_MODULE 構建一個Python stdlib模塊作為一個動態(tài)庫,Windows上導出“PyInit_xxx”符號。

/*不同平臺構建的差異請參考:https://docs.python.org/zh-cn/3/extending/windows.html#a-cookbook-approach*/

/* 首先處理windows平臺及cygwin */
#if defined(Py_ENABLE_SHARED) || defined(__CYGWIN__)
#       if defined(HAVE_DECLSPEC_DLL)
		//構建Python內(nèi)核,而非Python stdlib模塊
#               if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#                       define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE
#                       define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE
        		//核心模塊初始化函數(shù)不需要外部鏈接性,除非Cygwin來處理嵌入
#                       if defined(__CYGWIN__)
#                               define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
#                       else /* __CYGWIN__ */
#                               define PyMODINIT_FUNC PyObject*
#                       endif /* __CYGWIN__ */
#               else /* Py_BUILD_CORE */
		//核心構建結束,接下來構建擴展模塊(非核心模塊)或嵌入式環(huán)境,需要先導入公共函數(shù)及數(shù)據(jù)
			//使用cygwin會自動導入公共函數(shù)防止編譯,如果沒用使用cygwin,則需顯式導入
#                       if !defined(__CYGWIN__)
#                               define PyAPI_FUNC(RTYPE) Py_IMPORTED_SYMBOL RTYPE
#                       endif /* !__CYGWIN__ */
#                       define PyAPI_DATA(RTYPE) extern Py_IMPORTED_SYMBOL RTYPE
			//按C語言的語法,編譯導出非核心模塊的初始化函數(shù)
#                       if defined(__cplusplus)
#                               define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject*
#                       else /* __cplusplus */
#                               define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
#                       endif /* __cplusplus */
#               endif /* Py_BUILD_CORE */
#       endif /* HAVE_DECLSPEC_DLL */
#endif /* Py_ENABLE_SHARED */

/*如果任然未定義PyAPI_FUNC, PyAPI_DATA, PyMODINIT_FUNC, 說明不是windows平臺或cygwin。默認采用gcc方式定義*/
#ifndef PyAPI_FUNC
	//__attribute__ ((visibility ("default"))) RTYPE
#       define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE
#endif
#ifndef PyAPI_DATA
	//extern __attribute__ ((visibility ("default"))) RTYPE
#       define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE
#endif
//無論是核心還是非核心模塊,都需要外部鏈接性
#ifndef PyMODINIT_FUNC
#       if defined(__cplusplus)
		//extern "C" __attribute__ ((visibility ("default"))) PyObject*
#               define PyMODINIT_FUNC extern "C" Py_EXPORTED_SYMBOL PyObject*
#       else /* __cplusplus */
		//__attribute__ ((visibility ("default"))) PyObject*
#               define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject*
#       endif /* __cplusplus */
#endif

注:以上說的Windows平臺/編譯器,默認指Microsoft Visual C++,但理論上其它編譯器也可以支持

今天研究的是gcc,接下來按照gcc處理方式分析PyAPI_FUNC, PyAPI_DATA, PyMODINIT_FUNC是如何使用的。

PyAPI_FUNC

pycore_hashtable.h中的_Py_hashtable_compare_direct方法為例

extern "C" PyAPI_FUNC(int) _Py_hashtable_compare_direct {//函數(shù)體略};

根據(jù)define的使用方法替換一下,此處就相當于

extern "C" Py_EXPORTED_SYMBOL int _Py_hashtable_compare_direct {//函數(shù)體略};

繼續(xù)替換

extern "C" __attribute__ ((visibility ("default"))) int _Py_hashtable_compare_direct {//函數(shù)體略};

到這一步我們就清晰了,這里其實是定義了一個公開的可以被其它模塊調(diào)用或重寫的函數(shù)int _Py_hashtable_compare_direct

結論:PyAPI_FUNC 指定函數(shù)可以被各個模塊訪問。

PyAPI_DATA

boolobject.h中的PyBool_Type為例

PyAPI_DATA(PyTypeObject) PyBool_Type;	//PyTypeObject是一個結構體

替換

extern Py_EXPORTED_SYMBOL PyTypeObject PyBool_Type;

繼續(xù)替換

extern __attribute__ ((visibility ("default"))) PyTypeObject PyBool_Type;

結果是聲明了一個可以被其它模塊訪問的PyTypeObject結構體變量。

結論:PyAPI_DATA指定變量可以被其它模塊訪問

PyMODINIT_FUNC

arraymodule.h的中的模塊初始化函數(shù)為例

PyMODINIT_FUNC
PyInit_array(void)
{
    return PyModuleDef_Init(&arraymodule);
}

替換

//c語言
Py_EXPORTED_SYMBOL PyObject* PyInit_array(void)
{
    return PyModuleDef_Init(&arraymodule);
}

//C++
extern "C" Py_EXPORTED_SYMBOL PyObject* PyInit_array(void)
{
    return PyModuleDef_Init(&arraymodule);
}

到這里可以看出來,PyInit_array()函數(shù)調(diào)用了PyModuleDef_Init()方法對arraymodule進行了初始化,然后然返回一個PyObject結構體的指針變量

繼續(xù)替換

//c語言
__attribute__ ((visibility ("default"))) PyObject* PyInit_array(void)
{
    return PyModuleDef_Init(&arraymodule);
}

//C++
extern "C" __attribute__ ((visibility ("default"))) PyObject* PyInit_array(void)
{
    return PyModuleDef_Init(&arraymodule);
}

結果是定義了一個其它模塊可以訪問的模塊初始化函數(shù),用于初始化arraymodule,并返回PyObject結構體的指針變量

結論:PyMODINIT_FUNC指定模塊初始化函數(shù)可以被其它模塊訪問,并返回PyObject結構體的指針變量

總結

  1. GCC 的attribute語法可以為函數(shù),結構體,類,枚舉, 變量,標簽等添加屬性。語法:

    __attribute__ ((屬性列表))

    位置一般在關鍵字之后,或整個聲明之后,具體查閱官方文檔

  2. visibility屬性

    extern __attribute__ ((visibility ("default"))) PublicFunc1();
    class __attribute__ ((visibility ("default"))) PublicClass1;

    • default:具有外部鏈接性 (external linkage),可以被外部其它模塊引用,并且有可能被重寫

    • hidden:只能在同一共享對象(可簡單理解為庫文件)中被引用

    • internal:無法被其它模塊直接引用,但是可以通過指針間接引用

    • protected:可以被引用,但無法被重寫

  3. visibility屬性實際應用之cpython

    • PyAPI_FUNC:指定函數(shù)可以被各個模塊訪問

    • PyAPI_DATA: 指定變量可以被其它模塊訪問

    • PyMODINIT_FUNC: 指定模塊初始化函數(shù)可以被其它模塊訪問,并返回PyObject結構體的指針變量

“visibility屬性在cpython中的應用方法是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

文章標題:visibility屬性在cpython中的應用方法是什么
鏈接地址:http://bm7419.com/article44/igscee.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)站營銷網(wǎng)站制作、定制開發(fā)、App開發(fā)響應式網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化