C++11 实现 map 和结构体的相互转换

1,272 阅读2分钟

「这是我参与11月更文挑战的第 6 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 11 篇文章

参考原文地址: C++ 中数据表如何转成业务实体 —— map 和结构体的相互转换

我对文章的格式和错别字进行了调整,并在他的基础上,根据我自己的理解把重点部分进一步解释完善(原作者的代码注释甚少)。以下是正文。

应用场景

如何把数据库表中的一行转换成一个业务实体结构体,C# 和 Java 中都有实体框架,表到实体的转换很方便,C++ 中缺少这些框架,但是有一些折中的办法去做。其实问题的本质是: map 如何转成结构体。

问题

  1. 如何把 map 中字段(key)对应的值赋给结构体中相同名称字段?

  2. 如何让结构体在 map 中查找相应的字段(key)

  • 一种办法是通过手写的办法,把每个字段名称写成常量字符串,然后去 map 中查找,找到后,再给该字段赋值,这个办法是可行,但是缺点也很明显,需要重复硬编码很多字段名称,代码也不够优雅。

  • 一种改进的办法是通过一个宏和一个模板函数去赋值比较好。

具体实例

这是我们的结构体


#define VarName(x) #x  ///< 字段转成名称的宏 

/// @note 结构体定义
struct TestInfo
{
    int ID;
    int KPIID;
    int Code;
    int V1;
    int V2;
    int V3;

    void Init()
    {
        memset(this, 0, sizeof(TestInfo));
    }

    /// @note map -> struct
    void Make(SweetDB::Row & row)
    {
        Init();
        GetValue(row, VarName(ID), ID);
        GetValue(row, VarName(KPIID), KPIID);
        GetValue(row, VarName(Code), Code);
        GetValue(row, VarName(V1), V1);
        GetValue(row, VarName(V2), V2);
        GetValue(row, VarName(V3), V3);
    }
    
    /// @note struct -> map
    void MakeToRow(SweetDB::Row & row)
    {
        row[VarName(ID)] = ID;
        row[VarName(KPIID)] = KPIID;
    }

    template <typename T>
    static bool GetValue(SweetDB::Row & row, const char *name, T &t)
    {
        auto it = row.find(name);
        if (it == row.end()) ///< 如果找不到该字段
            return false;

        t = get<T>(it->second); ///< 从 map 取出对应的字段的值赋给结构体的对应成员上

        return true;
    }
};

测试代码

typedef boost::variant<double, int, string> Value;
typedef unordered_map<const char *, Value> Row;
TestInfoResult TestDoublePointer()
{
    SweetDB::Row row = {{"ID", 1}, {"KPIID", 2}, {"Code", 2}};
    TestInfo t;
    t.Make(row); ///< 把 map 中对应字段的值赋给 t

    SweetDB::Row row1;
    t.MakeToRow(row1); ///< 把 t 的字段值赋给 map
}

最后我们看到 map -> struct 成功了,struct -> map 也成功了。

原作者认为改进的代码的
优点是不用硬编码字段名称;
缺点是每个字段的赋值还是要编码,这个工作量还是没省。
如果是 C# 语言直接就通过反射搞定,不需要这么绕弯子了, C++ 语言就没办法,只能自己发明轮子了。也许大家还有更好的办法,可以一起探讨一下。
也许 C++ ORM 框架大体可以按这个思路去做吧。