Python/C APIを読む その1(便利なマクロ)

#概要
Pythonの構造を理解する助けとして、C APIのドキュメントを読み砕いていく。

#内容
##環境
Python 3.7 (CPython)に話を固定するため、
以下のコミットに話を限定する。
[tree](https://github.com/python/cpython/tree/a144feeb7ec501aaf30072d50e70d54b200e5ef0)   ([コミット](https://github.com/python/cpython/commit/79373951b3eab585d42e0f0ab83718cbe1d0ee33); [ブランチ](https://github.com/python/cpython/tree/3.7))
(↑　githubのリンク)

##内容
###Py_UNREACHABLE()
####\<概説\>
辿りつくはずのない場所に使う。(assert(0)やabort()を使いたくなるようなcase節のdefault内など。)([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_UNREACHABLE))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#  define Py_UNREACHABLE() __builtin_unreachable()
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L122))

- `__builtin_unreachable`は、コンパイラーにここにはたどり着かないことを知らせ最適化を促す？
- コンパイラー`gcc`の新しいバージョンの場合。

####\<使用例\>
```c:使用例　(cpython/Python/ast.c)
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();
    }
}
```
([使用例出典](https://github.com/python/cpython/blob/94faa0724f8cbae6867c491c8e465e35f4fdbfbb/Python/ast.c#L86))

###Py_ABS(x)
####\<概説\>
`x`の絶対値を返す。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_ABS))

####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define Py_ABS(x) ((x) < 0 ? -(x) : (x))
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L11))

####\<使用例\>
```c:使用例　(cpython/Objects/listobject.c)
    assert(Py_ABS(Py_SIZE(v)) <= 1);
```
([使用例出典](https://github.com/python/cpython/blob/145bf269df3530176f6ebeab1324890ef7070bf8/Objects/listobject.c#L2092))

###Py_MIN(x, y)
####\<概説\>
`x`と`y`の最小値を求める。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_MIN))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L5))

####\<使用例\>
```c:使用例　(cpython/Modules/arraymodule.c)
        Py_ssize_t common_length = Py_MIN(Py_SIZE(va), Py_SIZE(wa));
```
([使用例出典](https://github.com/python/cpython/blob/b8eb3765908c0063f0739595ba4b296cc8863d19/Modules/arraymodule.c#L705))

###Py_MAX(x, y)
####\<概説\>
`x`と`y`の最大値を求める。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_MAX))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L8))

####\<使用例\>
```c:使用例　(cpython/Python/fileutils.c)
    first = Py_MAX(first, 0);
```
([使用例出典](https://github.com/python/cpython/blob/9976834f807ea63ca51bc4f89be457d734148682/Python/fileutils.c#L2247))

###Py_STRINGIFY(x)
####\<概説\>
`x`をC文字列へ変換する。例えば、`Py_STRINGIFY(123)`は`"123"`を返す。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_STRINGIFY))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#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)
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L19))
マクロと#演算子については、[別サイト](https://www.hiroom2.com/2015/09/07/c%E8%A8%80%E8%AA%9E%E3%81%AE-line-%E3%83%9E%E3%82%AF%E3%83%AD%E3%82%92%E3%83%97%E3%83%AA%E3%83%97%E3%83%AD%E3%82%BB%E3%83%83%E3%82%B5%E3%81%AE%E6%AE%B5%E9%9A%8E%E3%81%A7%E6%96%87%E5%AD%97%E5%88%97%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/)に以下のような解説と例がある。
> \#演算子
>プリプロセッサが#\<identifier>の\<identifier>を文字列に変換します。 
>

```c:例1
#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;
}
```
```:例1出力
a = 1
b = 2
```
また、マクロを入れ子にすることで、以下のような動作になります。

```c:例2
#include <stdio.h>

#define STRINGIFY(n) #n
#define TOSTRING(n) STRINGIFY(n)

int main(void)
{
  puts(STRINGIFY(__LINE__));
  puts(TOSTRING(__LINE__));
  return 0;
}
```
```:例2出力
__LINE__
9
```
プリプロセッサがマクロを段階的に展開するためですが、詳細は出典元HPをご参照ください。

####\<使用例\>
```c:使用例　(cpython/
    do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
```
([使用例出典](https://github.com/python/cpython/blob/32bd68c839adb7b42af12366ab0892303115d1d1/Objects/object.c#L33))
`do{...} while (0)`は、複数行にわたるmacro定義の際によく使われるテクニック。(cf. [Stack Overflow](https://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block))

###Py_MEMBER_SIZE(type, member)
####\<概説\>
(`type`) 構造体の `member` のサイズをバイト単位で返す。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_MEMBER_SIZE))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L22))
型キャストした後に構造体の要素にアクセスし、サイズを得ています。

####\<使用例\>
見たところ、CPython内では使われていなさそう。

###Py_CHARMASK(c)
####\<概説\>
引数は文字か、`[-128, 127]`あるいは`[0, 255]` の範囲の整数である必要がある。このマクロは符号なし文字にキャストした`c`を返す。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_CHARMASK))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff))
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L25))

この動作の詳細については、まだよくわかりません。わかったら追記します。

####\<使用例\>
```c:使用例　(cpython/Parser/string_parser.c)
    const char *s = PyBytes_AsString(t->bytes);
    (中略)
    int quote = Py_CHARMASK(*s);
```
([使用例出典](https://github.com/python/cpython/blob/8370e07e1e5b626e78ddc7aadbfaf248976c4454/Parser/string_parser.c#L166))

###Py_GETENV(s)
####\<概説\>
`getenv(s)`に似ているが、コマンドラインで -E が渡された場合 (つまり`Py_IgnoreEnvironmentFlag`が設定された場合) `NULL`を返す。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_GETENV))
`PYTHONPATH`や`PYTHONHOME`のような環境変数をとるときに使う。
####\<定義\>
```c:定義　(cpython/Include/cpython/pydebug.h)
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv(s))
```
([定義出典](https://github.com/python/cpython/blob/4a6bf276ed3e6687394afe26b0d9a061ac06fc6b/Include/cpython/pydebug.h#L32))

####\<使用例\>
```c:使用例　(cpython/Python/thread.c)
    const char *p = Py_GETENV("PYTHONTHREADDEBUG");
```
([使用例出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Python/thread.c#L62))

###Py_UNUSED(arg)
####\<概説\>
コンパイラーからの警告を抑えるため、使わない引数を明示する。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_UNUSED))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#  define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L98))

- `##`はトークン結合演算子で、プリプロセッサーが二つの文字をくっつけて置き換える。([出典](http://wisdom.sakura.ne.jp/programming/c/c42.html))
- `__attribute__((unused))`は、GNU compilerへのメッセージで、使われなくてもコンパイラーエラーを出さないためのものである。([出典](https://www.keil.com/support/man/docs/armcc/armcc_chr1359124982981.htm))

```c:トークン演算子(##)　例
#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
```
```c:__attribute__((unused))　例
void Variable_Attributes_unused_0()
{
    static int aStatic =0;
    int aUnused __attribute__((unused));
    int bUnused;
    aStatic++;
}
```
直上の例は、`bUnused`が使われていないというエラーは出すが、`aUnused`が使われていないというエラーは出さない。

####\<使用例\>
```c:使用例　(cpython/Objects/iterobject.c)
static PyObject *
iter_len(seqiterobject *it, PyObject *Py_UNUSED(ignored))
{
```
([使用例出典](https://github.com/python/cpython/blob/919d42d477093154a30b55d9d79f023dbbe5614a/Objects/iterobject.c#L82))

###Py_DEPRECATED(version)
####\<概説\>
古くなり使用されていない宣言に使う。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.Py_DEPRECATED))
####\<定義\>
```c:定義　(cpython/Include/pyport.h)
#define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__))
```
([定義出典](https://github.com/python/cpython/blob/131d5516409791b170b09a6ef8ed8463c9b09015/Include/pyport.h#L513))

※`__attribute__((deprecated))`は、gnu compilerにdeprecateされたものであることを伝える。([出典](https://www.keil.com/support/man/docs/armcc/armcc_chr1359124981701.htm))(`__deprecated__`と前後に`__`がある理由はわからない)

####\<使用例\>
```c:使用例　(cpython/Include/ceval.h)
Py_DEPRECATED(3.9) PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords(
```
([使用例出典](https://github.com/python/cpython/blob/dcc54215ac1eb4b6fab2a9ffe1abcdf3ac4bb77e/Include/ceval.h#L17))

###PyDoc_STRVAR(name, str)
####\<概説\>
docstringsで使うことのできる`name`が名前の変数を作る。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.PyDoc_STRVAR))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L71))

####\<使用例\>
```c:使用例　(cpython/Objects/setobject.c)
PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.\n\
Raises KeyError if the set is empty.");
```
([使用例出典](https://github.com/python/cpython/blob/72789592a3491bab49f144bb292679b1484885d9/Objects/setobject.c#L654))

###PyDoc_STR(str)
####\<概説\>
docstringを作成する。docstringが無効にされているときは、空文字を出力する。([出典](https://docs.python.org/ja/3/c-api/intro.html#c.PyDoc_STR))
####\<定義\>
```c:定義　(cpython/Include/pymacro.h)
#define PyDoc_VAR(name) static const char name[]
```
([定義出典](https://github.com/python/cpython/blob/63298930fb531ba2bb4f23bc3b915dbf1e17e9e1/Include/pymacro.h#L70))

####\<使用例\>
```c:使用例　(cpython/Objects/exceptions.c)
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},
};
```
([使用例出典](https://github.com/python/cpython/blob/bcb094b41f7fe4dd1686c50891d85632fcf0d481/Objects/exceptions.c#L305))


##蛇足
```c:Python.h
...
#include "pyport.h"
#include "pymacro.h"
...
#include "pydebug.h"
...
```

#参考にさせていただいた本・頁
上記参照
#感想
内部を見れているようで面白かった。
#今後
`((unsigned char)((c) & 0xff))`の挙動を調べたい。
ブランチが厳密には特定できていないようなので、もし気が向けばそこも修正したい。