#概要
Pythonの構造を理解する助けとして、C APIのドキュメントを読み砕いていく。
#内容
##環境
Python 3.7 (CPython)に話を固定するため、
以下のコミットに話を限定する。
tree (コミット; ブランチ)
(↑ githubのリンク)
##内容
###Py_UNREACHABLE()
####<概説>
辿りつくはずのない場所に使う。(assert(0)やabort()を使いたくなるようなcase節のdefault内など。)(出典)
####<定義>
# define Py_UNREACHABLE() __builtin_unreachable()
(定義出典)
-
__builtin_unreachable
は、コンパイラーにここにはたどり着かないことを知らせ最適化を促す? - コンパイラー
gcc
の新しいバージョンの場合。
####<使用例>
static const char *
expr_context_name(expr_context_ty ctx)
{
switch (ctx) {
case Load:
return "Load";
case Store:
return "Store";
case Del:
return "Del";
default:
Py_UNREACHABLE();
}
}
(使用例出典)
###Py_ABS(x)
####<概説>
x
の絶対値を返す。(出典)
####<定義>
#define Py_ABS(x) ((x) < 0 ? -(x) : (x))
(定義出典)
####<使用例>
assert(Py_ABS(Py_SIZE(v)) <= 1);
(使用例出典)
###Py_MIN(x, y)
####<概説>
x
とy
の最小値を求める。(出典)
####<定義>
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
(定義出典)
####<使用例>
Py_ssize_t common_length = Py_MIN(Py_SIZE(va), Py_SIZE(wa));
(使用例出典)
###Py_MAX(x, y)
####<概説>
x
とy
の最大値を求める。(出典)
####<定義>
#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
(定義出典)
####<使用例>
first = Py_MAX(first, 0);
(使用例出典)
###Py_STRINGIFY(x)
####<概説>
x
をC文字列へ変換する。例えば、Py_STRINGIFY(123)
は"123"
を返す。(出典)
####<定義>
#define _Py_XSTRINGIFY(x) #x
/* Convert the argument to a string. For example, Py_STRINGIFY(123) is replaced
with "123" by the preprocessor. Defines are also replaced by their value.
For example Py_STRINGIFY(__LINE__) is replaced by the line number, not
by "__LINE__". */
#define Py_STRINGIFY(x) _Py_XSTRINGIFY(x)
(定義出典)
マクロと#演算子については、別サイトに以下のような解説と例がある。
#演算子
プリプロセッサが#<identifier>の<identifier>を文字列に変換します。
#include <stdio.h>
#define print(value) printf(#value " = %d\n", value)
int main(void)
{
int a = 1, b = 2;
print(a);
print(b);
return 0;
}
a = 1
b = 2
また、マクロを入れ子にすることで、以下のような動作になります。
#include <stdio.h>
#define STRINGIFY(n) #n
#define TOSTRING(n) STRINGIFY(n)
int main(void)
{
puts(STRINGIFY(__LINE__));
puts(TOSTRING(__LINE__));
return 0;
}
__LINE__
9
プリプロセッサがマクロを段階的に展開するためですが、詳細は出典元HPをご参照ください。
####<使用例>
do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
(使用例出典)
do{...} while (0)
は、複数行にわたるmacro定義の際によく使われるテクニック。(cf. Stack Overflow)
###Py_MEMBER_SIZE(type, member)
####<概説>
(type
) 構造体の member
のサイズをバイト単位で返す。(出典)
####<定義>
#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
(定義出典)
型キャストした後に構造体の要素にアクセスし、サイズを得ています。
####<使用例>
見たところ、CPython内では使われていなさそう。
###Py_CHARMASK(c)
####<概説>
引数は文字か、[-128, 127]
あるいは[0, 255]
の範囲の整数である必要がある。このマクロは符号なし文字にキャストしたc
を返す。(出典)
####<定義>
#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff))
(定義出典)
この動作の詳細については、まだよくわかりません。わかったら追記します。
####<使用例>
const char *s = PyBytes_AsString(t->bytes);
(中略)
int quote = Py_CHARMASK(*s);
(使用例出典)
###Py_GETENV(s)
####<概説>
getenv(s)
に似ているが、コマンドラインで -E が渡された場合 (つまりPy_IgnoreEnvironmentFlag
が設定された場合) NULL
を返す。(出典)
PYTHONPATH
やPYTHONHOME
のような環境変数をとるときに使う。
####<定義>
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv(s))
(定義出典)
####<使用例>
const char *p = Py_GETENV("PYTHONTHREADDEBUG");
(使用例出典)
###Py_UNUSED(arg)
####<概説>
コンパイラーからの警告を抑えるため、使わない引数を明示する。(出典)
####<定義>
# define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
(定義出典)
-
##
はトークン結合演算子で、プリプロセッサーが二つの文字をくっつけて置き換える。(出典) -
__attribute__((unused))
は、GNU compilerへのメッセージで、使われなくてもコンパイラーエラーを出さないためのものである。(出典)
#include <stdio.h>
#define var(i) printf("var" #i " = %d\n" , var ## i)
int main() {
int var1 = 10 , var2 = 20;
var(1);
var(2);
return 0;
}
var1 = 10
var2 = 20
void Variable_Attributes_unused_0()
{
static int aStatic =0;
int aUnused __attribute__((unused));
int bUnused;
aStatic++;
}
直上の例は、bUnused
が使われていないというエラーは出すが、aUnused
が使われていないというエラーは出さない。
####<使用例>
static PyObject *
iter_len(seqiterobject *it, PyObject *Py_UNUSED(ignored))
{
(使用例出典)
###Py_DEPRECATED(version)
####<概説>
古くなり使用されていない宣言に使う。(出典)
####<定義>
#define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__))
(定義出典)
※__attribute__((deprecated))
は、gnu compilerにdeprecateされたものであることを伝える。(出典)(__deprecated__
と前後に__
がある理由はわからない)
####<使用例>
Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords(
(使用例出典)
###PyDoc_STRVAR(name, str)
####<概説>
docstringsで使うことのできるname
が名前の変数を作る。(出典)
####<定義>
#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
(定義出典)
####<使用例>
PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.\n\
Raises KeyError if the set is empty.");
(使用例出典)
###PyDoc_STR(str)
####<概説>
docstringを作成する。docstringが無効にされているときは、空文字を出力する。(出典)
####<定義>
#define PyDoc_VAR(name) static const char name[]
(定義出典)
####<使用例>
static PyGetSetDef BaseException_getset[] = {
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
{"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
{"__context__", BaseException_get_context,
BaseException_set_context, PyDoc_STR("exception context")},
{"__cause__", BaseException_get_cause,
BaseException_set_cause, PyDoc_STR("exception cause")},
{NULL},
};
(使用例出典)
##蛇足
...
#include "pyport.h"
#include "pymacro.h"
...
#include "pydebug.h"
...
#参考にさせていただいた本・頁
上記参照
#感想
内部を見れているようで面白かった。
#今後
((unsigned char)((c) & 0xff))
の挙動を調べたい。
ブランチが厳密には特定できていないようなので、もし気が向けばそこも修正したい。