Menu

Why should not a widget be copyable or movable?

In C++, objects often provide copy ability and moveability to duplicate or move from an existing object. But it was by making a widget copyable or movable caused more problems than it solves.

Dilemma

Suppose the button widget is copyable, E.g.

form fm1;
button btn1(fm1);

form fm2;
button btn2(fm2);

btn1 = btn2; //Assume that copy is allowed.

We can say that after copying, the caption, position and size of btn1 is same to btn2′s. But, which should be the parent of btn1? fm1 or fm2? and another important question is that the event handlers of btn2 should be copied to btn1?

Error-Prone

A widget object is unlike other object that types are string, int and so on. A widget object owns event handlers.
A basic example that illustrates how to make an event handler for a widget.

class A
{
public:
    A() = default;

    A(widget& wd)
        :btn_(wd)
    {
        //Nana feature: we should not take care about the event handler
        //when the btn_ is being destructed, the event handler will be
        //deleted automatically.
        btn_.events().click([this]
        {
            //Access the button.
            auto text = btn_.caption();
        });
    }
private:
    button btn_;    
};

It's quite simple. But what would happen if a widget was movable? It would be erroneous, let’s prove it.

form fm;
A a;
{
    A b(fm);
    a = std::move(b);
    //Now, take a look at the click event handler, it still accesses the b.btn_,
    //the problem is that b and b's btn_ are now moved from state. It's unspecifed.
}

//Now, b is not existing. So the click event handler will crashes the program
//after clicking the button.

Complexity

to make the class A correct, we must face the complexity.

class A
{
public:
    A() = default;

    A(widget& wd)
        :btn_(wd)
    {
        _m_make_evt();
    }

    A(A&& other)
        : btn_(std::move(other.btn_))
    {
        btn_.umake_event(other.evt_click_);
        other.evt_click_ = nullptr;

        _m_make_evt();
    }

    A& operator=(A&& other)
    {
        if(this != &other)
        {
            other.btn_.umake_event(other.evt_click_);
            other.evt_click_ = nullptr;

            btn_ = std::move(other.btn_);
             _m_make_evt();
        }
        return *this;
    }
private:
    void _m_make_evt()
    {
        evt_click_ = btn_.events().click(std::bind(&A::_m_clicked, this));
    }

    void _m_clicked()
    {
        //Access the button.
        auto text = btn_.caption();
    }
private:
    button btn_;
    event_handle evt_click_;
};

It's too complex! We have to take careful about the move-constructor/assignment for all classes which have widgets. The complexity is not about the design of widgets, it's about the use of widgets.

Posted by Jinhao 2013-05-14

Anonymous
Anonymous

Add attachments
Cancel