#pragma once #include "V8Tree/Instance.h" #include #include namespace RBX { // A Service is an instance that is "Singleton" in the scope of its containing // ServiceProvider. In other words, a Service-derived class is unique within the // child tree of a ServiceProvider. class Service { public: const bool isPublic; protected: Service(bool isPublic=true) :isPublic(isPublic) { } ~Service() { } }; // Design decision: ServiceProvider liberally uses mutable members and declares const // member functions that change data. The idea here is that a ServiceProvider doesn't really // change fundamentally when creating and providing Services. It lets clients get a service // when const // TODO: consider the relative merits of returning Services as shared_ptr: safer, but more likely to lead to ptr leaks? extern const char* const sServiceProvider; class ServiceProvider : public DescribedNonCreatable { private: typedef DescribedNonCreatable Super; static Reflection::BoundFuncDesc(std::string)> func_FindService; static Reflection::BoundFuncDesc(std::string)> func_GetService; static Reflection::BoundFuncDesc(std::string)> dep_service; static Reflection::BoundFuncDesc(std::string)> dep_GetService; typedef std::vector< shared_ptr > ServiceArray; mutable ServiceArray serviceArray; mutable std::map > serviceMap; public: rbx::signal closingSignal; rbx::signal closingLateSignal; rbx::signal service)> serviceAddedSignal; rbx::signal service)> serviceRemovingSignal; ServiceProvider(); ServiceProvider(const char* name); template ServiceClass* find() const { BOOST_STATIC_ASSERT((boost::is_base_of::value)); BOOST_STATIC_ASSERT((boost::is_base_of::value)); size_t index = getClassIndex(); ServiceClass* service; if (index+1>serviceArray.size()) { // serviceArray has an empty entry for this service now serviceArray.resize(index+1, shared_ptr()); service = NULL; } else { service = static_cast(serviceArray[index].get()); if (service!=NULL) return service; } if (ServiceClass::isNullClassName()) return NULL; // See if the service is in the className map shared_ptr i = findServiceByClassName(ServiceClass::className()); if (!i) return NULL; service = boost::polymorphic_downcast(i.get()); serviceArray[index] = i; return service; } //mutable RBX::reentrant_concurrency_catcher threadGuard; template ServiceClass* create() const { //RBX::reentrant_concurrency_catcher::scoped_lock lock(threadGuard); ServiceClass* service = this->find(); if (service==NULL) { // If all else fails, create the service and put it in the table and map shared_ptr s = Creatable::create(); service = s.get(); // setParent can throw, so do not mutate any internal state until // setParent has returned without throwing. service->setAndLockParent(const_cast(this)); size_t index = getClassIndex(); serviceArray[index] = s; // By now onDescendantAdded will have been called, which will add the service to serviceMap: RBXASSERT((ServiceClass::className()==RBX::Name::getNullName()) || serviceMap.find(&ServiceClass::className())!=serviceMap.end()); } return service; } static const ServiceProvider* findServiceProvider(const Instance* context) { if (const Instance* root = Instance::getRootAncestor(context)) { if (const ServiceProvider* serviceProvider = Instance::fastDynamicCast(root)) { return serviceProvider; } } return NULL; } template static ServiceClass* find(const Instance* context) { if (const ServiceProvider* serviceProvider = findServiceProvider(context)) return serviceProvider->find(); return NULL; } template static ServiceClass* find(const ServiceProvider* serviceProvider) { if (serviceProvider!=NULL) return serviceProvider->find(); return NULL; } template static ServiceClass* create(const Instance* context) { if (const ServiceProvider* serviceProvider = findServiceProvider(context)) return serviceProvider->create(); return NULL; } template static ServiceClass* create(const ServiceProvider* serviceProvider) { if (serviceProvider!=NULL) return serviceProvider->create(); return NULL; } // Less efficient factory functions that use classNames. Use only if // you don't know the type of Service you want. static shared_ptr create(Instance* context, const RBX::Name& name); shared_ptr getPublicServiceByClassNameString(std::string name); protected: /*override*/ shared_ptr createChild(const RBX::Name& className, RBX::CreatorRole creatorRole); /*override*/ void onDescendantRemoving(const shared_ptr& instance); /*override*/ void onDescendantAdded(Instance* instance); /*override*/ void onChildAdded(Instance* child); /*override*/ void onChildRemoving(Instance* child); /* override */ bool askAddChild(const Instance* instance) const { return dynamic_cast(instance) != NULL; } void clearServices(); private: // Returns a new number each time it is called (starting at 0) static size_t newIndex(); template static size_t doGetClassIndex() { static size_t index = newIndex(); return index; } template static void callDoGetClassIndex() { doGetClassIndex(); } shared_ptr findServiceByClassName(const RBX::Name& className) const; shared_ptr findPublicServiceByClassNameString(std::string name); private: template static size_t getClassIndex() { static boost::once_flag flag = BOOST_ONCE_INIT; boost::call_once(&callDoGetClassIndex, flag); return doGetClassIndex(); } }; // A convenience class. Given an Instance* context, it will find the Service. You can treat it // like a Service*. Please note that it might be NULL if the context isn't a child of a ServiceProvider (like DataModel) template class ServiceClient { Instance* context; mutable shared_ptr service; public: ServiceClient(Instance* context) :context(context) { } bool isNull() const { return findService()==NULL; } operator S*() { return createService(); } operator const S*() const { return createService(); } const S* operator->() const { return createService(); } S* operator->() { return createService(); } private: S* findService() const { if (!service) service = shared_from(ServiceProvider::find(context)); return service.get(); } S* createService() const { if (!service) service = shared_from(ServiceProvider::create(context)); return service.get(); } }; }