刺探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;
}

然后一堆解释, 这个要注意啥啥啥, 未来可能啥啥啥, 哈哈哈。 这不就是防御性编程么

入门越陡峭, 工资越高。