I'm working with a library that uses a C interface and several functions that pass a pointer to a struct as the output (not return).
The struct is something like
struct S {
short a;
short b;
union {
char cdata[5];
short sdata[5];
long ldata[5];
float fdata[5];
double dfdata[5];
} u;
}
I've written some template classes that can provide access to this in the form
auto parameter = MyClass<double>(constructor parameters);
parameter.SetValue(5.2); // Sets u.dfdata[0] = 5.2;
// other functions for changing index, or passing in a vector, etc
This is nice(ish) in that it abstracts away a lot of the required knowledge about structs, and only requires the user (still me, but whatever...) to know about the type of the parameter (double
, float
, long
, short
or char
).
However, it's a compile time construct. The hardware I'm communicating with through this library is able to provide a function that has something like:
// renamed and tidied for ease of reading
// I don't have the source for this function, only docs
short GetParameter(short address, short* prevAddress, short* nextAddress,
S* parameter, Other* info);
where info
can be parsed to tell me the type of the parameter. This would, if it were not compile time and different types, allow something like
std::vector<??> a;
a.push_back(MyClass<double>(args));
a.push_back(MyClass<short>(args));
where double
and short
can be read from the info
struct as a short (0 = char, 1 = short...) and the other arguments can be read from the S struct.
I can build a std::vector<std::variant<MyClass<char>, MyClass<short>, ..., MyClass<double>>>
but then trying to work out how to use a variant
has left me confused if this is a good idea at all.
I want to be able to store sets of different types of parameters, for example for saving a complete configuration, but at the moment I think my functions are all compile-time calculations (variations on if constexpr(std::is_same_v<T, char>) { // do something with u.cdata[index] }
).
If I want to be able to create lists with a single type, I can't (as far as I can work out) have virtual T ReadValue()
or virtual void SetValue(T)
because I can't use templating on virtual functions, and if I template the class (as I do now) then I have different (class) types which become hard to operate on.
An ideal solution would look something like
using valType = std::variant<char, short, long, float, double>;
using varType = std::variant<MyClass<char>, ..., MyClass<double>>;
// Read this from some file, or whatever
auto values = std::vector<valType> { 3, 2.6f, 17.2, 1523l };
std::vector<varType> paramList = MyFunctionThatReadsThroughParamToNextParam();
for (size_t i = 0; i < values.size(); i++) {
paramList[i].SetValue(values[i]);
// where the type contained in values[i] matches the type expected by paramList[i]
// ideally this would throw if the types were incorrect, but really
// any defined behaviour here would be great.
}
Is anything like this remotely possible?