Let’s say I have a
struct Params {
int member1;
std::string member2;
int member3 = 42;
std::shared_ptr<std::string> member4;
};
Is there a way to wrap each type with std::optional
, so I get
struct MaybeParams {
std::optional<int> member1;
std::optional<std::string> member2;
std::optional<int> member3;
std::optional<std::shared_ptr<std::string>> member4;
};
In real life, the class Params
has about 50 data members and is a private struct inside another class; and I can’t modify their definition. I would also prefer a pure C++20 solution, except for the boost
library.
I need this to make a universal function used to override some members of the Params
, similar in spirit to what can be achieved in Python’s named keyword parameters, with each parameter defaulted to “None”.
Anyway, I believe we can’t solve the problem, because my guts tell me that would require a C++ reflection. But I post the question here nevertheless, in hope that once again the SO community solve an “impossible” problem.
Further context:
I need the MaybeParams
class for designated initializer list to have modifiedCopy
function like this:
MyClass {
public:
MyClass(const Params& pars): _pars(pars) {...}
...
private:
Params _pars;
};
const MyClass modifiedCopy(const MyClass& reference, const MyClass::MaybeParams& what_to_modify);
int main() {
const auto m1 = MyClass{.member1 = 1, .member2="ABC"};
const MyClass m2 = modifiedCopy(m1, {
.member2 = "XYZ",
.member4 = make_shared("XXX")
});
}
I agree that in the ideal world it is better to have classes with fewer data members or have budget to refactor. If anyone knows how to get to that world – please let me know 😉
8
The design problem seems solvable. To update Params::member2
(a std::string
), you store &Params::member2
(a member pointer) together with std::string{"new value"}
.
This pair could be stored in a ParamsUpdate<std::string>
– templated because not every member is a std::string
. Derive this ParamsUpdate<T>
from a ParamsUpdateBase
and put all updates in a std::vector<std::unique_ptr<ParamsUpdateBase>>
. ParamsUpdateBase
of course has a virtual void apply_to(Params&)
, that’s why you need to store a pointer.
C++17 Class Template Argument Deduction allows you to deduce T
from ParamsUpdate(&Params::member2, "new value")
but you’ll need to write a deduction guide for that.
C++ does not currently have reflection or metaclasses. There is no way to magically make a version of the class with every member individually optional.
Likely this is a design problem: Why do you need this in the first place? (See also What is the XY problem?) Can you refactor this code to not need a struct with 50 data members? How would you use a MaybeParams
class, do you have default values for the parameters in mind?
Often for this sort of use case most members do not have to be optional and there’s a default value that makes sense.
1