码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 异类词典的实现


    目录

    一、外围框架

    二、Create函数的实现

    三、Values的主体框架

    3.1 Get接口实现以及分析

    3.1 Tag2ID逻辑分析


    一、外围框架

    VarTypeDict的外围框架代码示例:

    1. template <typename...TParameters>
    2. struct VarTypeDict
    3. {
    4. template <typename...TTypes>
    5. struct Values {
    6. public:
    7. template <typename TTag, typename TVal>
    8. auto Set(TVal&& val) && ;
    9. template <typename TTag>
    10. const auto& Get() const;
    11. };
    12. public:
    13. static auto Create() {
    14. using namespace NSVarTypeDict;
    15. using type = typename Create_<sizeof...(TParameters),
    16. Values>::type;
    17. return type();
    18. )
    19. };

    Create 函数是一个静态成员函数,用于实例化内部使用的类型,并返回该类型的实例。

    在函数中,通过 Create_ 模板类来实例化 Values 模板类,然后返回这个实例。这里的 Create_ 模板类负责将模板参数包 TParameters... 和 TTypes... 转发给 Values 模板类。Create_ 模板类根据参数来生成相应的类型。然后,通过 typename Create_::type 来获取最终实例化的类型。最后,通过 return type(); 来创建并返回这个实例。

    Create_ 类似于一个元编程辅助类,在实际应用中可能会根据 TParameters 的个数生成相应的类型实例,并将这个类型保存到 type 中。

    之前文章的代码:

    1. std::cerr << fun(FParams::Create()
    2. .Set(2.4f)
    3. .Set(1.3f)
    4. .Set(0.1f));

    根据原文赘述总结:

    链式调用的设计模式在某些情况下非常有用,特别是在配置对象、构建器模式或者 Fluent 接口设计中。通过链式调用,可以使代码更加清晰和易读,同时也能够提供更直观的代码结构和调用方式。这种方式可以使得代码更具有表达性,可以一目了然地看出对象的构建过程,提高了代码的可维护性和可读性。

    同时,在模板元编程中,链式调用也可以用来对模板参数化的代码进行更加灵活和直观的使用,可以方便地对多个模板参数进行操作和设置。

    二、Create函数的实现

    原文如下:

    Create函数是整个模块首个对外的接口,但这个接口实现时有一个问题。

    考虑如下代码:

    VarTypeDict::Create().Set(true).Set(2.4f);

    问题就是Create返回的是Value的实例。这个实例最终需要包含容器中每个值所对应的具体数据类型。对于这段代码。理想的情况是,Create函数执行完成时,返回的是Value类型的实例。后续Set会依据这个信息来设置数据,但程序是从前向后执行的。在执行Create时,系统无法知道要设置的数据类型(bool, float类型), 它该怎么为TTypes赋值呢?

    作者思路:

    解决这个问题的一种方式是使用辅助函数或者辅助模板参数来捕获类型信息,这样就可以在Create中显式指定TTypes的类型。在C++中,可以使用辅助函数类型萃取(type_traits)、变参模板参数推导等技术来实现这一点。

    举例来说,可以设计一个辅助函数CreateHelper,其参数类型根据Set方法的参数类型进行推导,然后在CreateHelper函数中使用这些类型信息来显式指定TTypes的类型。这样,在调用Create时就可以捕获到需要的类型信息了。

    代码示例:

    1. // 辅助类型萃取,用于捕获Set方法的参数类型
    2. template <typename>
    3. struct TypeExtractor;
    4. template <typename T>
    5. struct TypeExtractor {
    6. using Type = T;
    7. };
    8. // Create辅助函数,捕获Set方法的参数类型并显式指定TTypes的类型
    9. template<typename... TTypes>
    10. auto CreateHelper() {
    11. return Values<typename TypeExtractor::Type...>();
    12. }
    13. // VarTypeDict类模板
    14. template <typename... TTypes>
    15. struct VarTypeDict {
    16. // Create方法,调用CreateHelper来捕获类型信息
    17. static auto Create() {
    18. return CreateHelper();
    19. }
    20. };
    21. int main() {
    22. // 调用Create方法并设置值
    23. VarTypeDict<bool, float>::Create().Set<bool>(true).Set<float>(2.4f);
    24. return 0;
    25. }

    书中作者思路:

    引入一个“占位类型”:

    struct Nullparameter

    在Create函数调用之初,用这个占位符类型填充TTypes,在之后的Set中,再来修改这个类型为实际的类型。还是以之前的代码为例(其中每一步调用后都给出了相应的返回类型):

    1. VartypeDict
    2. ::Create() // Values
    3. .Set(true) // Values
    4. .Set(2.4f); // Valuues

    Create函数如下:

    1. namespace NSVarTypeDict
    2. {
    3. template <size_t N, template<typename...> class TCont, typename...T>
    4. struct Create_ {
    5. using type = typename Create_1, TCont, NullParameter, T...>::type;
    6. };
    7. template <template<typename...> class TCont, typename...T>
    8. struct Create_<0, TCont, T...> {
    9. using type = TCont;
    10. };
    11. }
    12. template <typename...TParameters>
    13. struct VarTypeDict {
    14. // ...
    15. static auto Create() {
    16. using namespace NSVarTypeDict;
    17. using type = typename Create<sizeof...(TParameters), Values>::type;
    18. return type();
    19. }
    20. };

    代码讲解:

    以下内容结合原文看!!!

    这段代码是一个 C++ 的模板元编程实现,它通过模板递归的方式创建一个通用的变长参数模板类 VarTypeDict。

    首先定义了一个命名空间 NSVarTypeDict,用于存放模板元编程相关的代码。

    在命名空间 NSVarTypeDict 中,定义了一个模板结构体 Create_。这个结构体接受一个类型模板 TCont 和一系列类型参数 T,它有两个模板参数:size_t N 代表参数包的大小,template class TCont 代表模板容器类型,typename...T 则代表参数包中的类型。Create_ 结构体有两个模板特化版本:
    1. 第一个特化版本定义了一个递归结构,它根据 N 的值递减,将参数包中的每个参数添加到 TCont 容器中。
    2. 第二个特化版本定义了递归结束的条件,当 N 的值为 0 时,创建了最终的 TCont 类型,并将其赋值给 type。

    接着定义了一个模板类 VarTypeDict,它接受一系列模板参数 TParameters。在这个类中,定义了一个静态成员函数 Create()。在这个函数中,首先使用 using namespace NSVarTypeDict; 将 NSVarTypeDict 命名空间中的内容引入到当前作用域中。然后使用模板别名 using type = typename Create::type; 来创建一个类型,该类型是基于参数包 TParameters 的容器类型。最后,使用 return type(); 来创建一个匿名对象并返回。

    总的来说,这段代码实现了一个通用的变长参数模板类 VarTypeDict,它能够根据传入的参数类型动态生成对应的容器类型。模板元编程的递归思想和特化版本的定义,使得这个过程能够根据参数包的大小动态展开,实现了通用性和灵活性。

    三、Values的主体框架

    代码示例:

    1. template <typename...TParameters>
    2. struct VarTypeDict
    3. {
    4. template <typename...TTypes>
    5. struct Values
    6. {
    7. Values() = default;
    8. Values(std::shared_ptr<void>(&&input)[sizeof...(TTypes)])
    9. {
    10. for (size_t i = 0; i < sizeof...(TTypes); ++i)
    11. {
    12. m_tuple[i] = std::move(input[i]);
    13. }
    14. }
    15. public:
    16. template <typename TTag, typename TVal>
    17. auto Set(TVal&& val) &&
    18. {
    19. using namespace NSMUltiTypeDict;
    20. constexpr static size_t TagPos = Tag2ID;
    21. using rawVal = std::decay_t;
    22. rawVal* tmp = new rawVal(std::forward(val));
    23. m_tuple[TagPos] = std::shared_ptr<void>(tmp,
    24. [](void* ptr) {
    25. rawVal* ntpr = static_cast(ptr);
    26. delete nptr;
    27. });
    28. using new_type = NewTupleType, TTypes...>;
    29. return new_type(std::move(m_tuple));
    30. }
    31. template <typename TTag>
    32. auto& Get() const;
    33. private:
    34. std::shared_ptr<void> m_tuple[sizeof...(TTypes)];
    35. };
    36. );

    代码讲解:

    以下内容结合原文看!!!

    这段代码实现了一个 VarTypeDict 类模板,它包含一个内嵌的 Values 类模板。让我逐步解释一下这段代码的逻辑。

    首先定义了一个模板类 VarTypeDict,它接受一系列模板参数 TParameters。

    在 VarTypeDict 类模板中定义了一个内嵌的 Values 类模板。Values 类模板接受一系列模板参数 TTypes。该类模板包含以下几个成员函数和数据成员:

    1. 构造函数 Values() = default; 默认构造函数。

    2. 另一个构造函数 Values(std::shared_ptr(&&input)[sizeof...(TTypes)]),它接受一个大小为 sizeof...(TTypes) 的 std::shared_ptr 数组,将其内容逐个移动构造到 m_tuple 数组中。

    3. 成员函数 template auto Set(TVal&& val) && 用于设置特定类型的值。它首先使用 Tag2ID 获取模板参数 TTag 在 TParameters 中的位置,然后创建一个 TVal 类型的对象,并存储为 std::shared_ptr 类型,最后使用 NewTupleType 创建一个新的 Values 类型,并返回该类型的对象。

    4. 成员函数 template auto& Get() const; 用于获取特定类型的值。

    5. 数据成员 std::shared_ptr m_tuple[sizeof...(TTypes)] 用于存储元组中的值。

    值得注意的是,代码中使用了一些未定义的模板类型和函数,比如 NSMUltiTypeDict、Tag2ID、NewTupleType 等,这些部分的定义未在提供的代码片段中。因此,要完整理解这段代码的逻辑,还需要查看这些部分的定义。

    综上所述,这段代码实现了一个基于模板的多类型字典,可以动态存储和获取多种类型的值。通过 Values 类模板的成员函数 Set 和 Get,可以实现对值的设置和获取操作。

    3.1 Get接口实现以及分析

    代码示例:

    1. template <typename TTag>
    2. auto& Get() const
    3. {
    4.     using namespace NSMUltiTypeDict;
    5.     constexpr static size_t TagPos = Tag2ID;
    6.     return *static_castdecay_t<typename GetTupleType::type>*>(m_tuple[TagPos].get());
    7. }

    Get接口代码逻辑:

    1. Get 函数是一个模板成员函数,它接受模板参数 TTag,在编译期使用 Tag2ID 获取模板参数 TTag 在 TParameters 中的位置,存储在 TagPos 中。

    2. 接着使用 GetTupleType 获取在 Values 类模板的 Types 参数包中第 TagPos 个类型对应的类型,通过 std::decay_t 去除引用和 cv 限定符。

    3. 然后通过 m_tuple[TagPos] 获取对应位置的值,使用 get() 函数获取 std::shared_ptr 指向的原始指针,进行 static_cast 转换,并通过解引用运算符 * 返回对应类型的引用。

    总结来说,Get 函数的目的是根据传入的类型标签 TTag,在运行时获取对应类型的值,并返回对应类型的引用。这样就实现了通过类型标签快速获取特定类型的值的功能。

    3.1 Tag2ID逻辑分析

    NewTupleType逻辑分析请看原文,以下内容结合原文看!!!

    1. template <typename TTag, typename... TParameters>
    2. constexpr size_t Tag2ID()
    3. {
    4.     return Tag2IDImpl0, TParameters...>();
    5. }
    6. template <typename TTag, size_t Index, typename TParameter, typename... TParameters>
    7. constexpr size_t Tag2IDImpl()
    8. {
    9.     if constexpr (std::is_same_v) {
    10.         return Index;
    11.     } else {
    12.         return Tag2IDImpl1, TParameters...>();
    13.     }
    14. }
    15. template <typename TTag, size_t Index>
    16. constexpr size_t Tag2IDImp()
    17. {
    18.     // 没有找到匹配的类型标签,可能需要抛出错误或返回一个默认值
    19.     return static_cast<size_t>(-1);
    20. }


    上述代码使用递归实现了 Tag2ID 函数。首先调用了 Tag2ID 函数模板,并传入类型标签和类型列表作为模板参数。然后,调用了 Tag2IDImpl 函数模板,传入类型标签、当前索引为0以及类型列表作为模板参数。

    在 Tag2IDImpl 函数模板中,首先检查当前类型标签(TTag)是否与当前类型参数(TParameter)相同。如果相同,则返回当前索引值作为类型标签在类型列表中的位置。否则,递归调用 Tag2IDImpl 函数模板,并将索引值加1,继续匹配下一个类型参数。

    如果遍历完整个类型列表都没有找到匹配的类型标签,那么可能需要采取适当的错误处理,如抛出异常或返回一个默认值。

    总之,Tag2ID 函数的目的是根据给定的类型标签和类型列表,返回类型标签在类型列表中的位置。这个位置可以用于进一步操作,如在代码中获取对应位置的值或执行其他相关操作。

  • 相关阅读:
    网络编程 day1
    JAVA常用类
    32:TX Text Control ActiveX/ASP.NET/WinForms/WPF Crack
    Pytorch -> ONNX -> TensorRT 模型转换与部署
    leetcode 226. Invert Binary Tree 翻转二叉树(简单)
    Nmap使用教程图文教程(超详细)
    图划分(Graph Partition&Re-ordering): METIS(5.x)&ParMETIS(4.x)使用实践
    时间复杂度吐血总结
    JAVA 链式列表的训练
    MILP(混合整数线性规划)
  • 原文地址:https://blog.csdn.net/cylddrmm123/article/details/134292370
  • 最新文章
  • 沪漂五周年了:我越来越迷茫了
    Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
    MySQL-Seconds_behind_master的精度误差
    [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
    AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
    Agent OS :五种驯服不确定性的范式
    PortSwigger SQL注入LAB11
    数据库即时编译JIT
    [Begin]AI Learn Data Day 0
    深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号