如何处理模板类头中的循环#include调用?

问题描述 投票:0回答:2

Error "Unterminated conditional directive" in cross-referencing headers相关

我有一个Serializable模板类:

serializable.h

#pragma once
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include "Logger.h"
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception_ptr.hpp>

template<class T>
class Serializable {
public:
    static bool Deserialize(Serializable<T>* object, std::string serializedObject) {
        try {
            return object->SetValuesFromPropertyTree(GetPropertyTreeFromJsonString(serialized));
        } catch (...) {
            std::string message = boost::current_exception_diagnostic_information();
            Logger::PostLogMessageSimple(LogMessage::ERROR, message);
            std::cerr << message << std::endl;
        }
    }
private:
    static boost::property_tree::ptree GetPropertyTreeFromJsonString(const std::string & jsonStr) {
        std::istringstream iss(jsonStr);
        boost::property_tree::ptree pt;
        boost::property_tree::read_json(iss, pt);
        return pt;
    }
}
#endif // SERIALIZABLE_H

但问题是Logger类使用从Serializable继承的LogMessage对象(使用CRTP)。

Logger.h

#pragma once
#ifndef LOGGER_H
#define LOGGER_H
#include "LogMessage.h"

class Logger {
public:
    static void PostLogMessageSimple(LogMessage::Severity severity, const std::string & message);
}
#endif // LOGGER_H

LogMessage.h

#pragma once
#ifndef LOGMESSAGE_H
#define LOGMESSAGE_H
#include "serializable.h"

class LogMessage : public Serializable<LogMessage> {
public:
    enum Severity {
        DEBUG,
        INFO,
        WARN,
        ERROR
    };
private:
    std::string m_timestamp;
    std::string m_message;

    friend class Serializable<LogMessage>;
    virtual boost::property_tree::ptree GetNewPropertyTree() const;
    virtual bool SetValuesFromPropertyTree(const boost::property_tree::ptree & pt);
}

#endif // LOGMESSAGE_H

这里的问题是这些文件中的每一个都包含导致构建错误的另一个文件。不幸的是,我无法使用上述链接问题的解决方案(将#include“Logger.h”移动到Serializable.cpp),因为Serializable是一个模板类,因此需要在头文件中定义。

我不知道如何继续。任何帮助表示赞赏!

编辑:我还考虑在serializable.h中使用Logger和LogMessage的前向声明,但由于我在Logger中调用静态方法并使用LogMessage :: Severity,这不起作用。

c++ templates circular-dependency template-classes
2个回答
3
投票

有时循环依赖需要对所涉及的组件进行一些分析。找出圆存在的原因,然后弄清楚为什么它不需要存在。分析可以在多个层面进行。以下是我要开始的两个级别。

(由于代码显然是从实际代码中简化而来,我将避免假设它显示真正问题的程度。说到这一点,感谢不要用压倒性的细节淹没我们!代码足以说明问题。我的答案中的任何具体建议同样旨在说明要点,而不一定是最终的解决方案。)


在一个层面上,您可以查看类的意图。忘记代码并专注于目的。如果不知道B类是什么,A类就无法定义自己是否有意义?请记住,这比知道B类存在(这等同于前向定义,不需要标题)更强。如果没有查看代码就没有意义,那么也许你找到了可以解决的问题。不可否认,模板的使用使问题变得复杂,因为整个实现需要在标题中。

例如,Serializable真的应该能够定义自己而不知道将对序列化做什么(即Logger)。但是,它是一个模板,其实现需要能够记录错误。所以......很棘手。

不过,这是一个可以寻找解决方案的地方。一种可能性是将错误记录分离为仅处理字符串(已经是序列化数据)的基础片段,以及可以将LogMessage转换为基础片段的字符串的转换层。反序列化期间的错误强烈表明缺少序列化的任何内容,因此日志记录可以直接转到基础片段。依赖关系圈会闯入链中:

Serializable -> LoggerBase
Logger -> LoggerBase
Logger -> LogMessage -> Serializable -> LoggerBase

在另一个层面,您可以详细查看代码,而不是过多地担心目的。你有标题A包括标题B - 为什么? A的哪些部分实际上使用了B的东西? B的哪些部分实际使用?如果您需要更好地可视化这种依赖所在的位置,请绘制图表。然后引入一些目的考虑。这些部件是否在适当的位置定义?还有其他可能吗?

例如,在示例代码中,Serializable需要LogMessage的原因是访问枚举LogMessage::ERROR。这不是需要整个LogMessage定义的强有力的理由。也许像PostLogErrorSimple这样的包装可以消除对严重性常数的了解吗?也许现实比这更复杂,但重点是通过将依赖关系推送到源文件中可以使一些依赖关系步调一致。有时源文件是针对不同的类。

另一个例子来自Logger类。此类需要LogMessage才能访问LogMessage::Severity枚举(即将ERROR作为其值之一的枚举)。这也不是需要整个类定义的强有力的理由。也许枚举应该在其他地方定义?作为Logger的一部分或许?或者根本不在课程定义中?如果这种方法有效,则依赖关系圈会进入链中:

Serializable -> Severity
Serializable -> Logger -> Severity // To get the PostLogMessageSimple function
Logger -> Severity

理想情况下,一旦枚举被处理,Logger标题只能通过LogMessage的前向声明而不是包括完整的标题。 (前向声明足以通过引用接收对象。并且可能完整的Logger定义将有一些函数采用LogMessage参数。)


0
投票

如果您只是在类中声明并且定义了方法,那么您应该能够使它工作:

#pragma once
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception_ptr.hpp>

template<class T>
class Serializable {
public:
    static bool Deserialize(Serializable<T>* object, std::string serializedObject);
private:
    static boost::property_tree::ptree GetPropertyTreeFromJsonString(const std::string & jsonStr);
}

#include "Logger.h"

template < typename T >
inline bool Serializable<T>::Deserialize(Serializable<T>* object, std::string serializedObject) {
        try {
            return object->SetValuesFromPropertyTree(GetPropertyTreeFromJsonString(serialized));
        } catch (...) {
            std::string message = boost::current_exception_diagnostic_information();
            Logger::PostLogMessageSimple(LogMessage::ERROR, message);
            std::cerr << message << std::endl;
        }
    }

template < typename T >
inline boost::property_tree::ptree Serializable<T>::GetPropertyTreeFromJsonString(const std::string & jsonStr) {
        std::istringstream iss(jsonStr);
        boost::property_tree::ptree pt;
        boost::property_tree::read_json(iss, pt);
        return pt;
    }

#endif // SERIALIZABLE_H

这有使您的类界面更清晰的额外好处。

© www.soinside.com 2019 - 2024. All rights reserved.