如果要调用多个成员函数,对象编辑器是否是一种好方法?
Is Object Editor a good approach if there are multiple member functions to call?
>我经常对这样的类成员函数的顺序调用感到恼火(忽略新用法,它适用于Qt,但它与Qt无关)
A a = new A();
a->fun1("one");
a->fun2(1, 2);
...
a->fun10("end");
我一直觉得这样的代码应该写成简单的指令,而不是一个占主导地位的项目行。
Qt的简单例子:
auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);
但我宁愿在简单的单行中执行此操作,而不是那样做。所以我写了这样的东西:
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));
这是好方法,除了不是类型安全的吗?(我可以忍受)
---编辑
---另一种类型安全的方法是:
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));
但这需要每次都重新创建编辑器对象,并添加额外的使用代码。
让我们战斗,
第一个对手,香草方法:
auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);
优点:
- 这是所有人这样做的方式
- ~30列宽,可读性很强
缺点:
- 需要 5 行 每次重复动作
spinBox
的主题
第二个对手,花式方法:
newLayout->addWidget(EDITOR2<QSpinBox>(new QSpinBox)(&QSpinBox::setRange, -100, 100)(&QSpinBox::setValue, 50)(&QSpinBox::setSingleStep, 5));
优点:
- 它需要一行
- 这是一种更实用的编程方法
缺点:
- 这是一条很长的线,很难阅读
- 默认情况下,人们的反应将是
wtf
的变体,因为他们不习惯它 - 实例名
spinBox
不重复,但现在QSpinBox
类名是
最终的选择是你的,基于你对每个点的重视程度,我确实在一行中比较了第二种方法,就好像你使用换行符一样,你基本上回到了你试图修复的原始内容
以我的拙见,为如此少的东西增加一个类开销是不值得的,我是那种对原始方法的缺点感到恼火的人,杀手点是可读性损失,这很可能会迫使你使用换行符,这反过来几乎意味着你白白做了这一切。
这有点像一个流畅的接口,除了没有一堆命名函数作为构建器,你只需使用指向成员的指针。如果你喜欢这种事情(主要是基于意见),这是一种合理的方法,但完全缺乏类型安全性是不行的。
不管std::cerr
都不是进行错误处理的好方法。throw
或assert
.
你可以改进很多:
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());
尽管如果界面已经很流畅,这可能会更好:
newLayout->addWidget(
(new QSpinBox)
->range(-100, 100)
->value(50)
->singleStep(5));
但这意味着编写一堆新的命名函数,你肯定(可能?)不会这样做。
std::invoke()
为 C++17,但在 C++11 中可实现。
相关文章:
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 有没有一种方法可以在编译时获得作用域类名
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 有没有一种方法可以使用placement new将堆叠对象分配给分配的内存
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 在 c++ 中,有一种方法可以创建一个包含地图作为值的树状地图?
- C++ STD 函数运算符:有没有一种方法可以通过函数将一个向量映射到另一个向量上?
- 有没有一种方法可以捕获进程中的堆栈溢出?C++Linux
- 我想直接在结构中插入,但没有一种方法可以正确避免填充问题
- 类中一种方法的部分专用化
- 只需要知道我在c ++中打印模式的方式是否有效,或者有另一种方法可以有效地做到这一点
- 有没有一种方法可以忽略Qt c++中的文件名大小写敏感性?
- 有没有一种方法可以使用图形在C++中逐个字母地打印控制台中的文本
- 有没有一种方法可以将数据从javascript发送到exe文件
- 有没有一种方法可以从函数中返回一个新对象或对现有对象的引用
- 在C++中,有没有一种方法可以让我在不传递参数的情况下拥有一个函数
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 有没有一种方法可以使用typedef中的参数名称
- C++中有没有一种方法可以通过指定列表中的每个成员变量来构造对象