【问题标题】:Refactoring two very similar classes in two applications and one dll在两个应用程序和一个 dll 中重构两个非常相似的类
【发布时间】:2017-01-12 08:02:33
【问题描述】:

我创建了两个使用一个 dll 的应用程序(app1 和 app2)。 每个应用程序都有自己的配置,使用 protobuf 进行序列化 和反序列化。这两个应用程序都有自己的类 FileConfig。采用休闲方式。

App1 和 App2 的 FileConfig.h

class FileConfig {
    private:
        Config getDefaultConfig();
        Config getConfigFromMessage(const App1ConfigFile& configFile);
    public:
        Config parseFile(const std::string& filename);
        static UFCApi::Config* getConfig(const std::string& filename) {
            FileConfig fileConfig;
            return fileConfig.parseFile(filename);
        };
};

App1 中的 FileConfig.cpp

Config FileConfig::parseFile(const std::string& filename) {
    std::fstream file(filename, std::ios::in);

    App1ConfigFile configFile; // App1ConfigFile is protobuf generated class 

    std::stringstream buffer;
    buffer << file.rdbuf();
    auto configFileStr = buffer.str();

    if (google::protobuf::TextFormat::ParseFromString(configFileStr, configFile)) {
        return getConfigFromMessage(configFile);
    }

    return getDefaultConfig();
}

App2 中的 FileConfig.cpp

Config FileConfig::parseFile(const std::string& filename) {
    std::fstream file(filename, std::ios::in);

    App2ConfigFile configFile; // App2ConfigFile is protobuf generated class 

    std::stringstream buffer;
    buffer << file.rdbuf();
    auto configFileStr = buffer.str();

    if (google::protobuf::TextFormat::ParseFromString(configFileStr, configFile)) {
        return getConfigFromMessage(configFile);
    }

    return getDefaultConfig();
}

我想重构这两个类并使用两个派生类 App1FileConfig 和 App2FileConfig 创建基本 FileConfig。

我在休耕的情况下做到了这一点。 我将基类放在 dll 中,派生类放在 App1 和 App2 中(分别)。

dll.h

template <typename PersistenceConfigFile>
class FileConfig {
    private:
        virtual Config getDefaultConfig() = 0;
        virtual Config getConfigFromMessage(const PersistenceConfigFile& configFile) = 0;
    public:
        Config parseFile(const std::string& filename);
        static Config getConfig(const std::string& filename);
};

dll.cpp

template <typename PersistenceConfigFile>
Config FileConfig<PersistenceConfigFile>::parseFile(const std::string& filename) {
    std::fstream file(filename, std::ios::in);

    PersistenceConfigFile configFile;

    std::stringstream buffer;
    buffer << file.rdbuf();
    auto configFileStr = buffer.str();

    if (google::protobuf::TextFormat::ParseFromString(configFileStr, configFile)) {
        return getConfigFromMessage(configFile);
    }

    return getDefaultConfig();
}

App1 中的 App1.h

class App1Config: public FileConfig<App1ConfigFile> {
    private:
        Config getDefaultConfig() {//some implementation};
        Config getConfigFromMessage(App1ConfigFile configFile); {//some implementation};
    public:
        static Config getConfig(const std::string& filename) {
            App1Config app1Config;
            return app1Config.parseFile(filename)
        };
};

App2 中的 App2.h

class App1Config: public FileConfig<App2ConfigFile> {
    private:
        Config getDefaultConfig() {//some implementation};
        Config getConfigFromMessage(App1ConfigFile configFile); {//some implementation};
    public:
        static Config getConfig(const std::string& filename){
            App2Config app2Config;
            return app2Config.parseFile(filename)
        };
};

Dll 构建通过,App1 和 App2 构建失败,错误链接器找不到方法 parseFile。

error LNK2019: unresolved external symbol "public: class Config __cdecl FileConfig<class App1ConfigFile>::parseFile(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?parseFile@?$FileConfig@VApp1ConfigFile@@@UFCApi@@QEAAPEAVConfig@2@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: static class Config * __cdecl App1Config::getConfig(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?getConfig@App1Config@@SAPEAVConfig@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)

【问题讨论】:

    标签: c++ c++11 dll


    【解决方案1】:

    因为parseFile() 属于模板类,所以需要在标题dll.cpp 中定义它。

    【讨论】:

      【解决方案2】:

      模板和 DLL 不能很好地协同工作。您需要在标头中有实现,但实际上不会从 DLL 加载任何内容。

      您可以通过在 dll 中显式实例化来解决这个问题:

      template class FileConfig<App1ConfigFile>;
      template class FileConfig<App2ConfigFile>;
      

      但这充其量只是一种解决方法,因为如果您添加另一种配置类型,您还需要将显式实例化添加到 DLL(并重新构建它)。

      (顺便说一句,当谈到 DLL 时,它似乎是 Windows,所以你的类声明需要使用 __declspec(dllexport/dllimport)

      在我看来,最好的办法是为文件配置创建一个通用接口(它将完成FileConfig 本身无法通过虚拟调用完成的工作)并将其传递给FileConfig 构造函数(或解析方法)通过引用,因此FileConfig 不需要模板化,因此可以由 DLL 提供。

      【讨论】:

        猜你喜欢
        • 2017-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-13
        • 2011-09-11
        • 1970-01-01
        相关资源
        最近更新 更多