刺探matter 源代码架构2
授人以渔: 看陌生代码第一思路
c++代码相对于c语言,有面向对象加持,可以个人维护代码量为10w行。
如果想看一个复杂对象, 怎么去快速了解它的代码结构呢? 有两个入口点, 一个是变量,一个是函数。 扫一眼, 看数量。 如果变量数量少, 就看看他保存了哪些信息。 与这些信息相关的管理任务,肯定要在这个对象里。 如果函数数量少, 就看他提供了哪些功能。 与这些功能相关的管理任务,肯定要在这个对象里。 没看的代码,可以猜,以最好的实现赋予它。
详解: 变量少的对象, 有可能提供了很多细致的功能, 实际上是语法糖。 包装的变量少。 要记住那么多语法糖,没有意义。 也有一些业务,逻辑非常复杂, 但是抽象出来的接口不多, 看接口更容易理解。
matter 管理不同设备的一个例子
文件夹命名 src/platform
命名空间:Chip::DeviceLayer 这个逻辑让我很困惑, java都强制要求包名和文件夹名一致。这些是影响开发效率的。
平台是管理设备的, 有一个重要的类是ConfigurationManager, 每次调用都使用单例模式,ConfigurationMgr(), 初始化函数为SetConfigurationMgr
(venv310) icecut@jiangyiundeMBP2 src % grep -rn "SetConfigurationMgr" *
credentials/tests/TestFabricTable.cpp:3051: DeviceLayer::SetConfigurationMgr(&DeviceLayer::ConfigurationManagerImpl::GetDefaultInstance());
include/platform/ConfigurationManager.h:204:void SetConfigurationMgr(ConfigurationManager * configurationManager);
platform/nxp/k32w/k32w1/PlatformManagerImpl.cpp:93: SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance());
platform/nxp/k32w/k32w0/PlatformManagerImpl.cpp:179: SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance());
platform/nxp/rt/rw61x/PlatformManagerImpl.cpp:162: SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance());
platform/SingletonConfigurationManager.cpp:49:void SetConfigurationMgr(ConfigurationManager * configurationManager)
platform/openiotsdk/PlatformManagerImpl.cpp:109: SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance());
platform/mt793x/PlatformManagerImpl.cpp:68: SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance());
transport/raw/tests/NetworkTestHelpers.cpp:33: chip::DeviceLayer::SetConfigurationMgr(&chip::DeviceLayer::ConfigurationMgrImpl());
这个代码写法太嵌入式化了, 我的编辑器都无法正确的跳转了。
这里使用grep来搜索也是很好用的。
可以看到, 有很多平台, 但是都是调用同一个配置管理器。是吧,可能已经习惯了。靠编译时候的宏来区分平台。 对于这种代码, 最优价值的是函数/接口,不是实现。 至于怎么实现的, 除非你的工作是适配新的芯片平台, 否则,不需要关心。
头文件和源文件分离, 可能是参考linux的代码风格。 头文件在include里面, ConfigurationManager.h 我摘录几行,可以体会一下,读起来还是不难的。
virtual CHIP_ERROR GetPrimaryMACAddress(MutableByteSpan buf) = 0;
virtual CHIP_ERROR GetPrimaryWiFiMACAddress(uint8_t * buf) = 0;
virtual CHIP_ERROR GetPrimary802154MACAddress(uint8_t * buf) = 0;
virtual CHIP_ERROR GetSoftwareVersionString(char * buf, size_t bufSize) = 0;
virtual CHIP_ERROR GetSoftwareVersion(uint32_t & softwareVer) = 0;
virtual CHIP_ERROR GetFirmwareBuildChipEpochTime(System::Clock::Seconds32 & buildTime) = 0;
ConfigurationManagerImpl 这种写法也是一个很常用。 一个虚基类如果每次运行的时候只对应一个实现。 比如每个芯片对应一个实现。
代码越乱工资越高
matter的代码我说过要从example的例子看起。 比如我用的是linux的例子。那么我看到 main.cpp 使用了 AppMain.cpp 的 ChipLinuxAppInit() 函数。 里面有一个初始化是这样做
err = DeviceLayer::PlatformMgr().InitChipStack();
我上面解释过类似的, PlatformMgr() 猜测是一个单例模式。 由不同芯片平台来实现。 但是搜InitChipStack就会发现没有那么多实现。 很多是有前缀的 _InitChipStack()。 就是这么妖娆, 在PlatformManager.h里面, inline了一个实现
inline CHIP_ERROR PlatformManager::InitChipStack()
{
// NOTE: this is NOT thread safe and cannot be as the chip stack lock is prepared by
// InitChipStack itself on many platforms.
//
// In the future, this could be moved into specific platform code (where it can
// be made thread safe). In general however, init twice
// is likely a logic error and we may want to avoid that path anyway. Likely to
// be done once code stabilizes a bit more.
if (mInitialized)
{
return CHIP_NO_ERROR;
}
CHIP_ERROR err = static_cast<ImplClass *>(this)->_InitChipStack();
mInitialized = (err == CHIP_NO_ERROR);
return err;
}
然后一堆解释, 这个要注意啥啥啥, 未来可能啥啥啥, 哈哈哈。 这不就是防御性编程么
入门越陡峭, 工资越高。