Closed22

context by c

ピン留めされたアイテム
nikogolinikogoli

必要な情報はだいたい掘り終わったので、ここは一旦止めて、こっちで整理していく


https://developer.blender.org/diffusion/B/browse/master/source/blender/editors/screen/screen_context.c


Code Doc. にある、context の要素のセットの例

実際のコード
master/source/blender/editors/space_image/space_image.c
const char *image_context_dir[] = {"edit_image", "edit_mask", NULL};

static int /*eContextResult*/ image_context(const bContext *C,
                                            const char *member,
                                            bContextDataResult *result)
{
  SpaceImage *sima = CTX_wm_space_image(C);

  if (CTX_data_dir(member)) {
    CTX_data_dir_set(result, image_context_dir);
  }
  else if (CTX_data_equals(member, "edit_image")) {
    CTX_data_id_pointer_set(result, (ID *)ED_space_image(sima));
    return CTX_RESULT_OK;
  }
  else if (CTX_data_equals(member, "edit_mask")) {
    Mask *mask = ED_space_image_get_mask(sima);
    if (mask) {
      CTX_data_id_pointer_set(result, &mask->id);
    }
    return CTX_RESULT_OK;
  }
  return CTX_RESULT_MEMBER_NOT_FOUND;
}
nikogolinikogoli
CTX_data_dir の定義

空文字列の判定らしい 添字の変換ルールで間接参照になる

source/blender/blenkernel/intern/context.c$628
bool CTX_data_dir(const char *member)
{
  return member[0] == '\0';
}
CTX_data_dir_set の定義
source/blender/blenkernel/intern/context.c$672
void CTX_data_dir_set(bContextDataResult *result, const char **dir)
{
  result->dir = dir;
}
CTX_data_equals の定義

STREQは文字列比較らしい

source/blender/blenkernel/intern/context.c$623
bool CTX_data_equals(const char *member, const char *str)
{
  return (STREQ(member, str));
}
CTX_data_id_pointer_set の定義
source/blender/blenkernel/intern/context.c$633
void CTX_data_id_pointer_set(bContextDataResult *result, ID *id)
{
  RNA_id_pointer_create(id, &result->ptr);
}
nikogolinikogoli

Imageの定義

ED_space_image の定義
source/blender/editors/space_image/image_edit.c$55
Image *ED_space_image(SpaceImage *sima)
{
  return sima->image;
}

Maskの定義

ED_space_image_get_mask の定義
source/blender/editors/space_image/image_edit.c$122
Mask *ED_space_image_get_mask(SpaceImage *sima)
{
  return sima->mask_info.mask;
}
nikogolinikogoli
RNA_id_pointer_create の定義
source/blender/makesrna/intern/rna_access.c$122
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
{
  StructRNA *type, *idtype = NULL;

  if (id) {
    PointerRNA tmp = {NULL};
    tmp.data = id;
    idtype = rna_ID_refine(&tmp);

    while (idtype->refine) {
      type = idtype->refine(&tmp);

      if (type == idtype) {
        break;
      }
      idtype = type;
    }
  }

  r_ptr->owner_id = id;
  r_ptr->type = idtype;
  r_ptr->data = id;
}
rna_ID_refine の定義
source/blender/makesrna/intern/rna_ID.c$446
StructRNA *rna_ID_refine(PointerRNA *ptr)
{
  ID *id = (ID *)ptr->data;

  return ID_code_to_RNA_type(GS(id->name));
}
ID_code_to_RNA_typeの定義
source/blender/makesrna/intern/rna_ID.c$342
StructRNA *ID_code_to_RNA_type(short idcode)
{
  switch ((ID_Type)idcode) {
    case ID_AC:
      return &RNA_Action;
    ...
  }

  return &RNA_ID;
}

IDの定義

GS() の定義 たぶん
source/blender/makesdna/DNA_ID.h$460
#define GS(a) \
  (CHECK_TYPE_ANY(a, char *, const char *, char[66], const char[66]), \
   (ID_Type)(*((const short *)(a))))

CHECK_TYPE_ANYの定義

ID_Typeの定義:GS()がしてるっぽい引数のキャスト先

nikogolinikogoli
GS() に関する記述とコメント

ID_Typeのところにあるコメント

and the first 2 bytes of #ID.name (for runtime checks, see #GS macro).

Code Doc. の ID のところ

The name of the datablock (pointer->id.name) encodes the type of the datablock. For example, a mesh that is presented in the Blender user interface with the name "Suzanne" is actually named "MESuzanne" in the datablock. This can be used to cast an ID *id to the correct type. A function GS(name) is available to take such a name and return a constant that indicates the ID data type: if (GS(id->name) == ID_SC) … can be used to test whether the ID datablock is a Scene.

書いてあるとおりで、GD() で謎の処理を行ったあと、nameを ID_type にキャストすることで、(なぜなのかわからないが) "ME" を取り出している、という感じ?

ピン留めされたアイテム
nikogolinikogoli
CTX_wm_space_image の定義
source/blender/blenkernel/intern/context.c
struct SpaceImage *CTX_wm_space_image(const bContext *C)
{
  ScrArea *area = CTX_wm_area(C);
  if (area && area->spacetype == SPACE_IMAGE) {
    return area->spacedata.first;
  }
  return NULL;
}
SPACE_IMAGE の定義
source/blender/makesdna/DNA_space_types.h$1850
/* space types, moved from DNA_screen_types.h */
/* Do NOT change order, append on end. types are hardcoded needed */
typedef enum eSpace_Type {
  SPACE_EMPTY = 0,
  ...
  SPACE_IMAGE = 6,
  ...
} eSpace_Type;
ピン留めされたアイテム
nikogolinikogoli
CTX_wm_area の定義
source/blender/blenkernel/intern/context.c
ScrArea *CTX_wm_area(const bContext *C)
{
  return ctx_wm_python_context_get(C, "area", &RNA_Area, C->wm.area);
}
RNA_Area の定義
source/blender/makesrna/RNA_access.h$65
extern StructRNA RNA_Area;
ピン留めされたアイテム
nikogolinikogoli
ctx_wm_python_context_get の定義
source/blender/blenkernel/intern/context.c$256
static void *ctx_wm_python_context_get(const bContext *C,
                                       const char *member,
                                       const StructRNA *member_type,
                                       void *fall_through)
{
#ifdef WITH_PYTHON
  if (UNLIKELY(C && CTX_py_dict_get(C))) {
    bContextDataResult result;
    memset(&result, 0, sizeof(bContextDataResult));
    BPY_context_member_get((bContext *)C, member, &result);

    if (result.ptr.data) {
      if (RNA_struct_is_a(result.ptr.type, member_type)) {
        return result.ptr.data;
      }

      CLOG_WARN(&LOG,
                "PyContext '%s' is a '%s', expected a '%s'",
                member,
                RNA_struct_identifier(result.ptr.type),
                RNA_struct_identifier(member_type));
    }
  }
#else
  UNUSED_VARS(C, member, member_type);
#endif

  /* don't allow UI context access from non-main threads */
  if (!BLI_thread_is_main()) {
    return NULL;
  }

  return fall_through;
}

RNA_struct_is_a の定義
source/blender/makesrna/intern/rna_access.c$844
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
{
  const StructRNA *base;

  if (srna == &RNA_AnyType) {
    return true;
  }

  if (!type) {
    return false;
  }

  /* ptr->type is always maximally refined */
  for (base = type; base; base = base->base) {
    if (base == srna) {
      return true;
    }
  }

  return false;
}
ピン留めされたアイテム
nikogolinikogoli
CTX_py_dict_get の定義
source/blender/blenkernel/intern/context.c$224
void *CTX_py_dict_get(const bContext *C)
{
  return C->data.py_context;
}
bContextDataResult の定義
source/blender/blenkernel/intern/context.c
struct bContextDataResult {
  PointerRNA ptr;
  ListBase list;
  const char **dir;
  short type; /* 0: normal, 1: seq */
};

memset:初期化

ピン留めされたアイテム
nikogolinikogoli
BPY_context_member_get の定義 一部省略
source/blender/python/intern/bpy_interface.c$660
int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result)
{
  PyObject *pyctx;
  PyObject *item;
  PointerRNA *ptr = NULL;
  bool done = false;

  pyctx = (PyObject *)CTX_py_dict_get(C);
  item = PyDict_GetItemString(pyctx, member);

  if (item == NULL) {
    /* pass */
  }
  else if (item == Py_None) {
    done = true;
  }
  else if (BPy_StructRNA_Check(item)) {
    ptr = &(((BPy_StructRNA *)item)->ptr);
    CTX_data_pointer_set(result, ptr->owner_id, ptr->type, ptr->data);
    CTX_data_type_set(result, CTX_DATA_TYPE_POINTER);
    done = true;
  }
  else if (PySequence_Check(item)) {
    PyObject *seq_fast = PySequence_Fast(item, "bpy_context_get sequence conversion");
    if (seq_fast == NULL) {
      PyErr_Print();
      PyErr_Clear();
    }
    else {
      const int len = PySequence_Fast_GET_SIZE(seq_fast);
      PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
      int i;

      for (i = 0; i < len; i++) {
        PyObject *list_item = seq_fast_items[i];

        if (BPy_StructRNA_Check(list_item)) {
          ptr = &(((BPy_StructRNA *)list_item)->ptr);
          CTX_data_list_add(result, ptr->owner_id, ptr->type, ptr->data);
        }
        else {
          CLOG_INFO(BPY_LOG_CONTEXT,
                    1,
                    "'%s' list item not a valid type in sequence type '%s'",
                    member,
                    Py_TYPE(item)->tp_name);
        }
      }
      Py_DECREF(seq_fast);
      CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
      done = true;
    }
  }
nikogolinikogoli
source/blender/python/intern/bpy_rna.h$80
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
nikogolinikogoli
CTX_data_pointer_set の定義
source/blender/blenkernel/intern/context.c$638
void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, void *data)
{
  RNA_pointer_create(id, type, data, &result->ptr);
}
RNA_pointer_createの定義 コメント省略
source/blender/makesrna/intern/rna_access.c$146
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
{
  r_ptr->owner_id = id;
  r_ptr->type = type;
  r_ptr->data = data;

  if (data) {
    while (r_ptr->type && r_ptr->type->refine) {
      StructRNA *rtype = r_ptr->type->refine(r_ptr);

      if (rtype == r_ptr->type) {
        break;
      }
      r_ptr->type = rtype;
    }
  }
}
refine の定義 (抜粋)
source/blender/makesrna/intern/rna_internal_types.h$537
struct StructRNA *nested;

  /* function to give the more specific type */
  StructRefineFunc refine;
StructRefineFunc の定義
source/blender/makesrna/intern/rna_internal_types.h$58
typedef struct StructRNA *(*StructRefineFunc)(struct PointerRNA *ptr);

ポインタ値 ptrを引数にとって構造体StructRNAへのポインタへのポインタを返すもの?
PointerRNA -> type -> refineに何が入っているのか、が重要っぽい

nikogolinikogoli
PointerRNA の定義
source/blender/makesrna/RNA_types.h$49
typedef struct PointerRNA {
  struct ID *owner_id;
  struct StructRNA *type;
  void *data;
} PointerRNA;
CTX_data_type_set の定義
source/blender/makesrna/RNA_define.h$68
void CTX_data_type_set(bContextDataResult *result, short type)
{
  result->type = type;
}

PySequence_Fast:Python の関数、他のPySequence_Fast系の関数で利用できるような sequence or iterable な item へのポインタ値を返す (文字列はエラーメッセージ)

CTX_data_list_add の定義
source/blender/blenkernel/intern/context.c$651
void CTX_data_list_add(bContextDataResult *result, ID *id, StructRNA *type, void *data)
{
  CollectionPointerLink *link = MEM_callocN(sizeof(CollectionPointerLink), "CTX_data_list_add");
  RNA_pointer_create(id, type, data, &link->ptr);

  BLI_addtail(&result->list, link);
}
nikogolinikogoli
BLI_addtail の定義
source/blender/blenlib/intern/listbase.c$110
/**
 * Appends \a vlink (assumed to begin with a Link) onto listbase.
 */
void BLI_addtail(ListBase *listbase, void *vlink)
{
  Link *link = vlink;

  if (link == NULL) {
    return;
  }

  link->next = NULL;
  link->prev = listbase->last;

  if (listbase->last) {
    ((Link *)listbase->last)->next = link;
  }
  if (listbase->first == NULL) {
    listbase->first = link;
  }
  listbase->last = link;
}
MEM_callocN の定義 たぶん
source/blender/blenlib/intern/winstuff_dir.c$30
define MEM_callocN(size, str) ((void)str, calloc(size, 1))
ピン留めされたアイテム
nikogolinikogoli
CTX_RESULT_xxx の定義
source/blender/blenkernel/BKE_context.h$83
/typedef enum eContextResult {
  /* The context member was found, and its data is available. */
  CTX_RESULT_OK = 1,

  /* The context member was not found. */
  CTX_RESULT_MEMBER_NOT_FOUND = 0,

  /* The context member was found, but its data is not available.
   * For example, "active_bone" is a valid context member, but has not data in Object mode. */
  CTX_RESULT_NO_DATA = -1,
} eContextResult;
ListBase の定義
source/blender/makesdna/DNA_listBase.h$46
typedef struct ListBase {
  void *first, *last;
} ListBase;
nikogolinikogoli
bContext の定義
source/blender/blenkernel/intern/context.c
struct bContext {
  int thread;

  /* windowmanager context */
  struct {
    struct wmWindowManager *manager;
    struct wmWindow *window;
    struct WorkSpace *workspace;
    struct bScreen *screen;
    struct ScrArea *area;
    struct ARegion *region;
    struct ARegion *menu;
    struct wmGizmoGroup *gizmo_group;
    struct bContextStore *store;
    const char *operator_poll_msg; /* reason for poll failing */
  } wm;

  /* data context */
  struct {
    struct Main *main;
    struct Scene *scene;

    int recursion;
    /** True if python is initialized. */
    bool py_init;
    void *py_context;
    /**
     * If we need to remove members, do so in a copy
     * (keep this to check if the copy needs freeing).
     */
    void *py_context_orig;
  } data;
};
nikogolinikogoli
nikogolinikogoli

C とポインタ

  • s型 x 変数の定義:メモリ上に、s型の変数x用の領域を確保
  • & アドレス演算子:変数の左に付加すると、変数のポインタ値(アドレス値)を取得
  • * 間接参照演算子:ポインタ値の左に付加すると、ポインタ値が指す領域にある実体を取得
  • s型 *x ポインタ型変数の定義:メモリ上に「s型へのポインタ値」型の変数x用の領域を確保
      → &s型 xと(たぶん)解釈できる 定義しているのは*xではなくあくまでもx

コンパイラによる変換のルールと、ポインタと配列の性質 参考

  1. ary → &(ary[0]):配列変数が式に現れると、先頭要素のポインタ値に変換
  2. p[n] → *(p+n):ポインタに index がつくと、ポインタに対する加算命令+間接参照に変換

配列[index] ではこの2つの変換が連続して行われ、以下の流れで配列の要素にアクセスできる
 配列変数 →(変換+&)→ 先頭ポインタ →(変換+加算)→ index 部のポインタ →(*)→ 実体

nikogolinikogoli
  • constは再代入不可
  • structは構造体の型宣言 not 定義
  • 列挙型の中身はそのまま呼び出せる 「列挙型」を定義というより、定義を列挙している
  • 文字列を扱うには、char 型の配列を利用する他に、char 型のポインターを利用することもできる
  • 汎用ポインタvoid *:ポインタ型であればどのような型でも代入できる
nikogolinikogoli

こんなのを見つけた
https://blender.jp/modules/newbb/index.php?topic_id=1496

ただ、実際には C++ を通して python スクリプトを Blender に投げているという感じなので、Blender の C++ 部分をあれこれしているわけでなさそう

Blender の C に関する日本語情報は、2012~2014あたりには出されていたものの、2015以降は話題に上がらなくなった感じがある

このスクラップは2021/09/03にクローズされました