0

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?

chrisb2244
  • 2,940
  • 22
  • 44
  • Are you aware of std visit? – Yakk - Adam Nevraumont Nov 29 '17 at 02:05
  • @Yakk, Have read about it, unsure how to go about best using it to solve the problem I have regarding `T ReadValue()` or `void SetValue(T)` with a variant type. I'm guessing something involving lambda functions and auto return types, but I couldn't work it out. – chrisb2244 Nov 29 '17 at 02:07
  • Having parsed through some tangentially related links via the C++1z tag, I wonder if moving to a simple Non-Virtual Interface as detailed [here](https://stackoverflow.com/a/3978552/3098505) would work for me. I have only a specific set of types to handle, so having the interface with `void SetParameter(char value) { SetP_Char(value); }` where `virtual void SetP_Char(char c)` is private and overloaded might work. Edit: would probably be ok for `SetV(v)` but won't allow overloading on return for `char GetVal()`... – chrisb2244 Nov 29 '17 at 04:16

0 Answers0