getService is a template function, this is the first implemnt
class ServiceCenter {
public:
template<typename T>
std::shared_ptr<T> getService() {
auto const key = typeid(T).name();
std::lock_guard<std::recursive_mutex> lock(_mutex);
auto const itr = _serviceMap.find(key);
if (itr == _serviceMap.end()) {
return nullptr;
}
auto service = itr->second;
return std::dynamic_pointer_cast<T>(service);
}
};
first, use typeid(T).name() to get the name, and use std::dynamic_pointer_cast to get safe smart pointer conversion. Looks like it support base class type. The result is a shared_ptr, so this ServiceCenter can be free in app life time, cool!
I'm a bit doubtful, typeid return an base class name
The map find has no relationship with type, so let it out
class ServiceCenter {
public:
std::shared_ptr<BaseService> getService(const std::string &key) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
auto const itr = _serviceMap.find(key);
if (itr == _serviceMap.end()) {
return nullptr;
}
return itr->second;
}
template<typename T>
std::shared_ptr<T> getService() {
auto const key = typeid(T).name();
auto service = getService(key);
return std::dynamic_pointer_cast<T>(service);
}
};
now getService is shorter than before. But what if we want create the service if not get
class ServiceCenter {
public:
template<typename T>
std::shared_ptr<T> getService() {
std::lock_guard<std::recursive_mutex> lock(_mutex);
auto const key = typeid(T).name();
auto const service = getService(key);
if (service == nullptr) {
auto const tService = std::make_shared<T>();
tService->contextName = _contextName;
setService(key, tService);
tService->onServiceInit();
return tService;
} else {
auto const tService = std::dynamic_pointer_cast<T>(service);
if (tService == nullptr) {
aerror("ServiceCenter", "tService is null");
return nullptr;
}
return tService;
}
}
void setService(const std::string &key, const std::shared_ptr<BaseService> &service) {....}
};
Yes, use auto const tService = std::make_shared<T>(); to create. But now this function a litter bigger, can we abstract more.
So far, there are 3 places use type T.
Pretty clear, we has a class ServiceTypeHelperBase, so there is can avoid type T.
Now we can move all these code into non-template function as before.
class ServiceCenter {
public:
std::shared_ptr<void> getService(const ServiceTypeHelperBase* helper) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
auto const key = helper->getTypeName();
auto const service = getService(key);
if (service == nullptr) {
auto const tService = helper->newInstance();
tService->contextName = _contextName;
setService(key, tService);
tService->onServiceInit();
return helper->castToOriginType(tService);
} else {
auto const tService = helper->castToOriginType(service);
if (tService == nullptr) {
aerror("ServiceCenter", "tService is null");
return nullptr;
}
return tService;
}
}
template<typename T>
std::shared_ptr<T> getService() {
ServiceTypeHelper<T> helper;
auto service = getService(&helper);
return std::static_pointer_cast<T>(service);
}
};
Fantastic, you may notice that ServiceTypeHelperBase.castToOriginType() return a std::shared_ptr<void>, we must use std::static_pointer_cast to convert back! Sometimes we need to compromise on type safety and speed.
Because ServiceCenter is not a singleton, If we has multi instance, template function will make every instance very big. We can upgrade template class function to template class, to make each object has smaller code segment
从入门到精通:如何解决C++模板代码膨胀问题?
All of the story start from this architecture
getService
is a template function, this is the first implemntfirst, use
typeid(T).name()
to get the name, and usestd::dynamic_pointer_cast
to get safe smart pointer conversion. Looks like it support base class type. The result is a shared_ptr, so thisServiceCenter
can be free in app life time, cool!The map find has no relationship with type, so let it out
now
getService
is shorter than before. But what if we want create the service if not getYes, use
auto const tService = std::make_shared<T>();
to create. But now this function a litter bigger, can we abstract more. So far, there are 3 places use type T.we make a haler class to do this jobs
Pretty clear, we has a class
ServiceTypeHelperBase
, so there is can avoid type T. Now we can move all these code into non-template function as before.Fantastic, you may notice that
ServiceTypeHelperBase.castToOriginType()
return astd::shared_ptr<void>
, we must usestd::static_pointer_cast
to convert back! Sometimes we need to compromise on type safety and speed.Because
ServiceCenter
is not a singleton, If we has multi instance, template function will make every instance very big. We can upgrade template class function to template class, to make each object has smaller code segmentAs you see, If we want make smaller code size, we must abstract many middle layers, reduce the compiler generate duplicate code, by a function invoke.