@@ -24100,6 +24100,151 @@ struct each_invoker : public invoker {
2410024100 Func m_func;
2410124101};
2410224102
24103+ template <typename Func, typename ... Components>
24104+ struct find_invoker : public invoker {
24105+ // If the number of arguments in the function signature is one more than the
24106+ // number of components in the query, an extra entity arg is required.
24107+ static constexpr bool PassEntity =
24108+ (sizeof...(Components) + 1) == (arity<Func>::value);
24109+
24110+ // If the number of arguments in the function is two more than the number of
24111+ // components in the query, extra iter + index arguments are required.
24112+ static constexpr bool PassIter =
24113+ (sizeof...(Components) + 2) == (arity<Func>::value);
24114+
24115+ static_assert(arity<Func>::value > 0,
24116+ "each() must have at least one argument");
24117+
24118+ using Terms = typename term_ptrs<Components ...>::array;
24119+
24120+ template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
24121+ explicit find_invoker(Func&& func) noexcept
24122+ : m_func(FLECS_MOV(func)) { }
24123+
24124+ explicit find_invoker(const Func& func) noexcept
24125+ : m_func(func) { }
24126+
24127+ // Invoke object directly. This operation is useful when the calling
24128+ // function has just constructed the invoker, such as what happens when
24129+ // iterating a query.
24130+ void invoke(ecs_iter_t *iter) const {
24131+ term_ptrs<Components...> terms;
24132+
24133+ if (terms.populate(iter)) {
24134+ invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms);
24135+ } else {
24136+ invoke_callback< each_column >(iter, m_func, 0, terms.m_terms);
24137+ }
24138+ }
24139+
24140+ // Find invokers always use instanced iterators
24141+ static bool instanced() {
24142+ return true;
24143+ }
24144+
24145+ private:
24146+ // Number of function arguments is one more than number of components, pass
24147+ // entity as argument.
24148+ template <template<typename X, typename = int> class ColumnType,
24149+ typename... Args, if_t<
24150+ sizeof...(Components) == sizeof...(Args) && PassEntity> = 0>
24151+ static flecs::entity invoke_callback(
24152+ ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
24153+ {
24154+ ECS_TABLE_LOCK(iter->world, iter->table);
24155+
24156+ ecs_world_t *world = iter->world;
24157+ size_t count = static_cast<size_t>(iter->count);
24158+
24159+ ecs_assert(count > 0, ECS_INVALID_OPERATION,
24160+ "no entities returned, use find() without flecs::entity argument");
24161+
24162+ for (size_t i = 0; i < count; i ++) {
24163+ if (func(flecs::entity(world, iter->entities[i]),
24164+ (ColumnType< remove_reference_t<Components> >(comps, i)
24165+ .get_row())...))
24166+ {
24167+ return flecs::entity(world, iter->entities[i]);
24168+ }
24169+ }
24170+
24171+ ECS_TABLE_UNLOCK(iter->world, iter->table);
24172+
24173+ return flecs::entity();
24174+ }
24175+
24176+ // Number of function arguments is two more than number of components, pass
24177+ // iter + index as argument.
24178+ template <template<typename X, typename = int> class ColumnType,
24179+ typename... Args, int Enabled = PassIter, if_t<
24180+ sizeof...(Components) == sizeof...(Args) && Enabled> = 0>
24181+ static flecs::entity invoke_callback(
24182+ ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
24183+ {
24184+ size_t count = static_cast<size_t>(iter->count);
24185+ if (count == 0) {
24186+ // If query has no This terms, count can be 0. Since each does not
24187+ // have an entity parameter, just pass through components
24188+ count = 1;
24189+ }
24190+
24191+ flecs::iter it(iter);
24192+
24193+ ECS_TABLE_LOCK(iter->world, iter->table);
24194+
24195+ for (size_t i = 0; i < count; i ++) {
24196+ if (func(it, i, (ColumnType< remove_reference_t<Components> >(comps, i)
24197+ .get_row())...))
24198+ {
24199+ return flecs::entity(world, iter->entities[i]);
24200+ }
24201+ }
24202+
24203+ ECS_TABLE_UNLOCK(iter->world, iter->table);
24204+
24205+ return flecs::entity();
24206+ }
24207+
24208+ // Number of function arguments is equal to number of components, no entity
24209+ template <template<typename X, typename = int> class ColumnType,
24210+ typename... Args, if_t<
24211+ sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0>
24212+ static flecs::entity invoke_callback(
24213+ ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
24214+ {
24215+ size_t count = static_cast<size_t>(iter->count);
24216+ if (count == 0) {
24217+ // If query has no This terms, count can be 0. Since each does not
24218+ // have an entity parameter, just pass through components
24219+ count = 1;
24220+ }
24221+
24222+ flecs::iter it(iter);
24223+
24224+ ECS_TABLE_LOCK(iter->world, iter->table);
24225+
24226+ for (size_t i = 0; i < count; i ++) {
24227+ if (func( (ColumnType< remove_reference_t<Components> >(comps, i)
24228+ .get_row())...))
24229+ {
24230+ return flecs::entity(world, iter->entities[i]);
24231+ }
24232+ }
24233+
24234+ ECS_TABLE_UNLOCK(iter->world, iter->table);
24235+ }
24236+
24237+ template <template<typename X, typename = int> class ColumnType,
24238+ typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
24239+ static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
24240+ size_t index, Terms& columns, Args... comps)
24241+ {
24242+ return invoke_callback<ColumnType>(
24243+ iter, func, index + 1, columns, comps..., columns[index]);
24244+ }
24245+
24246+ Func m_func;
24247+ };
2410324248
2410424249////////////////////////////////////////////////////////////////////////////////
2410524250//// Utility class to invoke a system iterate action
@@ -24480,6 +24625,12 @@ struct iterable {
2448024625 this->next_each_action());
2448124626 }
2448224627
24628+ template <typename Func>
24629+ flecs::entity find(Func&& func) const {
24630+ iterate<_::find_invoker>(nullptr, FLECS_FWD(func),
24631+ this->next_each_action());
24632+ }
24633+
2448324634 /** Iter iterator.
2448424635 * The "iter" iterator accepts a function that is invoked for each matching
2448524636 * table. The following function signatures are valid:
0 commit comments