在说明我们这一章节想要讲什么,我们先来看一个例子:
(例子来源于zlmediakit中的MediaSource.cpp)
这个例子展示了递归查找map的一个模板元编程的例子,我们主要跟踪for_each_media_l
这个函数的前后调用。
//首先这里定义了一个层层嵌套的map,每个key都是std::string,后面的find函数是我们从这里层层嵌套的map中查询到我们想要的MediaSource,整个嵌套了4层,就是下暂时的schema,vhost,app,streama_id。
using Ptr = std::shared_ptr<MediaSource>;
using StreamMap = unordered_map<string/*strema_id*/, weak_ptr<MediaSource> >;
using AppStreamMap = unordered_map<string/*app*/, StreamMap>;
using VhostAppStreamMap = unordered_map<string/*vhost*/, AppStreamMap>;
using SchemaVhostAppStreamMap = unordered_map<string/*schema*/, VhostAppStreamMap>;
static recursive_mutex s_media_source_mtx;
static MediaSource::SchemaVhostAppStreamMap s_media_source_map;
MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost, const string &app, const string &id, bool from_mp4) {
return find_l(schema, vhost, app, id, from_mp4);
}
static MediaSource::Ptr find_l(const string &schema, const string &vhost_in, const string &app, const string &id, bool from_mp4) {
MediaSource::Ptr ret;
MediaSource::for_each_media([&](const MediaSource::Ptr &src) { ret = std::move(const_cast<MediaSource::Ptr &>(src)); }, schema, vhost, app, id);
return ret;
}
void MediaSource::for_each_media(const function<void(const Ptr &src)> &cb,
const string &schema,
const string &vhost,
const string &app,
const string &stream) {
deque<Ptr> src_list;
{
lock_guard<recursive_mutex> lock(s_media_source_mtx);
//printf("schema:%s,vhost:%s,app:%s,stream:%s\n", schema.c_str(), vhost.c_str(), app.c_str(), stream.c_str());
for_each_media_l(s_media_source_map, src_list, schema, vhost, app, stream);
}
for (auto &src : src_list) {
cb(src);
}
}
template<typename MAP, typename LIST, typename First, typename ...KeyTypes>
static void for_each_media_l(const MAP &map, LIST &list, const First &first, const KeyTypes &...keys) {
//如果key为空,这说明需要遍历全部的map。
if (first.empty()) {
for (auto &pr : map) {
for_each_media_l(pr.second, list, keys...);
}
return;
}
auto it = map.find(first);
if (it != map.end()) {
for_each_media_l(it->second, list, keys...);
}
}
//这个是递归终止函数
template<typename MAP, typename LIST, typename First>
static void for_each_media_l(const MAP &map, LIST &list, const First &first) {
if (first.empty()) {
for (auto &pr : map) {
emplace_back(list, pr.second);
}
return;
}
auto it = map.find(first);
if (it != map.end()) {
//递归找到了就放在list中
emplace_back(list, it->second);
}
}
template<typename LIST, typename Ptr>
static void emplace_back(LIST &list, const Ptr &ptr) {
auto src = ptr.lock();
if (src) {
list.emplace_back(std::move(src));
}
}
这个例子有一个非常详细的讲解:
链接:泛化之美–C++11可变模版参数的妙用