9

我的目标是建立一个数据结构来存储我的应用程序的设置。

在 PHP 中,我只会写...

$settings = array(
    "Fullscreen" => true,
    "Width"      => 1680,
    "Height"     => 1050,
    "Title"      => "My Application",
);

现在我尝试在 C++ 中创建一个类似的结构,但它还不能处理不同的数据类型。顺便说一句,如果有更好的方法来存储这些设置数据,请告诉我。

struct Setting{ string Key, Value; };

Setting Settings[] = {
    ("Fullscreen", "true"),     // it's acceptable to store the boolean as string
    ("Width", "1680"),          // it's not for integers as I want to use them later
    ("Height", 1050),           // would be nice but of course an error
    ("Title", "My Application") // strings aren't the problem with this implementation
};

如何为associative arraywith的结构建模flexible datatypes

4

6 回答 6

8

具有不同数据类型的关联数据结构正是struct...

struct SettingsType
{
    bool Fullscreen;
    int Width;
    int Height;
    std::string Title;
} Settings = { true, 1680, 1050, "My Application" };

现在,也许您想要某种反射,因为字段名称将出现在配置文件中?就像是:

SettingsSerializer x[] = { { "Fullscreen", &SettingsType::Fullscreen },
                           { "Width",      &SettingsType::Width },
                           { "Height",     &SettingsType::Height },
                           { "Title",      &Settings::Title } };

只要您SettingsSerializer根据指向成员的类型提供具有不同行为的重载构造函数,就会到达那里。

于 2012-08-24T21:02:28.930 回答
3

C++ 是一种强类型语言。容器只包含一种类型的对象,因此默认情况下,您尝试做的事情不能仅使用标准 C++ 来完成。

另一方面,您可以使用诸如 boost::variant 或 boost::any 之类的库,它们提供的类型可以容纳多种(或任何)类型中的一种,然后在您的应用程序中使用该类型的容器。

std::map您可以使用从设置名称映射到值而不是数组:

std::map<std::string, boost::variant<bool,int,std::string> >
于 2012-08-24T21:00:38.423 回答
2
#include <map>
#include <string>

std::map<std::string,std::string> settings;

settings.insert("Fullscreen","true");
settings.insert("Width","1680");
settings.insert("Height","1050");
settings.insert("Title","My Application");

如果您想坚持使用 STL,可能是一种方法。

于 2012-08-24T21:00:41.737 回答
2

一种解决方案可能是定义 ISetting 接口,例如:

class ISetting{
public:
  virtual void save( IStream* stream ) = 0;
  virtual ~ISetting(){}
};

之后,您可以使用地图来存储您的设置:

std::map< std::string, ISetting* > settings;

布尔设置的一个示例是:

class BooleanSetting : public ISetting{
private:
  bool m_value;
public:
  BooleanSetting(bool value){
    m_value = value
  }

  void save( IStream* stream ) {
    (*stream) << m_value;
  }

  virtual ~BooleanSetting(){}
};

到底:

settings["booleansetting"]=new BooleanSetting(true);
settings["someothersetting"]=new SomeOtherSetting("something");
于 2012-08-24T21:03:52.360 回答
1

一种可能的解决方案是创建一个Settings看起来像的类

class Settings {
    public:
        Settings(std::string filename);

        bool getFullscreen() { return Fullscreen; }
        // ...etc.

    private:
        bool Fullscreen;
        int Width;
        int Height;
        std::string Title;
};

这假设设置存储在某个文件中。可以实现构造函数以使用您选择的任何格式读取设置。当然,这样做的缺点是您必须修改类以添加任何其他设置。

于 2012-08-24T21:22:23.547 回答
0

要回答您的问题,您可以使用boost::anyorboost::variant来实现您想要的。我认为变体更好地开始。

boost::variant<
    std::string,
    int,
    bool
  > SettingVariant;

std::map<std::string, SettingVariant> settings;

为了不回答您的问题,我不建议使用无类型容器。强类型为您提供了一种构建代码的方法,当您做一些细微的错误时,编译器会给您错误。

struct ResolutionSettings {
  bool full_screen;
  size_t width;
  size_t height;
  std::string title;
};

然后只需一个简单的免费功能即可获得默认设置。

ResolutionSettings GetDefaultResolutionSettings() {
  ResolutionSettings settings;
  settings.full_screen = true;
  settings.width = 800;
  settings.height = 600;
  settings.title = "My Application';
  return settings;
}

如果您正在从磁盘读取设置,那么这是一个有点不同的问题。我仍然会编写强类型设置结构,并让您的弱类型文件阅读器使用boost::lexical强制转换来验证字符串转换是否有效。

ResolutionSettings settings;
std::string str = "800";
size_t settings.width = boost::lexical_cast<size_t>(str);

您可以将所有磁盘读取逻辑包装在不与任何其他功能耦合的另一个函数中。

ResolutionSettings GetResolutionSettingsFromDisk();

我认为这是最直接和最容易维护的(特别是如果您对 C++ 不太熟悉)。

于 2012-08-24T20:58:52.120 回答