I want to create a class only for setting params for function. I'm using a fluent interface for that. Some function returns the object to setting params and real code will execute in the destructor of this object.
First of all, is it a good idea? Is this a common way to improve code readability when I have a lot of parameters? Or maybe I should use another way to do that? I would be glad to advise on this.
If it's okay, how can I prevent accidentally saving an object to a local variable? In this case, the destructor will be called at end of the variable scope.
Example:
class MyClass {
public:
~MyClass() noexcept {
// some code to execute immediately after setting params A and B
}
// abstract setting methods
MyClass &SetA() { return *this; }
MyClass &SetB() { return *this; }
};
MyClass SomeFunction() {
return MyClass();
}
int main() {
// Correct usage:
SomeFunction()
.SetA()
.SetB();
// destructor here
// I want to prevent it:
auto my_class = SomeFunction();
my_class
.SetA()
.SetB();
{
auto my_class2 = SomeFunction();
my_class2
.SetA()
.SetB();
// destructor of `my_class2` here
}
// destructor of `my_class` here.
return 0;
}
In general the destructor is a good place if you want force that something happens even if an exception occured (but be aware that destructros are declared noexcept(true)
by default).
I personally use it for "finally" blocks (with noexcept(false)
). But usually in C++ you don't need finally
because of RAII.
I think you can't prevent the life-time extension / local variables. And we have no attribute [[discard]]
, only [[nodiscard]]
.
But why you don't do the work in SetA()? For me it would be more clear?
A good reason would be if you need all the param data to do the work...
There is one option. You could make your object not copyable / moveable:
MyClass() = default;
MyClass(const MyClass&) = delete;
MyClass(MyClass&&) = delete;
But with such approach you can't have something like 'SomeFunction'. And of course such a factory function might causes a copy which call your destructor, too.
As a suggestion I would simply do it explicit:
SomeFunction()
.SetA()
.SetB()
.Exec()
;