如果有多个成员函数要调用,对象编辑器是一个很好的方法吗?

时间:2017-02-09 15:09:52

标签: c++ templates c++14

我常常被这样的类成员函数的顺序调用所困扰(忽略新用法,它对Qt来说,但它不是严格与Qt相关的)

auto* spinBox = new QSpinBox();
spinBox->setRange(-100, 100);
spinBox->setValue(50);
spinBox->setSingleStep(5);
newLayout->addWidget(spinBox);

我一直觉得这样的代码应该写成简单的指令,而不是一个主导行的项目。

Qt的简单例子:

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

使用方法:

result = sorted([l.split() for l in sys.stdin.readlines()], key=lambda l: int(l[1]), reverse=True)
print(result)

但这需要每次都重新创建编辑器对象,并添加额外的使用代码。

2 个答案:

答案 0 :(得分:2)

让我们一起战斗,

第一个对手,香草逼近:

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现在是

根据您对每个点的重视程度,最终的选择是你的,我确实比较了一行中的第二种方法,就像你使用换行符一样,基本上回到你试图修复的原始内容< / p>

在我的拙见中,为这么少的人增加一个班级开销是不值得的,我是那种因原始方法的缺点而烦恼的人,杀手点是可读性损失,很可能会迫使你使用换行几乎意味着你已经完成了所有这一切。

答案 1 :(得分:1)

这有点像fluent interface,除了将一堆命名函数作为构建器之外,您只需使用指向成员的指针。这是一种合理的方法,如果你涉及这种事情(主要是基于意见的),但完全缺乏类型安全是不行的。

无论std::cerr是不是处理错误的好方法。 throwassert

你可以改进很多:

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中实现。