C++;应用程序/程序设置的类? 使用VisualStudioC++与MFC。我正试图找出存储应用程序/程序设置的好方法。我不是指它们的持久存储,而是指代码中用于保存设置的数据结构

C++;应用程序/程序设置的类? 使用VisualStudioC++与MFC。我正试图找出存储应用程序/程序设置的好方法。我不是指它们的持久存储,而是指代码中用于保存设置的数据结构,c++,visual-c++,configuration,mfc,settings,C++,Visual C++,Configuration,Mfc,Settings,我创建了一个名为Settings的静态类,它有几个静态方法和嵌套类来划分设置。例如: class Settings { public: Settings(void); ~Settings(void); static void SetConfigFile(const char * path); static CString GetConfigFilePath(); static void Load(); static void Save();

我创建了一个名为Settings的静态类,它有几个静态方法和嵌套类来划分设置。例如:

class Settings
{
public:
    Settings(void);
    ~Settings(void);

    static void SetConfigFile(const char * path);
    static CString GetConfigFilePath();
    static void Load();
    static void Save();

    class General
    {
    public:
        static CString GetHomePage();
        static void SetHomePage(const char * url);
    private:
        static int homePageUrl_;
    };

private:
    static CString configFilePath_;
};
然后,我可以在整个代码中访问我的设置,如:

Settings::General::GetHomePage();
现在我进入单元测试,我开始意识到静态类是不受欢迎的。所以我想把它变成一个基于实例的类。但是我必须管理嵌套的类实例,这很简单,但对于测试来说仍然有点麻烦。嵌套类的全部目的只是将设置分组到逻辑组中。我在争论基于字符串的设置类是否会更好,比如settings->get(“General.HomePage”),尽管我认为我更喜欢专用访问器方法的强类型


为了回答我的问题,什么样的数据结构才能保存支持直接单元测试的程序配置/设置?

如果它适合您,您可以这样做。您可以放弃枚举,转到常量字符串,甚至自由格式字符串。枚举实际上也不必在类中定义。有很多方法可以做到这一点

如果您想要实现类别,另一个类可以使用模板对多个实例执行类似的操作来定义枚举类型

只是个主意

#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>
using namespace std;


class Settings
{
public:

  typedef enum
  {
    HomePageURL,
    EmailAddress,
    CellPhone
  } SettingName;

private:
  typedef map<SettingName, string> SettingCollection;

  SettingCollection theSettings;


public:

  string& operator[](const SettingName& theName)
  {
    return theSettings[theName];
  }

  void Load ()
  {
    theSettings[HomePageURL] = "http://localhost";
  }

  void Save ()
  {
    // Do whatever here
  }

};


int _tmain(int argc, _TCHAR* argv[])
{
  Settings someSettings;

  someSettings.Load ();

  cout << someSettings [Settings::SettingName::HomePageURL] << endl;


    return 0;
}
#包括“stdafx.h”
#包括
#包括
#包括
使用名称空间std;
班级设置
{
公众:
类型定义枚举
{
主页URL,
电邮地址:,
手机
}设置名称;
私人:
typedef地图设置集合;
设置收集这些设置;
公众:
字符串和运算符[](常量设置名称和名称)
{
返回设置[名称];
}
空荷载()
{
设置[主页URL]=”http://localhost";
}
作废保存()
{
//在这里做什么都行
}
};
int _tmain(int argc,_TCHAR*argv[]
{
设置一些设置;
Load();

cout我认为您的要求(1)提供对配置变量的类型安全访问;(2)使用
“full.scoped.name”
语法指定配置变量的名称之间不必存在冲突。当然,您可以进行类型安全操作,例如:

const char * getString(const char * fullyScopedName);
int          getInt(const char * fullyScopedName);
bool         getBool(const char * fullyScopedName);
通过阅读《我的图书馆入门指南》(,)的第2章和第3章,您可能会找到一些灵感

编辑:我提到的Config4Cpp文档可能会为API设计提供灵感,但我后来意识到,如果您决定从头开始编写自己的配置类(而不是使用像Config4Cpp这样的第三方库),您可能会很感激关于实现选项的建议

您的类应该使用
std::map
来存储fullyScopedName->值映射的集合。显然,fullyScopedName将是一个字符串,但是有两个选项用于表示值

第一个选项是将值表示为字符串。类型安全访问器,如
getInt()
getBool()
将从映射中检索基于字符串的值,然后对其进行解析以将其转换为所需的类型。如果解析失败,则访问器操作将引发异常。(这是Config4Cpp采用的方法。)

第二个选项是表示如下伪代码所示的值:

enum ValueType { STRING_VAL, INT_VAL, BOOL_VAL };
struct Value {
    ValueType         type;
    union {
        const char *  stringVal;
        int           intVal;
        bool          boolVal;
    } data;
};
然后,类型安全访问器的实现可以按如下方式编码(伪代码):


我现在使用的这个类主要受Nathan答案的启发,除了模板化方法:

class Settings {
public:
    Settings(void);
    virtual ~Settings(void);

    enum SettingName { General_WindowWidth, General_HomePageUrl,
        General_ShowDownloadsWindow, Privacy_RememberPasswords,
        Privacy_RememberHistory };

    virtual void SetConfigFile(const char * path);
    virtual std::string GetConfigFilePath();
    virtual void Load();
    virtual void Save();

    template<class T>
    T Get(SettingName name) {
        return boost::lexical_cast<T>(settings_[name]);
    }

    template<class T>
    void Set(SettingName name, T value) {
        settings_[name] = boost::lexical_cast<std::string>(value);
    }

    void Set(SettingName name, std::string value) {
        settings_[name] = value;
    }

private:
    std::string configFilePath_;
    std::map<SettingName, std::string> settings_;
};
类设置{
公众:
设置(无效);
虚拟设置(void);
枚举设置名称{General_WindowWidth,General_HomePageUrl,
一般\u show downloadswindow,隐私\u memberrpasswords,
隐私(记忆故事);;
虚拟void SetConfigFile(const char*path);
虚拟std::字符串GetConfigFilePath();
虚空荷载();
虚拟空保存();
模板
T Get(设置名称){
返回boost::词法转换(设置[name]);
}
模板
无效集(设置名称、T值){
设置\uu[name]=boost::词法\u转换(值);
}
无效集(设置名称、标准::字符串值){
设置u[name]=值;
}
私人:
std::string configFilePath\uux;
标准::地图设置;
};

使用
typedef enum bla{}在C++声明中,你不需要写代码> EnUM BLA Value< /Cord>。<代码> BRAVARNEX满足。@ Rexx:在这个玩具例子中,我同意。但是,随着代码的演变,你发现模板模板不适合你的需求。ype或SettingCollection::key_type as types,这样,如果在一段复杂的代码中更改模板参数,则只需在一个位置更改类型。如果在类中使用了模板类型,并且这些类型本身就是模板,那么这也可以很好地工作。它还显示了类型的用途,因为在整个代码中使用的类型都是u他在地图上画了一条线。
class Settings {
public:
    Settings(void);
    virtual ~Settings(void);

    enum SettingName { General_WindowWidth, General_HomePageUrl,
        General_ShowDownloadsWindow, Privacy_RememberPasswords,
        Privacy_RememberHistory };

    virtual void SetConfigFile(const char * path);
    virtual std::string GetConfigFilePath();
    virtual void Load();
    virtual void Save();

    template<class T>
    T Get(SettingName name) {
        return boost::lexical_cast<T>(settings_[name]);
    }

    template<class T>
    void Set(SettingName name, T value) {
        settings_[name] = boost::lexical_cast<std::string>(value);
    }

    void Set(SettingName name, std::string value) {
        settings_[name] = value;
    }

private:
    std::string configFilePath_;
    std::map<SettingName, std::string> settings_;
};