Search code examples
c++fltk

Elegant way to push all declarations with a particular pattern-match into vector?


I'm refactoring some code and curious if there is a modern C++ feature that allows me to dynamically push all matching declarations into a vector without having to manually type out every parameter name.

For example;

I have the following declarations in my header (instantiated elsewhere);

Fl_Button * button_file_browse;
Fl_Button * button_parse_txt;
Fl_Button * button_parse_xer;
Fl_Button * button_file_browse;
Fl_Button * button_perform_sentiment;
Fl_Button * button_txt_folder_browse;

Then elsewhere I push these all into a vector one-by-one; it's a little painful with tons of widgets. Especially if I add a new Fl_Button I need to go and add it to this piece of code as well.

std::vector<Fl_Button *> vector_fl_button;
vector_fl_button.push_back(button_file_browse);
vector_fl_button.push_back(button_parse_txt);
vector_fl_button.push_back(button_parse_xer);
vector_fl_button.push_back(button_perform_sentiment);
vector_fl_button.push_back(button_txt_folder_browse);

Is there a beautiful C++ feature that allows me to type something as elegant as below?

std::vector<Fl_Button *> vector_fl_button;
vector_fl_button.push_back(button_*); // Pushes all pointers starting with button_

Solution

  • Is there a beautiful C++ feature that allows me to type something as elegant as below?

    std::vector<Fl_Button *> vector_fl_button;
    vector_fl_button.push_back(button_*); 
    

    No, not built-in. However, FLTK makes it possible to build the vector dynamically. The name of the objects in the C++ code is however lost in compilation so you'll need to find some other criteria.

    Fortunately, Fl_Widgets, like Fl_Button have a user_data() function that can return a void* or long (really a void* that you need to cast to a long). So, if you don't want a vector of all Fl_Buttons, you can set user_data when designing the windows. Here I've used fluid to set the user_data value on a button that I like to be included in the vector:

    enter image description here

    Here's a function to find all widgets of a certain type placed inside another widget (like a Fl_Window) and to apply a unary predicate on the found widgets. If the predicate returns true, it stores the pointer.

    widget_funcs.hpp

    #ifndef WIDGET_FUNCS_HPP
    #define WIDGET_FUNCS_HPP
    
    #include <FL/Fl_Group.H>
    
    #include <algorithm>
    #include <iterator>
    #include <vector>
    
    template <class T, class UnaryPredicate>
    auto find_widgets_of_type(Fl_Widget* w, UnaryPredicate pred) {
        std::vector<T*> rv;
    
        // check if "w" is a container class (essentially a dynamic_cast):
        Fl_Group* g = w->as_group();
    
        if(g) {
            // Copy all the pointers that can be dynamically casted to T*
            // and that fulfills the conditions in the predicate function.
            std::for_each(g->array(), std::next(g->array(), g->children()),
                          [&rv,&pred](Fl_Widget* child) {
                              auto isT = dynamic_cast<T*>(child);
                              if(isT && pred(isT)) rv.push_back(isT);
                          });
        }
        return rv;
    }
    
    template <class T>  // use this overload if you want all Fl_Buttons
    auto find_widgets_of_type(Fl_Widget* w) {
        return find_widgets_of_type<T>(w, [](const T*){ return true; });
    }
    
    #endif
    

    You can then call the above function with the widget you'd like to get Fl_Buttons from as an argument:

    #include "widget_funcs.hpp"
    
    class SomeClass {
    public:
        SomeClass(Fl_Widget* window) :
            vector_fl_button(
                find_widgets_of_type<Fl_Button>(window, [](Fl_Button* b) {
                    return reinterpret_cast<long>(b->user_data()) == 1;
                }))
        {}
        // ...
    
    private:
        std::vector<Fl_Button*> vector_fl_button;
    };
    

    If you need to find buttons in grandchildren too, you could just make the function recursive.

    If you already use user_data for something else, you can replace the predicate in the example above with some other property that is common for all the buttons you'd like to have in the vector.