这个模块是EBus的具体实现,在这里将EBus需要各个模块的功能组合起来,实现一些完整的EBus功能。
一些预定义的数据结构
NullMutex
/**
* A dummy mutex that performs no locking.
* EBuses that do not support multithreading use this mutex
* as their EBusTraits::MutexType.
*/
struct NullMutex
{
void lock() {}
bool try_lock() { return true; }
void unlock() {}
};
无锁的EBus使用的Mutex。
NullBusId
/**
* Indicates that EBusTraits::BusIdType is not set.
* EBuses with multiple addresses must set the EBusTraits::BusIdType.
*/
struct NullBusId
{
NullBusId() {};
NullBusId(int) {};
};
/// @cond EXCLUDE_DOCS
inline bool operator==(const NullBusId&, const NullBusId&) { return true; }
inline bool operator!=(const NullBusId&, const NullBusId&) { return false; }
/// @endcond
/**
* Indicates that EBusTraits::BusIdOrderCompare is not set.
* EBuses with ordered address IDs must specify a function for
* EBusTraits::BusIdOrderCompare.
*/
struct NullBusIdCompare;
不使用Address功能的EBus使用的BusId和它的比较函数。
NullLockGuard
namespace Internal
{
// Lock guard used when there is a NullMutex on a bus, or during dispatch
// on a bus which supports LocklessDispatch.
template <class Lock>
struct NullLockGuard
{
explicit NullLockGuard(Lock&) {}
NullLockGuard(Lock&, AZStd::adopt_lock_t) {}
void lock() {}
bool try_lock() { return true; }
void unlock() {}
};
}
不使用Mutex时,没有任何功能的LockGuard。
EBusImplTraits
包含了EBusTraits的内部的各种数据结构。
template <class Interface, class BusTraits>
struct EBusImplTraits
{
/**
* Properties that you use to configure an EBus.
* For more information, see EBusTraits.
*/
using Traits = BusTraits;
/**
* Allocator used by the EBus.
* The default setting is AZStd::allocator, which uses AZ::SystemAllocator.
*/
using AllocatorType = typename Traits::AllocatorType;
/**
* The class that defines the interface of the EBus.
*/
using InterfaceType = Interface;
/**
* The events defined by the EBus interface.
*/
using Events = Interface;
/**
* The type of ID that is used to address the EBus.
* Used only when the address policy is AZ::EBusAddressPolicy::ById
* or AZ::EBusAddressPolicy::ByIdAndOrdered.
* The type must support `AZStd::hash<ID>` and
* `bool operator==(const ID&, const ID&)`.
*/
using BusIdType = typename Traits::BusIdType;
/**
* Sorting function for EBus address IDs.
* Used only when the AddressPolicy is AZ::EBusAddressPolicy::ByIdAndOrdered.
* If an event is dispatched without an ID, this function determines
* the order in which each address receives the event.
*
* The following example shows a sorting function that meets these requirements.
* @code{.cpp}
* using BusIdOrderCompare = AZStd::less<BusIdType>; // Lesser IDs first.
* @endcode
*/
using BusIdOrderCompare = typename Traits::BusIdOrderCompare;
/**
* Locking primitive that is used when connecting handlers to the EBus or executing events.
* By default, all access is assumed to be single threaded and no locking occurs.
* For multithreaded access, specify a mutex of the following type.
* - For simple multithreaded cases, use AZStd::mutex.
* - For multithreaded cases where an event handler sends a new event on the same bus
* or connects/disconnects while handling an event on the same bus, use AZStd::recursive_mutex.
*/
using MutexType = typename Traits::MutexType;
/**
* Contains all of the addresses on the EBus.
*/
using BusesContainer = AZ::Internal::EBusContainer<Interface, Traits>;
/**
* Locking primitive that is used when executing events in the event queue.
*/
using EventQueueMutexType = AZStd::conditional_t<AZStd::is_same<typename Traits::EventQueueMutexType, NullMutex>::value, // if EventQueueMutexType==NullMutex use MutexType otherwise EventQueueMutexType
MutexType, typename Traits::EventQueueMutexType>;
/**
* Pointer to an address on the bus.
*/
using BusPtr = typename BusesContainer::BusPtr;
/**
* Pointer to a handler node.
*/
using HandlerNode = typename BusesContainer::HandlerNode;
/**
* Specifies whether the EBus supports an event queue.
* You can use the event queue to execute events at a later time.
* To execute the queued events, you must call
* `<BusName>::ExecuteQueuedEvents()`.
* By default, the event queue is disabled.
*/
static constexpr bool EnableEventQueue = Traits::EnableEventQueue;
static constexpr bool EventQueueingActiveByDefault = Traits::EventQueueingActiveByDefault;
static constexpr bool EnableQueuedReferences = Traits::EnableQueuedReferences;
/**
* True if the EBus supports more than one address. Otherwise, false.
*/
static constexpr bool HasId = Traits::AddressPolicy != EBusAddressPolicy::Single;
/**
* The following Lock Guard classes are exposed so that it's possible to override them with custom lock/unlock functionality
* when using custom types for the EBus MutexType.
*/
/**
* Template Lock Guard class that wraps around the Mutex
* The EBus uses for Dispatching Events.
* This is not the EBus Context Mutex if LocklessDispatch is true
*/
template <typename DispatchMutex>
using DispatchLockGuard = typename Traits::template DispatchLockGuard<DispatchMutex, Traits::LocklessDispatch>;
/**
* Template Lock Guard class that protects connection / disconnection.
*/
template<typename ContextMutex>
using ConnectLockGuard = typename Traits::template ConnectLockGuard<ContextMutex>;
/**
* Template Lock Guard class that protects bind calls.
*/
template<typename ContextMutex>
using BindLockGuard = typename Traits::template BindLockGuard<ContextMutex>;
/**
* Template Lock Guard class that protects callstack tracking.
*/
template<typename ContextMutex>
using CallstackTrackerLockGuard = typename Traits::template CallstackTrackerLockGuard<ContextMutex>;
};
traits中定义了EBus中的各种数据类型,它们大都从模板参数BusTraits中获得。
EBusEventer
/**
* Dispatches events to handlers that are connected to a specific address on an EBus.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusEventer
{
/**
* The type of ID that is used to address the EBus.
* Used only when the address policy is AZ::EBusAddressPolicy::ById
* or AZ::EBusAddressPolicy::ByIdAndOrdered.
* The type must support `AZStd::hash<ID>` and
* `bool operator==(const ID&, const ID&)`.
*/
using BusIdType = typename Traits::BusIdType;
/**
* Pointer to an address on the bus.
*/
using BusPtr = typename Traits::BusPtr;
/**
* An event handler that can be attached to multiple addresses.
*/
using MultiHandler = typename Traits::BusesContainer::MultiHandler;
/**
* Acquires a pointer to an EBus address.
* @param[out] ptr A pointer that will point to the specified address
* on the EBus. An address lookup can be avoided by calling Event() with
* this pointer rather than by passing an ID, but that is only recommended
* for performance-critical code.
* @param id The ID of the EBus address that the pointer will be bound to.
*/
static void Bind(BusPtr& ptr, const BusIdType& id);
};
用于分发事件到连接至某个特定地址的EBus的handlers。但是这里只有一个Bind函数,具体的定义如下:
template <class Bus, class Traits>
inline void EBusEventer<Bus, Traits>::Bind(BusPtr& ptr, const BusIdType& id)
{
auto& context = Bus::GetOrCreateContext();
typename Traits::template BindLockGuard<decltype(context.m_contextMutex)> lock(context.m_contextMutex);
context.m_buses.Bind(ptr, id);
}
context.m_buses是一个EBusContainer,所以这里使用的是EBusContainer的Bind函数,它的功能是将这个id绑定到一个BusPtr,可以理解为给EBus赋予一个id。
这个struct实际的功能感觉和描述并不相符,它里面没有任何分发事件的行为,只有一个bind函数。
EBusEventEnumerator
用于寻找连接在某个EBus上的handler。
/**
* Provides functionality that requires enumerating over handlers that are connected
* to an EBus. It can enumerate over all handlers or just the handlers that are connected
* to a specific address on an EBus.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusEventEnumerator
{
/**
* The type of ID that is used to address the EBus.
* Used only when the address policy is AZ::EBusAddressPolicy::ById
* or AZ::EBusAddressPolicy::ByIdAndOrdered.
* The type must support `AZStd::hash<ID>` and
* `bool operator==(const ID&, const ID&)`.
*/
using BusIdType = typename Traits::BusIdType;
/**
* Pointer to an address on the bus.
*/
using BusPtr = typename Traits::BusPtr;
/**
* Finds the first handler that is connected to a specific address on the EBus.
* This function is only for special cases where you know that a particular
* component's handler is guaranteed to exist.
* Even if the returned pointer is valid (not null), it might point to a handler
* that was deleted. Prefer dispatching events using EBusEventer.
* @param id Address ID.
* @return A pointer to the first handler on the EBus, even if the handler
* was deleted.
*/
static typename Traits::InterfaceType* FindFirstHandler(const BusIdType& id);
/**
* Finds the first handler at a cached address on the EBus.
* This function is only for special cases where you know that a particular
* component's handler is guaranteed to exist.
* Even if the returned pointer is valid (not null), it might point to a handler
* that was deleted. Prefer dispatching events using EBusEventer.
* @param ptr Cached address ID.
* @return A pointer to the first handler on the specified EBus address,
* even if the handler was deleted.
*/
static typename Traits::InterfaceType* FindFirstHandler(const BusPtr& ptr);
/**
* Returns the total number of event handlers that are connected to a specific
* address on the EBus.
* @param id Address ID.
* @return The total number of handlers that are connected to the EBus address.
*/
static size_t GetNumOfEventHandlers(const BusIdType& id);
};
同样通过Traits来获取类型信息,其中有3个静态函数,两个FindFirstHandler功能是相同的,通过BusId/BusPtr来找到第一个连接在它们上面的Handler。GetNumOfEventHandlers计算连接到此EBus上的Handler上数量(它没有BusPtr版本,只有BusId版本)。
具体实现:
template <class Bus, class Traits>
typename Traits::InterfaceType * EBusEventEnumerator<Bus, Traits>::FindFirstHandler(const BusIdType& id)
{
typename Traits::InterfaceType* result = nullptr;
Bus::EnumerateHandlersId(id, [&result](typename Traits::InterfaceType* handler)
{
result = handler;
return false;
});
return result;
}
这里调用了EnumerateHandlersId函数,在BusContainer中出现过,它的功能是在该id的EBus上执行一个函数,就像for_each一样,这里的函数就是将它的hander赋值给result,并且第一次就返回false,实现拿到第一个handler就停止的目的。
GetNumOfEventHandlers实现:
template <class Bus, class Traits>
size_t EBusEventEnumerator<Bus, Traits>::GetNumOfEventHandlers(const BusIdType& id)
{
size_t size = 0;
Bus::EnumerateHandlersId(id, [&size](typename Bus::InterfaceType*)
{
++size;
return true;
});
return size;
}
同样使用了EnumerateHandlersId函数,对所有handler进行计数。
EBusBroadcaster
用于分发事件至所有handler,与EBusEventer向对,EBusEventer只分发至某个EBus上的handler。
/**
* Dispatches an event to all handlers that are connected to an EBus.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusBroadcaster
{
/**
* An event handler that can be attached to only one address at a time.
*/
using Handler = typename Traits::BusesContainer::Handler;
};
这里没有任何实现,看起来EBusBroadcaster和EBusEventer都不具备它们应有的功能,对应的功能位于EBusContainer中,这是未实现的设计?
EBusNullQueue
不能实现排序功能的EBus使用的数据结构。
/**
* Data type that is used when an EBus doesn't support queuing.
*/
struct EBusNullQueue
{
};
里面什么都没有。
EBusBroadcastQueue
EBusBroadcaster的升级版,按顺序广播一个事件至所有handler。
/**
* EBus functionality related to the queuing of events and functions.
* This is specifically for queuing events and functions that will
* be broadcast to all handlers on the EBus.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusBroadcastQueue
{
/**
* Executes queued events and functions.
* Execution will occur on the thread that calls this function.
* @see QueueBroadcast(), EBusEventQueue::QueueEvent(), QueueFunction(), ClearQueuedEvents()
*/
static void ExecuteQueuedEvents()
{
if (auto* context = Bus::GetContext())
{
context->m_queue.Execute();
}
}
/**
* Clears the queue without calling events or functions.
* Use in situations where memory must be freed immediately, such as shutdown.
* Use with care. Cleared queued events will never be executed, and those events might have been expected.
*/
static void ClearQueuedEvents()
{
if (auto* context = Bus::GetContext(false))
{
context->m_queue.Clear();
}
}
static size_t QueuedEventCount()
{
if (auto* context = Bus::GetContext(false))
{
return context->m_queue.Count();
}
return 0;
}
/**
* Sets whether function queuing is allowed.
* This does not affect event queuing.
* Function queuing is allowed by default when EBusTraits::EnableEventQueue
* is true. It is never allowed when EBusTraits::EnableEventQueue is false.
* @see QueueFunction, EBusTraits::EnableEventQueue
* @param isAllowed Set to true to allow function queuing. Otherwise, set to false.
*/
static void AllowFunctionQueuing(bool isAllowed) { Bus::GetOrCreateContext().m_queue.SetActive(isAllowed); }
/**
* Returns whether function queuing is allowed.
* @return True if function queuing is allowed. Otherwise, false.
* @see QueueFunction, AllowFunctionQueuing
*/
static bool IsFunctionQueuing()
{
auto* context = Bus::GetContext();
return context ? context->m_queue.IsActive() : Traits::EventQueueingActiveByDefault;
}
/**
* Helper to Queue a Broadcast only when function queueing is available
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void TryQueueBroadcast(Function&& func, InputArgs&& ... args);
/**
* Enqueues an asynchronous event to dispatch to all handlers.
* The event is not executed until ExecuteQueuedEvents() is called.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void QueueBroadcast(Function&& func, InputArgs&& ... args);
/**
* Helper to Queue a Reverse Broadcast only when function queueing is available
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void TryQueueBroadcastReverse(Function&& func, InputArgs&& ... args);
/**
* Enqueues an asynchronous event to dispatch to all handlers in reverse order.
* The event is not executed until ExecuteQueuedEvents() is called.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void QueueBroadcastReverse(Function&& func, InputArgs&& ... args);
/**
* Enqueues an arbitrary callable function to be executed asynchronously.
* The function is not executed until ExecuteQueuedEvents() is called.
* The function might be unrelated to this EBus or any handlers.
* Examples of callable functions are static functions, lambdas, and
* bound-member functions.
*
* One use case is to determine when a batch of queued events has finished.
* When the function is executed, we know that all events that were queued
* before the function have finished executing.
*
* @param func Callable function.
* @param args Arguments for `func`.
*/
template <class Function, class ... InputArgs>
static void QueueFunction(Function&& func, InputArgs&& ... args);
};
这个结构体的功能就是对EBusQueuePolicy的应用,它作为一个数据保存在EBus中,其中有一个队列message,其中保存了很多函数,Execute时顺序执行这些函数。
EBusBroadcastQueue执行所有异步事件,其中调用的m_queue.Execute()在EBusQueuePolicy中介绍过,就是顺序执行队列中的所有事件,它是线程安全的。
EBusBroadcastQueue中的许多函数功能是相似的,比如QueueBroadcast是将一个事件按顺序异步执行,QueueBroadcastReverse是将一个事件按反序异步执行,所以这里只详细分析它们的其中一个版本。这些函数加入的事件会在EBusBroadcastQueue被调用时执行。
所有的函数都依赖于核心函数QueueFunction,在注释中有解释,它的作用是将一个可调用的函数对象加入队列,在EBusBroadcastQueue执行它们,这个可调用的函数对象并不一定与当前EBus或者Handle关联,它可能是一个静态函数,lambda函数或者成员函数。
template <class Bus, class Traits>
template <class Function, class ... InputArgs>
inline void EBusBroadcastQueue<Bus, Traits>::QueueFunction(Function&& func, InputArgs&& ... args)
{
// 确认当前EBus支持Queue event功能
static_assert((AZStd::is_same<typename Bus::QueuePolicy::BusMessageCall, typename AZ::Internal::NullBusMessageCall>::value == false),
"This EBus doesn't support queued events! Check 'EnableEventQueue'");
auto& context = Bus::GetOrCreateContext(false);
if (context.m_queue.IsActive())
{
AZStd::scoped_lock<decltype(context.m_queue.m_messagesMutex)> messageLock(context.m_queue.m_messagesMutex);
// 这里将传入的func和args做了包装
context.m_queue.m_messages.push(typename Bus::QueuePolicy::BusMessageCall(
[func = AZStd::forward<Function>(func), args...]() mutable
{
AZStd::invoke(AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
},
typename Traits::AllocatorType()));
}
else
{
AZ_Warning("EBus", false, "Unable to queue function onto EBus. This may be due to a previous call to AllowFunctionQueuing(false)."
" Hint: This is often disabled during shutdown of a ComponentApplication");
}
}
这个函数的核心是将一个lambda和一个分配器加入队列,lambda函数的类型是Bus::QueuePolicy::BusMessageCall,在EBusQueuePolicy有它的定义:
typedef AZStd::function<void()> BusMessageCall;
就是一个不接受任何参数,也不返回任何值的函数,这里构造的lambda函数也是这个形式。然后lambda函数捕获了传入的func和args,并在其中调用它们(使用std::invoke)。经过这样的包装后,不论什么形式的函数都可以传进来。第二个参数是typename Traits::AllocatorType(),一个内存分配器,因为EBusQueuePolicy中的队列支持自定义内存分配器,这里使用的是EBus中的内存分配器。
有了这个函数,就可以实现其他的加入队列的功能了,比如:
EBusBroadcastQueue
template <class Bus, class Traits>
template <class Function, class ... InputArgs>
inline void EBusBroadcastQueue<Bus, Traits>::QueueBroadcast(Function&& func, InputArgs&& ... args)
{
Internal::QueueFunctionArgumentValidator<Function, Traits::EnableQueuedReferences>::Validate();
using Broadcaster = void(*)(Function&&, InputArgs&&...);
Bus::QueueFunction(static_cast<Broadcaster>(&Bus::Broadcast), AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
}
这个函数可以在EBus上广播Function事件,它第一步先验证参数,使用的是QueueFunctionArgumentValidator,看下它的实现。
QueueFunctionArgumentValidator
template <class Function, bool AllowQueuedReferences>
struct QueueFunctionArgumentValidator
{
static void Validate() {}
};
template <class Function>
struct ArgumentValidatorHelper
{
constexpr static void Validate()
{
ValidateHelper(AZStd::make_index_sequence<AZStd::function_traits<Function>::num_args>());
}
template<typename T>
using is_non_const_lvalue_reference = AZStd::integral_constant<bool, AZStd::is_lvalue_reference<T>::value && !AZStd::is_const<AZStd::remove_reference_t<T>>::value>;
template <size_t... ArgIndices>
constexpr static void ValidateHelper(AZStd::index_sequence<ArgIndices...>)
{
static_assert(!AZStd::disjunction_v<is_non_const_lvalue_reference<AZStd::function_traits_get_arg_t<Function, ArgIndices>>...>,
"It is not safe to queue a function call with non-const lvalue ref arguments");
}
};
template <class Function>
struct QueueFunctionArgumentValidator<Function, false>
{
using Validator = ArgumentValidatorHelper<Function>;
constexpr static void Validate()
{
Validator::Validate();
}
};
QueueFunctionArgumentValidator默认是没有实现的,只有一个空函数Validate。
当QueueFunctionArgumentValidator指定第二个模板参数为false时,表示函数不允许传递引用作为参数,此时它才有实现。
ValidateHelper用来验证这个函数的参数是否都为常量左值引用,如果不是则编译报错,其中使用了is_non_const_lvalue_reference来判断参数类型。
这个结构体使用了大量的模板traits,它们的实现暂时不深入研究,现在确定的是这个QueueFunctionArgumentValidator功能,它用来验证一个函数传入的参数,如果将第二个模板参数AllowQueuedReferences设置为false,它会保证传入此函数的参数必须为常量左值引用(直接传值应该也能匹配?),否则编译就会出错。
再回头看EBusBroadcastQueue的实现,使用QueueFunctionArgumentValidator完成验证后,后面就很简单了,代码片段:
using Broadcaster = void(*)(Function&&, InputArgs&&...);
Bus::QueueFunction(static_cast<Broadcaster>(&Bus::Broadcast), AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
它调用QueueFunction并传入了三个参数,通过QueueFunction的定义可以知道,QueueFunction接收多个参数,并将第一个参数作为可调用的函数,后面的参数作为这个函数的参数。所以这里的第一个参数是Broadcaster,它也是一个接收多个参数的函数,并且没有返回值,它由Bus::Broadcast转换而来,Bus::Broadcast定义在EBusContainer中,在Dispatcher里面,其格式与这里定义的Broadcaster相同。
所以这里的调用逻辑是
QueueFunction(Broadcaster, func, args...)
// |
// |
// \|/
Broadcaster(func, args...)
// |
// |
// \|/
std::invoke(func, args...)
这样就成功调用了func函数并传入args。
TryQueueBroadcast
template <class Bus, class Traits>
template <class Function, class ... InputArgs>
inline void EBusBroadcastQueue<Bus, Traits>::TryQueueBroadcast(Function&& func, InputArgs&& ... args)
{
if (EBusEventQueue<Bus, Traits>::IsFunctionQueuing())
{
EBusEventQueue<Bus, Traits>::QueueBroadcast(AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
}
}
在QueueBroadcast外做了一次验证,保证此EBus开启了FunctionQueuing功能。
QueueBroadcastReverse
template <class Bus, class Traits>
template <class Function, class ... InputArgs>
inline void EBusBroadcastQueue<Bus, Traits>::QueueBroadcastReverse(Function&& func, InputArgs&& ... args)
{
Internal::QueueFunctionArgumentValidator<Function, Traits::EnableQueuedReferences>::Validate();
using Broadcaster = void(*)(Function&&, InputArgs&&...);
Bus::QueueFunction(static_cast<Broadcaster>(&Bus::BroadcastReverse), AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
}
QueueBroadcast反序版本,将Bus::Broadcast换成了Bus::BroadcastReverse,它也是在EBusContainer中定义的。
EBusEventQueue
/**
* Enqueues asynchronous events to dispatch to handlers that are connected to
* a specific address on an EBus.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusEventQueue
: public EBusBroadcastQueue<Bus, Traits>
{
/**
* The type of ID that is used to address the EBus.
* Used only when the address policy is AZ::EBusAddressPolicy::ById
* or AZ::EBusAddressPolicy::ByIdAndOrdered.
* The type must support `AZStd::hash<ID>` and
* `bool operator==(const ID&, const ID&)`.
*/
using BusIdType = typename Traits::BusIdType;
/**
* Pointer to an address on the bus.
*/
using BusPtr = typename Traits::BusPtr;
/**
* Helper to queue an event by BusIdType only when function queueing is enabled
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void TryQueueEvent(const BusIdType& id, Function&& func, InputArgs&& ... args);
/**
* Enqueues an asynchronous event to dispatch to handlers at a specific address.
* The event is not executed until ExecuteQueuedEvents() is called.
* @param id Address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void QueueEvent(const BusIdType& id, Function&& func, InputArgs&& ... args);
/**
* Helper to queue an event by BusPtr only when function queueing is enabled
* @param ptr Cached address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void TryQueueEvent(const BusPtr& ptr, Function&& func, InputArgs&& ... args);
/**
* Enqueues an asynchronous event to dispatch to handlers at a cached address.
* The event is not executed until ExecuteQueuedEvents() is called.
* @param ptr Cached address ID. Handlers that are connected to this ID will receive the event.
* @param func Function pointer of the event to dispatch.
* @param args Function arguments that are passed to each handler.
*/
template <class Function, class ... InputArgs>
static void QueueEvent(const BusPtr& ptr, Function&& func, InputArgs&& ... args);
...
};
继承了EBusBroadcastQueue的特殊版本,之前是广播,这里是将事件发送给指定id上的Handler,通过指定BusId或BusPtr进行定位r,其中提供的函数与EBusBroadcastQueue相似,这里就看一下QueueEvent的实现。
template <class Bus, class Traits>
template <class Function, class ... InputArgs>
inline void EBusEventQueue<Bus, Traits>::QueueEvent(const BusIdType& id, Function&& func, InputArgs&& ... args)
{
Internal::QueueFunctionArgumentValidator<Function, Traits::EnableQueuedReferences>::Validate();
using Eventer = void(*)(const BusIdType&, Function&&, InputArgs&&...);
Bus::QueueFunction(static_cast<Eventer>(&Bus::Event), id, AZStd::forward<Function>(func), AZStd::forward<InputArgs>(args)...);
}
和EBusBroadcastQueue几乎是一样的,区别就是传递了Bus::Event,它在EBusContainer中定义,需要一个参数id或BusPtr来指定可以接收到事件的Handler。
EBusBroadcastEnumerator
/**
* Provides functionality that requires enumerating over all handlers that are
* connected to an EBus.
* To enumerate over handlers that are connected to a specific address
* on the EBus, use a function from EBusEventEnumerator.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusBroadcastEnumerator
{
/**
* Finds the first handler that is connected to the EBus.
* This function is only for special cases where you know that a particular
* component's handler is guaranteed to exist.
* Even if the returned pointer is valid (not null), it might point to a handler
* that was deleted. Prefer dispatching events using EBusEventer.
* @return A pointer to the first handler on the EBus, even if the handler
* was deleted.
*/
static typename Traits::InterfaceType* FindFirstHandler();
};
与EBusEventEnumerator对应的广播版本,其中只有一个函数FindFirstHandler,用来寻找第一个连接到此EBus的Handler。
template <class Bus, class Traits>
typename Traits::InterfaceType * EBusBroadcastEnumerator<Bus, Traits>::FindFirstHandler()
{
typename Traits::InterfaceType* result = nullptr;
Bus::EnumerateHandlers([&result](typename Traits::InterfaceType* handler)
{
result = handler;
return false;
});
return result;
}
同样使用EnumerateHandlers执行一个lambda来寻找handler,实现方式与EBusEventEnumerator的相同。
EventDispatcher
// This alias is required because you're not allowed to inherit from a nested type.
template <typename Bus, typename Traits>
using EventDispatcher = typename Traits::BusesContainer::template Dispatcher<Bus>;
使用的就是EBusContainer中的Dispatcher,其中有Event和Broadcast等函数,实现了EBus的事件分发功能。
EBusImpl
上面介绍了许多结构体,真正的EBusImpl就是通过组合这些结构体的功能来实现的。
/**
* Base class that provides eventing, queueing, and enumeration functionality
* for EBuses that dispatch events to handlers. Supports accessing handlers
* that are connected to specific addresses.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
* @tparam BusIdType The type of ID that is used to address the EBus.
*/
template <class Bus, class Traits, class BusIdType>
struct EBusImpl
: public EventDispatcher<Bus, Traits>
, public EBusBroadcaster<Bus, Traits>
, public EBusEventer<Bus, Traits>
, public EBusEventEnumerator<Bus, Traits>
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusEventQueue<Bus, Traits>, EBusNullQueue>
{
};
基础的EBusImpl,结合了EventDispatcher,EBusBroadcaster,EBusEventer,EBusEventEnumerator功能,再根据Traits::EnableEventQueue是否为true选择继承EBusEventQueue或EBusNullQueue。
这里继承了EventDispatcher,所以上面才可以以Bus::Broadcast的形式调用EventDispatcher(EBusContainer中的Dispatcher)的Broadcast函数。
EBusImpl还有一个特化版本:
/**
* Base class that provides eventing, queueing, and enumeration functionality
* for EBuses that dispatch events to all of their handlers.
* For a base class that can access handlers at specific addresses, use EBusImpl.
* @tparam Bus The EBus type.
* @tparam Traits A class that inherits from EBusTraits and configures the EBus.
* This parameter may be left unspecified if the `Interface` class
* inherits from EBusTraits.
*/
template <class Bus, class Traits>
struct EBusImpl<Bus, Traits, NullBusId>
: public EventDispatcher<Bus, Traits>
, public EBusBroadcaster<Bus, Traits>
, public EBusBroadcastEnumerator<Bus, Traits>
, public AZStd::conditional_t<Traits::EnableEventQueue, EBusBroadcastQueue<Bus, Traits>, EBusNullQueue>
{
using EBusBroadcastEnumerator<Bus, Traits>::FindFirstHandler;
static typename Traits::InterfaceType* FindFirstHandler(const NullBusId&)
{
// Invoke the EBusBroadcastEnumerator FindFirstHandler function
// Since this EBus doesn't use a BusId, the argument isn't needed
return FindFirstHandler();
}
static typename Traits::InterfaceType* FindFirstHandler(const typename Traits::BusPtr&)
{
// Invoke the EBusBroadcastEnumerator FindFirstHandler function
// Since this EBus doesn't use a BusId, the argument isn't needed
return FindFirstHandler();
}
};
用来实现无Address的EBus版本,它的第三个模板参数为NullBusId。
同时,这里定义了新的FindFirstHandler,由于是没有Address的EBus,所以显式指定FindFirstHandler使用EBusBroadcastEnumerator中的函数,从代码中也可以看出,传入的参数NullBusId和Traits::BusPtr是没有使用的。
总结
EBusImpl是EBus的核心实现部分,它包含以下部件
- EBusEventer和EBusBroadcaster,用于分发事件,但是没有具体实现,推测是后续的更新方向。
- EBusEventEnumerator和EBusBroadcastEnumerator,用户枚举EBus上的Handler,目前的用法只有寻找EBus上的第一个Handler和统计Handler的数量。
- EBusNullQueue,EBusBroadcastQueue和EBusEventQueue,用于将一个事件(函数)加入到执行队列中,后面可以在合适的时机顺序执行。这样EBus有了异步执行的功能。
- EventDispatcher,是BusContainer的Dispatcher的别名,其中实现了EBus最重要的Broadcast和Event函数,推测是一个需要废弃的部件,它的功能应该移动到EBusEventer和EBusBroadcaster中。(也有可能是发现Broadcast和Event函数离开BusContainer实现不了?)