【问题标题】:Cleanup / Reset before or after operation操作前或操作后的清理/重置
【发布时间】:2014-01-20 20:58:32
【问题描述】:

原来是这样的:

我想使用一个类 3rd 方库(即Poco)来解析一个 JSON 字符串。这个Parser 类的设计方式是,在每次解析(操作)之后,它必须被重置(通过调用reset(),谁会想到呢?)。我在我正在实现的类中的不同位置多次重复使用Parser,因此这对我很重要。即使类已经重置,调用reset() 也是无害的。

因此我的问题是:我应该确保在每次操作之前重置解析器,还是应该确保在操作之后重置解析器。

第一种情况很方便,因为它保证了我的解析器在运行之前处于正确的状态,但同时我觉得很脏,因为我让解析器处于无效状态,其他人可能会认为我重置了解析器。

我不喜欢第二种情况,因为我担心我可能会错过我的方法可能退出的情况(例如未捕获的异常),使解析器处于无效状态,而其他人希望它这次肯定有效。当然,这可以通过使用 RAII(如作用域锁)来解决,但这对我来说听起来有点过度设计。

另一种解决方案是结合这两种情况,因此:重置、解析并再次重置;但这是一种多余的 IMO。


我在这里真的不确定,无法决定任何这些情况,我希望您的意见(基于与设计相关的考虑,不要关闭我的问题!;))将帮助我选择。

【问题讨论】:

    标签: c++ poco


    【解决方案1】:

    我建议完全不要使用 Poco,而是查看 JsonCpp。 Poco 通过让你在其上调用 reset 做出的设计选择相当奇怪。 除此之外,RAII 方法相当简单:

    template <typename T>
    struct ScopedParser {
        T& parser; // parser object
    
        ScopedParser(T& p) : parser(p) {} // set parser in constuctor
        ScopedParser(ScopedParser&&) = delete; // no moving
        ScopedParser(const ScopedParser&) = delete; // no copying
    
        ~ScopedParser() {
            parser.reset(); // reset it
        }
    }
    

    使用示例:

    void myFunc() {
        Poco::JSONParser p;
        ScopedParser<Poco::JSONParser> pScoped(p);
    }
    

    【讨论】:

    • +1 对于JsonCpp的概念,我可能会看一下,但目前我不想引入新的依赖项,
    【解决方案2】:

    这似乎适合将解析器嵌入到 RIIA 样式类中。 目标是为您的解析器创建一个唯一的“用户”。

    这是一个快速的模型:

    class MyParserBuilder
    {
    public:
    
      class MyParser
      {
      public:
        MyParser(poco::Parser& parser, MyParserBuilder& builder) : 
        _parser(parser), 
        _builder(builder)
        {
    
        }
    
        ~MyParser() 
        { 
          _builder.reset(); 
        }
    
       // Either expose some functions from the parser or use 
       // something like:
        poco::Parser& operator*() { return _parser; }
    
      private:
        poco::Parser& _parser;
        MyParserBuilder& _builder;
      };
    
      MyParserBuilder() : _used{false} {}
    
      std::unique_ptr<MyParser> build() 
      { 
          if (_used) // Or lock ?
          {
            return std::unique_ptr<MyParser>();
          }
          _used = true;
          return std::unique_ptr<MyParser>(new MyParser(_parser, *this)); 
        }
    
        void reset()
        {
          _parser.reset();
          _used = false;
        }
    
      private:
        bool _used; 
        poco::Parser _parser;
    };
    

    http://ideone.com/6wwg20

    为了安全起见,我还要添加类似“脏”状态的东西,这样您就只能执行每次 MyParser 实例化需要重置一次的操作。

    【讨论】:

      【解决方案3】:

      第一种情况对我来说听起来不错,只要“其他”只是在同一个私有 Parser 对象上运行的其他成员函数。

      结合这两种情况只会减少潜在的错误。仍然可能存在未捕获的异常,其他人可能期望解析器已重置。

      最好的选择仍然是类 RAII 锁的辅助对象。我不认为像 5 行这样的类是过度工程化的。如果解析器被多个线程使用,您可以在其中添加一个真正的锁。

      如果对您的应用程序的性能没有问题,甚至可以为每个函数使用一个新的作用域解析器。这甚至可能比同步访问同一个解析器更快。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-21
        • 1970-01-01
        • 2015-06-21
        • 2012-12-11
        • 1970-01-01
        • 2011-12-02
        相关资源
        最近更新 更多