将具有特定 pattern-match 的所有声明推送到向量中的优雅方法?
Elegant way to push all declarations with a particular pattern-match into vector?
我正在重构一些代码,我很好奇是否有现代 C++ 功能允许我动态地将所有匹配的声明推送到一个向量中,而无需手动输入每个参数名称。
例如;
我的 header 中有以下声明(在别处实例化);
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;
然后在别处我将这些全部推入一个向量one-by-one;大量的小部件有点痛苦。特别是如果我添加一个新的 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,所以一切都完成了“in-house”。我通常用 Lisp 编程,只是有时我会退回到我的旧 go-to 语言 C++...
特别是对于维护,它可能会有所回报并减少“疏忽错误”。
由于我使用的是 cl-ppcre
,这是 Lisp 正则表达式引擎,您甚至可以考虑利用自己做事的习惯。
例如,如果您在 header 中的单个 class 中总是有 1 个 header 文件和您的按钮声明,您可以只使用 header 文件而不是将按钮声明复制并粘贴到 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> (vectorize-buttons (button-names))
"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_file_browse);
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_*);
不,不是内置的。但是,FLTK 使动态构建 vector
成为可能。然而,C++ 代码中对象的名称在编译过程中丢失了,因此您需要找到一些其他标准。
幸运的是,Fl_Widget
像Fl_Button
一样有一个user_data()
函数可以return一个void*
或long
(真的void*
,您需要转换为 long
)。所以,如果你不想所有 Fl_Button
的vector
,你可以在设计windows的时候设置user_data
。在这里,我使用 fluid
在我希望包含在 vector
:
中的按钮上设置 user_data
值
这是一个函数,用于查找放置在另一个小部件(如 Fl_Window
)中的特定类型的所有小部件,并对找到的小部件应用一元谓词。如果谓词returns 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
s 的小部件作为参数调用上述函数:
#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
.
我正在重构一些代码,我很好奇是否有现代 C++ 功能允许我动态地将所有匹配的声明推送到一个向量中,而无需手动输入每个参数名称。
例如;
我的 header 中有以下声明(在别处实例化);
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;
然后在别处我将这些全部推入一个向量one-by-one;大量的小部件有点痛苦。特别是如果我添加一个新的 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,所以一切都完成了“in-house”。我通常用 Lisp 编程,只是有时我会退回到我的旧 go-to 语言 C++...
特别是对于维护,它可能会有所回报并减少“疏忽错误”。
由于我使用的是 cl-ppcre
,这是 Lisp 正则表达式引擎,您甚至可以考虑利用自己做事的习惯。
例如,如果您在 header 中的单个 class 中总是有 1 个 header 文件和您的按钮声明,您可以只使用 header 文件而不是将按钮声明复制并粘贴到 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> (vectorize-buttons (button-names))
"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_file_browse); 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_*);
不,不是内置的。但是,FLTK 使动态构建 vector
成为可能。然而,C++ 代码中对象的名称在编译过程中丢失了,因此您需要找到一些其他标准。
幸运的是,Fl_Widget
像Fl_Button
一样有一个user_data()
函数可以return一个void*
或long
(真的void*
,您需要转换为 long
)。所以,如果你不想所有 Fl_Button
的vector
,你可以在设计windows的时候设置user_data
。在这里,我使用 fluid
在我希望包含在 vector
:
user_data
值
这是一个函数,用于查找放置在另一个小部件(如 Fl_Window
)中的特定类型的所有小部件,并对找到的小部件应用一元谓词。如果谓词returns 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
s 的小部件作为参数调用上述函数:
#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
.