【问题标题】:Settings Array in C++C++ 中的设置数组
【发布时间】:2012-08-20 10:33:28
【问题描述】:

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

在 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
};

如何使用flexible datatypesassociative array 的结构建模?

【问题讨论】:

  • 通常人们会简单地使用一个结构。为什么需要灵活的数据类型或关联数组?

标签: c++ arrays types settings


【解决方案1】:

具有不同数据类型的关联数据结构正是 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一个重载的构造函数,根据指向成员的指针类型,它的行为会有所不同。

【讨论】:

  • 这不太优雅,因为每次需要添加新设置类型时都需要更改此文件。
【解决方案2】:

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

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

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

std::map<std::string, boost::variant<bool,int,std::string> >

【讨论】:

  • boost:variant 类型是我需要的,但也许有一个解决方案不使用库来完成此任务。因为我再也不需要在这个游戏应用程序中使用灵活类型了。
  • Is C a strongly typed language? 我认为这个讨论同样适用于 C++。在这里的上下文中,也许说 C++ 是一种静态类型语言会更准确。
  • @sharethis:我真的会选择预打包的解决方案。如果您不介意内存中的额外成本,您可以创建一个struct,其中包含一个确定数据类型的selector,然后为每种类型创建一个字段。但请注意,即使这样也需要更多内存,并且您将不得不花一些时间让它工作(找到合适的接口,以免对用户代码造成太大负担......)
  • @Code-Guru:我以前听说过这个论点,我真的不想重温它......如果你愿意,我个人认为 C++ 是一种强类型语言。但话又说回来,我没有强制转换也没有void*...
  • 很公平。除了触及那场小型圣战之外,您的回答还有一些很好的信息;-)
【解决方案3】:
#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,这可能是一种方法。

【讨论】:

  • 我已经尝试过地图。不幸的是,您的回答无法解决我需要的值的整数和布尔数据的问题。
  • @sharethis 您需要添加一个额外的层来将std::string 值转换为boolint。并非完全不可能,但肯定需要一些工作。
  • 这就是我不想将Width 存储为字符串的原因。请看我的示例代码。 :-)
【解决方案4】:

一种解决方案可能是定义 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");

【讨论】:

  • 我真的很喜欢这种方法。我现在唯一的问题是如何以简单的方式声明数组(而不是映射),例如= {...}
  • @sharethis 为什么需要阵列?我认为 std::map 更适合这里。
  • 因为我想在单个命令中声明孔配置,就像在我的示例代码中一样。
  • @sharethis 我已经编辑了我的帖子,看看你可以为每行设置一个与你使用数组实现的非常相似的设置。
  • 类似,但我必须在每一行中写入设置。顺便说一句,&lt;&lt; 有一个错误,说“没有运算符匹配”。
【解决方案5】:

一种可能的解决方案是创建一个类似于 Settings 的类

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

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

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

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

【讨论】:

    【解决方案6】:

    要回答您的问题,您可以使用boost::anyboost::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++ 不太熟悉)。

    【讨论】:

    • 使用std::map 也可能会有所帮助。
    • @Tom Kerr。你能给我解释一下吗?我从未听说过boost。对于这个 simple 任务,最好不要使用库。
    • 这不是真的“简单”。 PHP 是动态类型的,所以这样的事情是很自然的,但存储任意数据并不是您通常在静态类型语言中所做的事情。事先弄清楚你想存储什么。
    • @GWW。我之前尝试过使用地图。地图的问题在于一步声明它们的字段。我不需要成百上千的map.inserts。
    • @Cubic。我用 italic 字母写了 simple,因为我知道这不是 C++ 中的基本任务。我现在想存储整数、字符串和布尔值。 :-)
    猜你喜欢
    • 2012-06-12
    • 2023-04-05
    • 1970-01-01
    • 2022-11-11
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 2018-11-05
    相关资源
    最近更新 更多