将具有特定模式匹配的所有声明推送到向量中的优雅方法?

问题描述

我正在重构一些代码,想知道是否有现代 C++ 功能允许我将所有匹配的声明动态推送到向量中,而无需手动输入每个参数名称

例如;

我的标题中有以下声明(在其他地方实例化);

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;

然后在其他地方,我将所有这些都一个一个地推入一个向量中;使用大量小部件有点痛苦。特别是如果我添加一个新的 Fl_Button,我还需要将它添加到这段代码中。

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);

是否有一个漂亮的 C++ 功能可以让我输入像下面这样优雅的东西?

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

解决方法

只是为了展示,如果您手头有强大的脚本语言,代码生成是多么简单。我通常使用 Common Lisp,因为无论如何我都使用 emacs,所以这一切都是“内部”完成的。而且我通常用 Lisp 编程,只是有时我会退回到我的旧语言 C++...

特别是对于维护而言,它可能会带来回报并减少“疏忽错误”。 由于我使用了 cl-ppcre 是 Lisp 正则表达式引擎,你甚至可以考虑利用你做事对你有利的习惯。

例如,如果在该头文件的单个类中总是有 1 个带有按钮声明的头文件,则可以只使用头文件而不是将按钮声明复制并粘贴到 lisp 代码中...

下面的几行 lisp 只是展示了基础知识。如果你不喜欢 Lisp,我想你可以在 APL、Haskell、Perl、Julia 甚至 Python 中做同样的事情。

(defparameter *my-buttons*
  "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;")

(defparameter *button-decl-pattern*
  (ppcre:create-scanner "\\s*Fl_Button\\s*\\*\\s*(button_\\w+);"))

(defun button-names (&optional (decls *my-buttons*))
  (with-input-from-string (stream decls)
    (loop for line = (read-line stream nil)
      while line
      collecting
      (aref
       (nth-value 1 (ppcre:scan-to-strings
             *button-decl-pattern*
             line))
       0))))

(defun vectorize-buttons (buttons)
  (with-output-to-string (stream)
    (format stream "std::vector<Fl_Button *> vector_fl_button;~%")
    (loop for button in buttons do
      (format stream
          "vector_fl_button.push_back(~A);~%"
          button))))

CL-USER>(矢量化按钮(按钮名称))
"std::vector 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_file_browse); vector_fl_button.push_back(button_perform_sentiment); vector_fl_button.push_back(button_txt_folder_browse); "

,

是否有一个漂亮的 C++ 功能可以让我输入像下面这样优雅的东西?

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

不,不是内置的。但是,FLTK 可以动态构建 vector。但是,C++ 代码中对象的名称在编译过程中会丢失,因此您需要找到其他一些条件。

幸运的是,Fl_Widgets,比如 Fl_Button 有一个 user_data() 函数,可以返回一个 void*long(实际上是一个 void*您需要强制转换为 long)。因此,如果您不想要 all vectorFl_Button,您可以在设计窗口时设置 user_data。在这里,我使用 fluid 在我喜欢包含在 user_data 中的按钮上设置 vector 值:

enter image description here

这是一个函数,用于查找放置在另一个小部件(如 Fl_Window)中的特定类型的所有小部件,并对找到的小部件应用一元谓词。如果谓词返回 true,则存储指针。

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

然后,您可以使用要从中获取 Fl_Button 作为参数的小部件调用上述函数:

#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;
};

如果你也需要在孙子中找到按钮,你可以让函数递归。

如果您已经将 user_data 用于其他用途,您可以将上面示例中的谓词替换为其他属性,这些属性对于您希望在 vector 中拥有的所有按钮都是通用的。