代码之家  ›  专栏  ›  技术社区  ›  tauran

带有C语言语句的Python++

  •  14
  • tauran  · 技术社区  · 14 年前

    我正在尝试实现类似于 Python with statement 在C++中。由于我计划主要将其与Qt OpenGL一起使用,因此这些方法被称为 bind release (在Python中 __enter__ , __exit__ )。

    我想出的代码:

    标题:

    #include <iostream>
    #include <vector>
    
    class With
    {
    public:
        class A
        {
        public:
            virtual ~A() { }
        };
    
        template <typename T>
        class B : public A
        {
        public:
            B(T& _t) : t(_t)
            {
                t.bind();
            }
    
            virtual ~B()
            {
                t.release();
            }
    
            T& t;
        };
    
        template <typename... Args>
        With(Args&... args)
        {
            set(args...);
        }
    
        ~With();
    
        template <typename T, typename... Args>
        void set(T& t, Args&... args)
        {
            set(t);
            set(args...);
        }
    
        template <typename T>
        void set(T& t)
        {
            a.push_back(dynamic_cast<A*>(new B<T>(t)));
        }
    
        std::vector<A*> a;
    };
    

    中央处理器:

    With::~With()
    {
        for (auto it = a.begin(); it != a.end(); ++it)
        {
            delete *it;
        }
    }
    

    用法:

    class X
    {
    public:
        void bind() { std::cout << "bind x" << std::endl; }
        void release() { std::cout << "release x" << std::endl; }
    };
    
    class Y
    {
    public:
        void bind() { std::cout << "bind y" << std::endl; }
        void release() { std::cout << "release y" << std::endl; }
    };
    
    int main()
    {
        X y;
        Y y;
    
        std::cout << "start" << std::endl;
        {
            With w(x, y);
            std::cout << "with" << std::endl;
        }
    
        std::cout << "done" << std::endl;
    
        return 0;
    }
    

    问题:

    1. 需要 class A class B 感觉有点笨拙。还有更好的选择吗?
    2. 使用过程中有任何倒退吗 && 而不是 & ?这将使临时对象的使用成为可能(例如。 With w(X(), y); )
    4 回复  |  直到 4 年前
        1
  •  14
  •   David Lawson    8 年前

    with语句是在python中完成C++中已经很正常的事情的一种方法。它被称为RAII:资源获取是初始化。

    在python中,当创建类对象时 __init__ 方法被调用(但这不是一个严格的保证)。这个 __del__ 方法由垃圾收集器在对象不再使用后的某个时刻调用,但它不是确定性的。

    在C++中,析构函数是在定义良好的点调用的,因此不需要 with

    我建议你 使用 类似B类的东西(不需要A类或With)。

    template <typename T>
    class B {
    public:
        B(T& t) : m_t(t){
            m_t.bind();
        }
        ~B() {
            m_t.release();
        }
        T& m_t;
    }
    

    这样使用:

    {
        B<X> bound_x(x);  // x.bind is called
        B<Y> bound_y(y);  // y.bind is called
        // use x and y here
    } // bound_x and bound_y is destroyed here 
      // so x.release and y.release is called    
    
        2
  •  2
  •   Puppy    14 年前

    它附带了语言,被称为RAII。

    struct X {
        X() { std::cout << "bind\n"; }
        ~X() { std::cout << "release\n"; }
    };
    int main() {
        X x;
    }
    
        3
  •  1
  •   David Lawson    8 年前

    一种可能的解决方案:

    template <typename T>
    void with(T *t, std::function<void ()> fn) {
        t->bind();
        fn();
        t->unbind();
    }
    

    用法:

    with(object, []() {
        // object bound
    });
    
        4
  •  1
  •   tinkerbeast    4 年前

    虽然我同意这里的答案,即RAII是管理资源生命周期的C++方法,但我个人认为RAII不够健壮,无法处理所有场景。我发现构造函数/析构函数范式对于内存资源管理来说已经足够好了。对于其他资源(例如,文件描述符、套接字、定时器等),使用显式资源管理块提供了更大的健壮性。我的推理如下-

    • 假设您的资源是一个文件描述符。您想在析构函数中关闭它,但发生了一些IO错误。你是做什么的?从析构函数中抛出异常是一个非常糟糕的选择。忽略错误可能也同样糟糕。对于这种情况,其他语言提供了资源块机制,将资源管理与对象生命周期解耦。
    • C++在其标准库中已经面临这个问题。这个 std::mutex 锁定和解锁方法本质上是资源持有者。然而 std::互斥 无法绑定到锁定/解锁。所以我们有一个包装类 std::lock_guard 仅用于锁定/解锁RAII目的。虽然这是一个优雅的解决方案,但问题是缺乏通用性。想象一下,每个与资源管理相关的类都会产生自己的包装器类。如果我们有一个标准定义的资源管理机制,我们可能可以用一个包装器模板来统一地做到这一点。

    我希望这能让RAII的支持者相信,在RAII范式的基础上建立一个标准指定的资源管理模型是好的。

    关于Python的问题 with 条款:一个非常天真的实现如下:

    // Standardised interface for resource managers.
    template<typename T>
    concept Withable = requires(T t) {
        { t.bind() } -> std::same_as<void>;
        { t.release() } -> std::same_as<void>;
    };
    
    // Universal wrapper for all resource managers.
    template<Withable T>
    struct WithWrapper {
        T* ref_;
    
        WithWrapper(T& obj)
        : ref_{&obj}
        { ref_->bind(); }
    
        ~WithWrapper() { ref_->release(); }
    
        T& get() { return *ref_; }
    };
    
    // In case we want the with keyword.
    #define with(...) if (__VA_ARGS__; true)
    

    你的案子的用法是-

    // Making sure your types are compatible.
    static_assert(Withable<X>);
    static_assert(Withable<Y>);
    
    // Using the with and wrapper.
    int main() {
        X y;
        Y y;
        // With my naive implementation it's not really possible to declare two
        // separate types in same initialisation block. However even with two 
        // "with"s, the execution behaviour is extacly the same as Python.
        with (auto xw = WithWrapper(x)) { with (auto yw = WithWrapper(y)) {
            // Do something with xw and yw ...
        } }
    }
    
    推荐文章