modern-gtk2: Delay construction of Gtk::TreeModel* objects (!17)

C++ initialises static member variables before main() is called.
Therefore the static members of:
    struct Slots
    {
        static Gtk::TreeModelColumn<Glib::ustring> text;
        static Gtk::TreeModelColumn<bool> sensitive;
    private:
        static Gtk::TreeModel::ColumnRecord record_;
    };
are constructed before Gtk::Main() is called in main().  However the
Gtkmm documentation specifically says that they must be constructed
afterwards [1].

Resolve this by using the Construct On First Use Idiom [2] to delay
initialisation until the slots are first used.  Normally this idiom uses
static local objects, however it is being applied to class static
objects here because the objects are accessed in many methods.  The
downside of this approach is that the objects are never destructed,
which memory analysers like Valgrind could see as a memory leak, but
that is actually deliberate.  That leak can be removed once we can use
C++11 and std::unique_ptr.

[1] gtkmm: Gtk::TreeModelColumnRecord Class Reference
    https://developer.gnome.org/gtkmm/2.24/classGtk_1_1TreeModelColumnRecord.html#details

    "Neither TreeModel::ColumnRecord nor the TreeModelColumns contain
    any real data - they merely describe what C++ type is stored in
    which column of a TreeModel, and save you from having to repeat that
    type information in several places.

    Thus TreeModel::ColumnRecord can be made a singleton (as long as you
    make sure it's instantiated after Gtk::Main), even when creating
    multiple models from it.
    "

[2] C++ FAQ / How do I prevent the "static initialization order
    problem"?
    https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use

Closes !17 - Gtk2 modernisation
This commit is contained in:
Luca Bacci 2018-10-27 15:27:10 +02:00 committed by Mike Fleetwood
parent 6bfa95d427
commit c602170faa
2 changed files with 31 additions and 32 deletions

View File

@ -144,15 +144,20 @@ public:
Item_Collection items(); Item_Collection items();
Item_Collection_Const items() const; Item_Collection_Const items() const;
struct Slots class Slots
: public Gtk::TreeModelColumnRecord
{ {
static Gtk::TreeModelColumn<Glib::ustring> text; public:
static Gtk::TreeModelColumn<bool> sensitive; Gtk::TreeModelColumn<Glib::ustring> m_text;
Gtk::TreeModelColumn<bool> m_sensitive;
private: Slots()
static Gtk::TreeModel::ColumnRecord record_; {
friend class OptionStore; add(m_text);
add(m_sensitive);
}
}; };
static Slots *m_slots;
protected: protected:
explicit OptionStore(); explicit OptionStore();

View File

@ -27,10 +27,7 @@ namespace GParted
// NOTE: As stated in Gtkmm3 documentation, slots can be shared for all model instances. // NOTE: As stated in Gtkmm3 documentation, slots can be shared for all model instances.
// See: Class Reference for Gtk::TreeModelColumn and Gtk::TreeModelColumnRecord. // See: Class Reference for Gtk::TreeModelColumn and Gtk::TreeModelColumnRecord.
// https://developer.gnome.org/gtkmm/3.22/classGtk_1_1TreeModelColumnRecord.html#details // https://developer.gnome.org/gtkmm/3.22/classGtk_1_1TreeModelColumnRecord.html#details
Gtk::TreeModelColumn<Glib::ustring> OptionStore::Slots::text; OptionStore::Slots *OptionStore::m_slots = NULL;
Gtk::TreeModelColumn<bool> OptionStore::Slots::sensitive;
Gtk::TreeModel::ColumnRecord OptionStore::Slots::record_;
OptionStore_Item::OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model, OptionStore_Item::OptionStore_Item(const Glib::RefPtr<OptionStore>& ref_model,
@ -54,36 +51,36 @@ void OptionStore_Item::set(const Glib::ustring& text,
bool sensitive) bool sensitive)
{ {
Gtk::TreeModel::iterator iter = *this; Gtk::TreeModel::iterator iter = *this;
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
void OptionStore_Item::set_text(const Glib::ustring& text) void OptionStore_Item::set_text(const Glib::ustring& text)
{ {
Gtk::TreeModel::iterator iter = *this; Gtk::TreeModel::iterator iter = *this;
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
} }
void OptionStore_Item::set_sensitive(bool sensitive) void OptionStore_Item::set_sensitive(bool sensitive)
{ {
Gtk::TreeModel::iterator iter = *this; Gtk::TreeModel::iterator iter = *this;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
Glib::ustring OptionStore_Item::text() const Glib::ustring OptionStore_Item::text() const
{ {
Gtk::TreeModel::const_iterator iter = *this; Gtk::TreeModel::const_iterator iter = *this;
return (*iter)[OptionStore::Slots::text]; return (*iter)[OptionStore::m_slots->m_text];
} }
bool OptionStore_Item::sensitive() const bool OptionStore_Item::sensitive() const
{ {
Gtk::TreeModel::const_iterator iter = *this; Gtk::TreeModel::const_iterator iter = *this;
return (*iter)[OptionStore::Slots::sensitive]; return (*iter)[OptionStore::m_slots->m_sensitive];
} }
@ -110,8 +107,8 @@ void OptionStore_Item_Collection::push_front(const Glib::ustring& text,
bool sensitive) bool sensitive)
{ {
Gtk::TreeModel::iterator iter = m_ref_model->prepend(); Gtk::TreeModel::iterator iter = m_ref_model->prepend();
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
@ -119,8 +116,8 @@ void OptionStore_Item_Collection::push_back(const Glib::ustring& text,
bool sensitive) bool sensitive)
{ {
Gtk::TreeModel::iterator iter = m_ref_model->append(); Gtk::TreeModel::iterator iter = m_ref_model->append();
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
@ -130,8 +127,8 @@ void OptionStore_Item_Collection::insert(const OptionStore_Item& item,
{ {
Gtk::TreeModel::iterator previous_iter = item.to_iterator_(); Gtk::TreeModel::iterator previous_iter = item.to_iterator_();
Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter); Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter);
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
@ -141,8 +138,8 @@ void OptionStore_Item_Collection::insert(unsigned position,
{ {
Gtk::TreeModel::iterator previous_iter = m_ref_model->children()[position]; Gtk::TreeModel::iterator previous_iter = m_ref_model->children()[position];
Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter); Gtk::TreeModel::iterator iter = m_ref_model->insert(previous_iter);
(*iter)[OptionStore::Slots::text] = text; (*iter)[OptionStore::m_slots->m_text] = text;
(*iter)[OptionStore::Slots::sensitive] = sensitive; (*iter)[OptionStore::m_slots->m_sensitive] = sensitive;
} }
@ -269,13 +266,10 @@ OptionStore_Item_Const OptionStore_Item_Collection::operator[](unsigned position
OptionStore::OptionStore() OptionStore::OptionStore()
: Glib::ObjectBase("GParted_OptionStore") : Glib::ObjectBase("GParted_OptionStore")
{ {
if (!Slots::record_.size()) if (!m_slots)
{ m_slots = new Slots();
Slots::record_.add(Slots::text);
Slots::record_.add(Slots::sensitive);
}
set_column_types(Slots::record_); set_column_types(*m_slots);
} }
@ -323,8 +317,8 @@ void OptionComboBox::pack_cell_renderers()
Gtk::CellRendererText *cell = manage(new Gtk::CellRendererText()); Gtk::CellRendererText *cell = manage(new Gtk::CellRendererText());
pack_start(*cell); pack_start(*cell);
add_attribute(*cell, "text", OptionStore::Slots::text); add_attribute(*cell, "text", OptionStore::m_slots->m_text);
add_attribute(*cell, "sensitive", OptionStore::Slots::sensitive); add_attribute(*cell, "sensitive", OptionStore::m_slots->m_sensitive);
} }