如果要调用多个成员函数,对象编辑器是一种好方法吗? [英] Is Object Editor a good approach if there are multiple member functions to call?
问题描述
我经常对这样的类成员函数的顺序调用感到烦恼(忽略新用法,它是针对Qt的,但并不完全与Qt相关)
I am often annoyed by sequential calls of class member function like this (ignore new usage, it's for Qt, but it's not strictly Qt-related)
A a = new A();
a->fun1("one");
a->fun2(1, 2);
...
a->fun10("end");
我一直觉得这样的代码应该写成简单的指令,而不是项目主导的代码。
I always felt that such code should be written as simple instruction, not a project dominating lines.
来自Qt的简单示例:
auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);
但是我宁愿用简单的单行代码代替它。所以我写了这样的东西:
But I would prefer to do this in simple single line instead of that. So I have written something like this:
class {
public:
template<class X>
auto& operator()(X* ptr) {
this->ptr = ptr;
return *this;
}
template<class X, class R, class... Args>
auto& operator()(R (X::* fun)(Args...), Args... args) {
if(ptr == nullptr) {
std::cerr << "Editor can't edit nullptr" << std::endl;
return *this;
}
auto call = std::mem_fn(fun);
call(*static_cast<X*>(ptr), args...);
return *this;
}
template <class X>
operator X*() {
auto* result = static_cast<X*>(ptr);
ptr = nullptr;
return result;
}
private:
void *ptr = nullptr;
} EDITOR;
现在的用法是:
newLayout->addWidget(EDITOR(new QSpinBox)(&QSpinBox::setRange,-100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
除了类型安全之外,这是一种好方法吗? (我可以接受)
Is it good approach, except not being type-safe? (I could live with that)
---
编辑
---
另一种类型安全的方法是:
Another, type-safe approach would be:
template<class X>
class EDITOR2 {
public:
EDITOR2(X* ptr) {
this->ptr = ptr;
}
template<class R, class... Args>
auto& operator()(R (X::* fun)(Args...), Args&&... args) {
if(ptr == nullptr) {
std::cerr << "Editor can't edit nullptr";
return *this;
}
auto call = std::mem_fn(fun);
call(*ptr, args...);
return *this;
}
operator X*() {
return ptr;
}
X *ptr;
};
使用方式:
newLayout->addWidget(EDITOR2<QSpinBox>(new QSpinBox)(&QSpinBox::setRange, -100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
但这需要每次重新创建编辑器对象,并添加其他使用代码。
But that requires to recreate editor object every time, and adds additional usage code.
推荐答案
这有点像流畅的界面,除了使用一堆命名函数作为构建器之外,您只需使用指向成员的指针即可。这是一种合理的方法,如果您喜欢这种事情(主要是基于观点),但是完全缺乏类型安全性是不行的。
This is kind of like a fluent interface, except rather than having a bunch of named functions to as a builder, you just use pointers to members. It's a reasonable approach, if you're into that sort of thing (primarily opinion based), but the total lack of type safety is not ok.
不管 std :: cerr
不是执行错误处理的好方法。 投掷
或断言
。
Regardless std::cerr
is not a good way to do error handling. throw
or assert
.
您可以对其进行很多改进:
You can improve it a lot:
template <class T>
struct EditorImpl
{
T* ptr;
template <class F, class... Args>
Editor& operator()(F&& f, Args&&... args)
{
std::invoke(std::forward<F>(f), ptr, std::forward<Args>(args)...);
return *this;
}
T* yield() const {
return ptr;
}
};
template <class T>
EditorImpl<T> Editor(T* ptr) { return EditorImpl<T>{ptr}; }
然后您可以编写:
newLayout->addWidget(
Editor(new QSpinBox)
(&QSpinBox::setRange,-100, 100)
(&QSpinBox::setValue, 50)
(&QSpinBox::setSingleStep, 5)
.yield());
尽管如果使用该界面可能会更好已经很流利:
Though this would probably work better if the interface was already fluent:
newLayout->addWidget(
(new QSpinBox)
->range(-100, 100)
->value(50)
->singleStep(5));
但这将意味着编写一堆新的命名函数,您肯定会(可能?)
But that would imply writing a bunch of new named functions, which you're definitely (probably?) not going to do.
std :: invoke()
是C ++ 17,但可在C ++中实现11。
std::invoke()
is C++17, but is implementable in C++11.
这篇关于如果要调用多个成员函数,对象编辑器是一种好方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!