Let's say you have a class which have members which should always be set (i.e. defaults would leave it in a bad state). To prevent having objects with "bad state", you have a constructor which requires everything to be set. The issue is, that you also have a set of "factory" classes, create the objects by parsing some data stream and setting members directly. You want to be explicit about which classes can construct your objects without using the constructor. Is there a good pattern for this?
Example:
enum class DataState
{
STATE_ONE,
STATE_TWO
};
class Data
{
public:
Data(std::string _dp1, DataState _dp2): dp1(_dp1), dp2(_dp2) {}
Data() {}; /* Don't want this constructor because it forces a default for dp2
std::string dp1;
DataState dp2;
};
The default constructor is needed because you have factory classes which get callbacks with a "Data" object, and fill it's members on a per member basis. .i.e. something like:
Field1Callback(Data &d, const std::string &fieldVal)
{
d.dp1 = field;
}
Field2Callback(Data &d, const std:string &fieldVal)
{
d.dp2 = ConvertToState(fieldVal);
}
Are there any patterns for being explicit about what classes are allowed to call "default" constructors? i.e. serializers and parsers?
To clarify, here is some snippet code for a templated parser that I'm working with:
template<typename R>
class CSVParser
{
public:
CSVParser(std::vector<std::pair<std::string, std::function<bool(const std::string &field, R &rec)>>> parsers);
bool ProcessBuffer(const unsigned char *buffer, size_t length, size_t &bytes_parsed, bool last_buffer, std::function<void(const R& onNewRecord)> onNewRecord)
};
Example construction for parser for Data
could be:
CSVParser<Data> d(
{
{
"dp1",
[](const std::string &field, Data &rec) { rec.dp1 = field; return true;}
},
{
"dp2",
[](const std::string &field, Data &rec) { rec.dp2 = ConvertToState(field); return true;}
}
}
);
Couple of solutions would be:
- Make default constructor private, add an empty friend class, and then have instantiation of CSVParser which derives from friend.
- Move all members for Data to a class
DataRaw
which exposes everything, but only useDataRaw
for parsers, and use Data everywhere else in the program. - Create a
DataSerialize
class which derives fromData
and passes garbage into constructor, knowing that it will overwrite it. Callbacks to handle constructed Data object will get passed aDataSerialize
object by ref, and never know the difference.
This seems to be a common issue in other languages as well, where factory objects need to be able to break the public/private boundaries.