Cleanup: Main id looping: add FOREACH_MAIN_LISTBASE macro.

We don't want to use flow control like `break` statement into the basic
`FOREACH_MAIN_ID` macro, as this is a nested loop.

When refined behavior is needed (like breaking whole iteration, or just
skipping to next ID type), FOREACH_MAIN_LISTBASE and
FOREACH_MAIN_LISTBASE_ID macros should be used instead.

Based on D4382 by @campbellbarton
(Other potential solution, using flow control macros: D4384).
This commit is contained in:
2019-03-18 11:32:06 +01:00
parent 66932a2c81
commit d0e28721b0
4 changed files with 76 additions and 71 deletions

View File

@@ -152,32 +152,30 @@ struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
} \
} ((void)0)
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id) \
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb) \
{ \
ListBase *_lbarray[MAX_LIBARRAY]; \
int _i = set_listbasepointers(_bmain, _lbarray); \
while (_i--) { \
_lb = _lbarray[_i];
#define FOREACH_MAIN_LISTBASE_END \
} \
} ((void)0)
/* DO NOT use break statement with that macro, use FOREACH_MAIN_LISTBASE and FOREACH_MAIN_LISTBASE_ID instead
* if you need that kind of control flow. */
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id) \
{ \
ListBase *_lb; \
FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb) { \
FOREACH_MAIN_LISTBASE_ID_BEGIN(_lbarray[_i], _id)
#define FOREACH_MAIN_ID_END \
FOREACH_MAIN_LISTBASE_ID_END; \
} \
} FOREACH_MAIN_LISTBASE_END; \
} ((void)0)
/** \param _do_break A boolean, to allow breaking iteration (only used to break by type,
* you must also use an explicit `break;` operation if you want to
* immediately break from inner by-ID loop).
*/
#define FOREACH_MAIN_ID_BREAKABLE_BEGIN(_bmain, _id, _do_break) \
{ \
ListBase *_lbarray[MAX_LIBARRAY]; \
int i = set_listbasepointers(_bmain, _lbarray); \
while (i-- && !_do_break) { \
FOREACH_MAIN_LISTBASE_ID_BEGIN(_lbarray[i], _id) \
#define FOREACH_MAIN_ID_BREAKABLE_END FOREACH_MAIN_ID_END
struct BlendThumbnail *BKE_main_thumbnail_from_imbuf(struct Main *bmain, struct ImBuf *img);
struct ImBuf *BKE_main_thumbnail_to_imbuf(struct Main *bmain, struct BlendThumbnail *data);

View File

@@ -460,16 +460,21 @@ bool BKE_blendfile_read_from_memfile(
void BKE_blendfile_read_make_empty(bContext *C)
{
Main *bmain = CTX_data_main(C);
ListBase *lb;
ID *id;
FOREACH_MAIN_ID_BEGIN(bmain, id)
FOREACH_MAIN_LISTBASE_BEGIN(bmain, lb)
{
if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM, ID_WS)) {
break; /* Only breaks iter on that ID type, and continues with IDs of next type. */
FOREACH_MAIN_LISTBASE_ID_BEGIN(lb, id)
{
if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM, ID_WS)) {
break;
}
BKE_id_delete(bmain, id);
}
BKE_id_delete(bmain, id);
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_ID_END;
FOREACH_MAIN_LISTBASE_END;
}
/* only read the userdef from a .blend */

View File

@@ -1384,19 +1384,16 @@ void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag)
}
for (bool do_loop = true; do_loop; ) {
bool do_break = false;
do_loop = false;
FOREACH_MAIN_ID_BREAKABLE_BEGIN(bmain, id, do_break)
FOREACH_MAIN_ID_BEGIN(bmain, id)
{
/* We only want to check that ID if it is currently known as used... */
if ((id->tag & LIB_TAG_DOIT) == 0) {
BKE_library_foreach_ID_link(
bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY);
}
/* Else it is an unused ID (so far), no need to check it further. */
do_break = true;
break;
}
FOREACH_MAIN_ID_BREAKABLE_END;
FOREACH_MAIN_ID_END;
}
}

View File

@@ -159,6 +159,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
#else
Main *bmain = G_MAIN; /* XXX Ugly, but should work! */
#endif
ListBase *lb;
ID *id;
PyObject *subset = NULL;
@@ -223,57 +224,61 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
data_cb.types_bitmap = key_types_bitmap;
FOREACH_MAIN_ID_BEGIN(bmain, id)
FOREACH_MAIN_LISTBASE_BEGIN(bmain, lb)
{
/* We cannot skip here in case we have some filter on key types... */
if (key_types_bitmap == NULL && val_types_bitmap != NULL) {
if (!id_check_type(id, val_types_bitmap)) {
break; /* Break iter on that type of IDs, continues with next ID type. */
}
}
/* One-time init, ID is just used as placeholder here, we abuse this in iterator callback
* to avoid having to rebuild a complete bpyrna object each time for the key searching
* (where only ID pointer value is used). */
if (data_cb.py_id_key_lookup_only == NULL) {
data_cb.py_id_key_lookup_only = pyrna_id_CreatePyObject(id);
}
if (!data_cb.is_subset &&
/* We do not want to pre-add keys of flitered out types. */
(key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) &&
/* We do not want to pre-add keys when we have filter on value types, but not on key types. */
(val_types_bitmap == NULL || key_types_bitmap != NULL))
FOREACH_MAIN_LISTBASE_ID_BEGIN(lb, id)
{
PyObject *key = data_cb.py_id_key_lookup_only;
PyObject *set;
/* We cannot skip here in case we have some filter on key types... */
if (key_types_bitmap == NULL && val_types_bitmap != NULL) {
if (!id_check_type(id, val_types_bitmap)) {
break;
}
}
RNA_id_pointer_create(id, &((BPy_StructRNA *)key)->ptr);
/* One-time init, ID is just used as placeholder here, we abuse this in iterator callback
* to avoid having to rebuild a complete bpyrna object each time for the key searching
* (where only ID pointer value is used). */
if (data_cb.py_id_key_lookup_only == NULL) {
data_cb.py_id_key_lookup_only = pyrna_id_CreatePyObject(id);
}
/* We have to insert the key now, otherwise ID unused would be missing from final dict... */
if ((set = PyDict_GetItem(data_cb.user_map, key)) == NULL) {
/* Cannot use our placeholder key here! */
key = pyrna_id_CreatePyObject(id);
set = PySet_New(NULL);
PyDict_SetItem(data_cb.user_map, key, set);
Py_DECREF(set);
Py_DECREF(key);
if (!data_cb.is_subset &&
/* We do not want to pre-add keys of flitered out types. */
(key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) &&
/* We do not want to pre-add keys when we have filter on value types, but not on key types. */
(val_types_bitmap == NULL || key_types_bitmap != NULL))
{
PyObject *key = data_cb.py_id_key_lookup_only;
PyObject *set;
RNA_id_pointer_create(id, &((BPy_StructRNA *)key)->ptr);
/* We have to insert the key now, otherwise ID unused would be missing from final dict... */
if ((set = PyDict_GetItem(data_cb.user_map, key)) == NULL) {
/* Cannot use our placeholder key here! */
key = pyrna_id_CreatePyObject(id);
set = PySet_New(NULL);
PyDict_SetItem(data_cb.user_map, key, set);
Py_DECREF(set);
Py_DECREF(key);
}
}
if (val_types_bitmap != NULL && !id_check_type(id, val_types_bitmap)) {
continue;
}
data_cb.id_curr = id;
BKE_library_foreach_ID_link(NULL, id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_CB_NOP);
if (data_cb.py_id_curr) {
Py_DECREF(data_cb.py_id_curr);
data_cb.py_id_curr = NULL;
}
}
if (val_types_bitmap != NULL && !id_check_type(id, val_types_bitmap)) {
continue;
}
data_cb.id_curr = id;
BKE_library_foreach_ID_link(NULL, id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_CB_NOP);
if (data_cb.py_id_curr) {
Py_DECREF(data_cb.py_id_curr);
data_cb.py_id_curr = NULL;
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_ID_END;
FOREACH_MAIN_LISTBASE_ID_END;
ret = data_cb.user_map;