I have a struct/class that has many attributes among which one is principal. when constructing an instance of this class, the principal attribute must be given in parameter, but any of the others may or may not be provided. When an attribute is not provided as a parameter it should be computed from the principal attribute.
I have no clue on how to write such a class without writing an exponential number of constructors; or using setters after each constructor that would have only one parameter corresponding to the principal attribute.
I give here a minimal example of what would be ideal for me. But this of course doesn't compile:
#include <cstdlib>
#include <iostream>
using namespace std;
struct StringInfo
{
string str;
int importance;
StringInfo(string strP, int importanceP = strP.length()):
str(strP), importance(importanceP){}
};
int main(int argc, char** argv) {
string test = "test";
StringInfo info(test);
cout << info.importance << endl;
}
Do you have any better (non-exponential) solution? Thanks in advance.
You practically want a behaviour similar to Python's optional parameters, eg :
callFunction(param1=2, param3="hello")
You can do this in C++ if you have as a member a map of type erased key-value pairs :
map<string, ErasedType> _m;
The key of the map is a string with the description of the member (it's up to you to choose this).
Let's leave for now ErasedType
. Provided that you have such a member you could write :
class MyClass
{
map<string, ErasedType> _m;
public:
MyClass(map<string, ErasedType> const& args) : _m(args) {}
};
So that you would only specify the keys you want to and assign specific values for them. An example of constructing the input for such a constructor is
map<string, ErasedType> args = { {"arg1", 1}, {"arg1", 1.2} };
Then accessing the particular value would take a member function like so :
ErasedType get(string const& name) { return _m[name]; }
Now on the ErasedType
. The most typical approach would be to have it as a union of all possible types you keep :
union ErasedType { // this would be a variant type
int n;
double d;
char c;
};
A second idea would be to have the map values be each one an instance of the boost::any
container. A third idea (more old school) would be to use void*
#include <map>
#include <string>
#include <iostream>
#include <boost/any.hpp>
using namespace std;
using boost::any_cast;
class OptionalMembers
{
map<string, boost::any> _m;
public:
OptionalMembers(map<string, boost::any> const& args) : _m(args)
{
// Here you would initialize only those members
// not specified in args with your default values
if (args.end() == args.find("argName")) {
// initialize it yourself
}
// same for the other members
}
template<typename T>
T get(string const& memberName) {
return any_cast<T>(_m[memberName]);
}
};
int main()
{
// say I want my OptionalMembers class to contain
// int argInt
// string argString
// and then more members that won't be given
// as arguments to the constuctor
std::string text("Hello!");
OptionalMembers object({ { "argInt", 1 }, { "argString", text } });
cout << object.get<int>("argInt") << endl;
cout << object.get<string>("argString") << endl;
return 0;
}
In the previous example you are free to have as many "members" as you want and specify each one at will in the constructor
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With