【MediaPipe】源码分析之:REGISTER_CALCULATOR宏

更新时间:2023-07-09 18:10:40 阅读: 评论:0

【MediaPipe】源码分析之:REGISTER_CALCULATOR宏REGISTER_CALCULATOR是MediaPipe中注册Calculator的宏,每个Calculator都通过它来注册的。Calculator是MediaPipe中的基本元素,其地位不⾔⽽喻。REGISTER_CALCULATOR功能强⼤,但是⽐较复杂:⽤了多层可变参数宏、多层可变参数模板,想要把它展开真⼼不是件容易的事。
它到底⼲了啥呢?本⽂以TfLiteConverterCalculator为例进⾏分析。
---------------------------------------------------------------------------------------------------------------------------------
【正⽂】
\mediapipe\mediapipe\calculators\tflite\tflite_converter_calculator:
..
class TfLiteConverterCalculator : public CalculatorBa {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
:
:mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Clo(CalculatorContext* cc) override;
..
};
REGISTER_CALCULATOR(TfLiteConverterCalculator);
洗衣机不脱水了是怎么回事::mediapipe::Status TfLiteConverterCalculator::Open(CalculatorContext* cc) {
..
}
::mediapipe::Status TfLiteConverterCalculator::Process(CalculatorContext* cc) {
..
}
::mediapipe::Status TfLiteConverterCalculator::Clo(CalculatorContext* cc) {
..
}
其中有宏如下:
REGISTER_CALCULATOR(TfLiteConverterCalculator);
#define REGISTER_CALCULATOR(name)                                          \
REGISTER_FACTORY_FUNCTION_QUALIFIED(::mediapipe::CalculatorBaRegistry, \
calculator_registration, name,      \
absl::make_unique<name>);            \
李荣浩个人资料REGISTER_FACTORY_FUNCTION_QUALIFIED(::mediapipe::internal::StaticAccessToCalculatorBaRegistry,                      \
access_registration, name,                                                        \
absl::make_unique<::mediapipe::internal::StaticAccessToCalculatorBaTyped<name>>)
分两段分析,先来前半段:
【1】
#define REGISTER_CALCULATOR(name)                                          \
REGISTER_FACTORY_FUNCTION_QUALIFIED(::mediapipe::CalculatorBaRegistry, \
calculator_registration, name,      \
absl::make_unique<name>);            \
分两点:1.REGISTER_FACTORY_FUNCTION_QUALIFIED;2.::mediapipe::CalculatorBaRegistry
【1.1】REGISTER_FACTORY_FUNCTION_QUALIFIED:
#define REGISTER_FACTORY_FUNCTION_QUALIFIED(RegistryType, var_name, name, ...) \
static auto* REGISTRY_STATIC_VAR(var_name, __LINE__) =                      \
new ::mediapipe::RegistrationToken(                                      \
RegistryType::Register(#name, __VA_ARGS__))
REGISTER_FACTORY_FUNCTION_QUALIFIED是⼀个可变参数宏,前三个必选,第4个开始可选,其中__VA_ARGS__代表第四个及后⾯的参数。然⽽,【1】的调⽤中传了4个实参,分别是:
::mediapipe::CalculatorBaRegistry,
calculator_registration,
name,
absl::make_unique<name>);
由此可知__VA_ARGS__实际上只有⼀个参数,它就是absl::make_unique<TfLiteConverterCalculator>
咦!这⾥的absl::make_unique是什么⿁?
先说absl,absl是Abil的缩写,是 Google 从代码库中抽取出的⼀组C++公共库。它是 Google 内部最基本的构建块,经过了充分的测试和优化,像 gRPC、Protobuf 和 TensorFlow 等很多项⽬都有应⽤。我们经常看到std::cout、std::vector等,并且知道它是标准模板库STL。类似地,absl::make_unique的意思就⽐较明显了:意思是构建⼀个独享智能指针。
absl::make_unique<TfLiteConverterCalculator>完全可以⽤std::make_unique<TfLiteConverterCalculator>来代替。
【1.1.1】REGISTRY_STATIC_VAR
#define REGISTRY_STATIC_VAR_INNER(var_name, line) var_name##_##line##__
#define REGISTRY_STATIC_VAR(var_name, line) \
REGISTRY_STATIC_VAR_INNER(var_name, line)
根据传进来的参数将REGISTRY_STATIC_VAR_INNER展开,则【1.1】的表达式为:
(REGISTER_CALCULATOR(TfLiteConverterCalculator)在tflite_converter_calculator中的第163⾏)祝你生日快乐歌词
#define REGISTER_FACTORY_FUNCTION_QUALIFIED(RegistryType, var_name, name, ...) \
static auto* calculator_registration_163__ =                      \
new ::mediapipe::RegistrationToken(                                      \
RegistryType::Register(#name, __VA_ARGS__))
这⾥定义了⼀个static变量calculator_registration_163__,类型为auto。auto并⾮真实的某⼀种类型,⽽是其类型是根据等号右边返回的数据来定。这⾥等号右边是new了⼀个RegistrationToken对象,因此calculator_registration_163__的类型就是RegistrationToken。
【calculator_registration_163__为什么要声明为static?】
这⾥有个⼩疑问:
calculator_registration_163__中的163是调⽤宏REGISTER_CALCULATOR所在⽂件中的的⾏数,那有没有这种可能:两个不同的Calculator分别声明在两个⽂件中,⽽调⽤这个宏所在的⾏数刚好相同呢?我的答案是:完全有这种可能!系统中⽤
REGISTER_CALCULATOR注册的Calculator越多,这种可能性越⼤!此时我们不禁要发问:声明两个相同名字的变量难道没有问题吗?
答案是:没有问题。原因是这个变量的类型是static,是⽂件专属的变量,如果把前⾯的static去掉,那么就会变成全局变量,那么编译会报错(变量重定义)。笔者亲⾃验证这个想法,把auto前⾯的static去掉:正能量激励人四字成语
果然编译报错!原来,⽂件tflite_tensors_to_detections_calculator的163⾏也调⽤了REGISTER_CALCULATOR这个宏:
冬至日
REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
这就很好地解释了calculator_registration_163__为什么要声明为static。
【1.1.2】new RegistrationToken(参数)
RegistrationToken定义在mediapipe\mediapipe\framework\deps\registration_token.h中:
class RegistrationToken {
public:
explicit RegistrationToken(std::function<void()> unregisterer);
// It is uful to have an empty constructor for when we want to declare a
// token, and assign it later.
RegistrationToken() {}
RegistrationToken(const RegistrationToken&) = delete;
大数据推广RegistrationToken& operator=(const RegistrationToken&) = delete;
RegistrationToken(RegistrationToken&& rhs);
RegistrationToken& operator=(RegistrationToken&& rhs);
.
.
};
此处很容易猜到调⽤的是其移动构造函数:RegistrationToken(RegistrationToken&& rhs);那么构造函数的参数的类型也就可以确定为RegistrationToken&&,这是⼀个右引⽤,如果不清楚什么是右引⽤,可点。
也就是说RegistryType::Register(#name, __VA_ARGS__)返回的类型是RegistrationToken&&
⽽根据【1】可知,这⾥的RegistryType是::mediapipe::CalculatorBaRegistry,⽽⼜有:
using CalculatorBaRegistry = GlobalFactoryRegistry<std::unique_ptr<CalculatorBa>>;
这⾥有⼏个重要信息:
1)CalculatorBaRegistry是⼀个GlobalFactoryRegistry为模板的模板类;流量和存量
2)这个模板类的实参类型是指向CalculatorBa的智能独享指针;
(注意,TfLiteConverterCalculator正是CalculatorBa的⼦类)
【1.1.3】RegistryType::Register(#name, __VA_ARGS__)
根据外层传进来的参数,可知,它实际是GlobalFactoryRegistry::Register()
GlobalFactoryRegistry定义在mediapipe\mediapipe\framework\deps\registration.h:
template <typename R, Args>
class GlobalFactoryRegistry {
using Functions = FunctionRegistry<R, >;
走出去public:
static RegistrationToken Register(const std::string& name,typename Functions::Function func) {
return functions()->Register(name, std::move(func));
}
...
/
/ Returns the factory function registry singleton.
static Functions* functions() {
static auto* functions = new Functions();
return functions;
}
...
};
只要记住,传到RegistryType::Register(name, func)的实参func是⼀个指向TfLiteConverterCalculator的智能指针。
【1.1.3.1】GlobalFactoryRegistry::Register()的形参
static RegistrationToken Register(const std::string& name,typename Functions::Function func)
两个疑问:1.这⾥的typename是什么⿁,为什么出现在这⾥?
2.Functions::Function func到底是什么?
对于typename,它原本是模板定义中引⼊的关键字,作⽤与class相似,但⽐class更强,模板定义中某些场景下表达的具体含义有歧义,⽆法判断⼀个表达式是指代⼀个变量还是指代⼀个数据类型,这时就要加上typename来指明该表达式是指⼀个数据类型。简单地说,这⾥的typename是为了指明Functions::Function是⼀个数据类型。。
Functions::Function有点复杂,相关代码简化如下:
template <typename R, Args>
class FunctionRegistry {
public:
using Function = std::function<)>;
...
}
.
..
template <typename R, Args>
class GlobalFactoryRegistry {
using Functions = FunctionRegistry<R, >;
public:
static RegistrationToken Register(const std::string& name,typename Functions::Function func) {
return functions()->Register(name, std::move(func));
}
...
}
template <typename R, Args>的写法叫可变参数模板;
GlobalFactoryRegistry和FunctionRegistry都是可变参数模板;
using的意思可以简单地解理为typedef,但⽐typedef更强;
GlobalFactoryRegistry和FunctionRegistry都有Functions的定义:using Functions = xxxxxx;但它们的含义不同。
可变参数模板,顾名思义,就是模板的参数个数是可变的(有点类似于可变参数函数printf),这⾥我们根据
using CalculatorBaRegistry = GlobalFactoryRegistry<std::unique_ptr<CalculatorBa>>;
可知GlobalFactoryRegistry的实参只有⼀个,就是std::unique_ptr<CalculatorBa>。
再看GlobalFactoryRegistry::Functions的定义:
using Functions = FunctionRegistry<R, >;
它是另⼀个可变参数模板FunctionRegistry 的别名,实参是R,也就是std::unique_ptr<CalculatorBa>
⽽FunctionRegistry ::Functions的定义:
using Function = std::function<)>;
将前⾯确定的实参R代⼊得:
using Function = std::function<std::unique_ptr<CalculatorBa>()>;
意思是Function是⼀个函数类型,该函数没有参数,返回std::unique_ptr<CalculatorBa>。
现在终于可以知道第⼆个问题了:2.Functions::Function func到底是什么?答案是:⼀个没有参数、返回⼀个指向CalculatorBa智能指针的函数。当然,这⾥要注意这个作案正确的前提是:
a)GlobalFactoryRegistry的实参个数是1
using CalculatorBaRegistry = GlobalFactoryRegistry<std::unique_ptr<CalculatorBa>>;
如果实参个数多于1个,那么Functions::Function所指代的函数则是有参数的,其参数为第2个及后⾯的实参。
b)GlobalFactoryRegistry的实参为std::unique_ptr<CalculatorBa>
using CalculatorBaRegistry = GlobalFactoryRegistry<std::unique_ptr<CalculatorBa>>;
如果模板GlobalFactoryRegistry的实参变成了其它,那么其返回的数据类型将变成对应的,例如:
using StaticAccessToCalculatorBaRegistry = GlobalFactoryRegistry<std::unique_ptr<StaticAccessToCalculatorBa>>;
那么Functions::Function func指的则是:⼀个没有参数、返回⼀个指向StaticAccessToCalculatorBa智能指针的函数。
【1.1.3.2】GlobalFactoryRegistry::Register()的实参
前⾯已经知道::Register(name, func)的实参func是⼀个指向TfLiteConverterCalculator的智能指针,即
absl::make_unique<TfLiteConverterCalculator>,也就是absl::unique<TfLiteConverterCalculator>。
现在问题来了,Register的形参是std::function<std::unique_ptr<CalculatorBa>()>,是⼀个函数指针,⽽实参是
absl::unique<TfLiteConverterCalculator>是⼀个对象指针,类型不同!这怎么⾏呢。

本文发布于:2023-07-09 18:10:40,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1087758.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:参数   模板   可变
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图