【问题标题】:Sometimes call function from within a variadic template function有时从可变参数模板函数中调用函数
【发布时间】:2017-11-29 19:02:50
【问题描述】:

我有一个基本的消息类,用于序列化数据,然后通过套接字发送该数据:

std::vector<char> m_rawMessageData;
int m_currentReadPosition = 0;

template<typename writeDataType>
void LoadData(writeDataType &inData)
{
    int dataSize = sizeof(writeDataType);
    int currentSize = m_rawMessageData.size();

    m_rawMessageData.resize(dataSize + m_rawMessageData.size());
    std::memcpy(&m_rawMessageData.at(currentSize), &(inData), dataSize);
}

template<typename ReadDataType>
int GetData(ReadDataType &outData)
{
    std::stringstream dataReader;

    int dataSize = sizeof(ReadDataType);

    if (m_currentReadPosition + dataSize > m_rawMessageData.size())
        return 0;

    std::memcpy(&outData, &m_rawMessageData.at(m_currentReadPosition),
        dataSize);  

    m_currentReadPosition += dataSize;

    return dataSize;
}   

我知道如果调用不正确,此代码中可能存在一些错误/错误,但我是目前唯一的开发人员,我需要让某些东西正常工作。另外,我假设只会传递基本类型,并且两端的 endiness 是相同的。

我希望这个消息类能够处理结构。我的想法是:在我的每个结构中,我将编写一个“序列化/反序列化”函数。然后,在我的 LoadData 和 GetData 调用中,我将检查是否存在“Serialize/Deserialize”函数,然后调用它而不是执行通用 memcpy。

我能够使用与此问题中的代码类似的东西来检查是否有序列化函数: Check if a class has a member function of a given signature

但我不确定我应该如何调用“序列化”函数。我不能简单地使用“inData.Serialize()”,因为它无法编译。

简而言之,我希望我的 LoadData 函数看起来像这样:

template<typename writeDataType>
void LoadData(writeDataType &inData)
{
    //Check if writeDataType has Serialize Function. 
    if (serializeDoesExist)
    {
         inData.Serialize(m_rawMessageData);
    }
    else
    {
        int dataSize = sizeof(writeDataType);
        int currentSize = m_rawMessageData.size();

        m_rawMessageData.resize(dataSize + m_rawMessageData.size());
        std::memcpy(&m_rawMessageData.at(currentSize), &(inData), dataSize);
    }
}

任何帮助将不胜感激。

注意:除非绝对必要,否则我的老板不喜欢将外部库添加到项目中。这就是为什么我没有使用 Protobuf 或类似的东西。

【问题讨论】:

    标签: c++ serialization


    【解决方案1】:

    使用std::experimental::isdetected,您可以创建特征:

    template<class T>
    using serialize_t = decltype(std::declval<T>().Serialize());
    
    template <typename T>
    using has_serialize = std::experimental::is_detected<serialize_t, T>;
    

    然后,使用 SFINAE:

    template<typename writeDataType>
    std::enable_if_t<has_serialize<writeDataType>::value>
    LoadData(writeDataType &inData)
    {
        inData.Serialize(m_rawMessageData);
    }
    
    template<typename writeDataType>
    std::enable_if_t<!has_serialize<writeDataType>::value>
    LoadData(writeDataType &inData)
    {
        int dataSize = sizeof(writeDataType);
        int currentSize = m_rawMessageData.size();
    
        m_rawMessageData.resize(dataSize + m_rawMessageData.size());
        std::memcpy(&m_rawMessageData.at(currentSize), &(inData), dataSize);
    }
    

    【讨论】:

    • 非常好,它甚至简化了检测步骤。仅使用 C++14 就可以做到这一点吗?如果没有,我会继续并将其标记为答案,因为它既优雅又简单。
    • std::experimental::detected 可以用 C++11 实现。 (链接给出可能的实现,std::void_t 的依赖关系也可以在 C++11 中完成)。
    【解决方案2】:

    我建议您编写一个专用于 std::enable_if 的 LoadData/GetData 对,检查所需成员的存在。专用表单使用这些成员,而通用表单继续使用 memcpy

    【讨论】:

    • 你的意思是为我在程序中定义的每个结构编写一个专门的 LoadData/GetData 吗?我想这是一个可能的解决方案,但我真的很想保持通用性。
    • 不,我的意思是使用 std::enable_if 和您的成员检测来选择一个专业化,如果该成员存在则将使用该成员,但不存在的对继续使用 memcpy。事实证明,LoadData 和 GetData 的当前和不存在形式都需要一个 std::enable_if 参数(一个条件为真,另一个不为真)。这样每个 LoadData/GetData 都只有适合该代码路径的代码。
    【解决方案3】:

    您可以使用 try-catch 语句来检查此函数是否存在。

    try{
        inData.Serialize();
    }catch(<You can specify the exception here, or just write "..." to catch any exception>...){
        <What you want to do if the function does not exists>
    }
    

    如果您想了解更多有关 try-catch 和异常处理的详细信息: https://www.tutorialspoint.com/cplusplus/cpp_exceptions_handling.htm

    【讨论】:

    • 我不相信这会起作用,因为“inData”是一个类型名。问题是我一开始就不能写 inData.Serialize() ,因为这会导致编译器错误。
    • 对不起,我的错误,我将“inData”视为一个实例。那么你可能不得不使用 SFINAE。看看这个问题:stackoverflow.com/questions/257288/…
    猜你喜欢
    • 2012-04-15
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 2018-03-05
    • 2017-10-12
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多