Good afternoon all.
Long text ahead summarized: can we add in ADCore the possibility of having std::vector as NDAttributes similarly to what is done with std::string? Does that make sense?
Explanation:
We are trying to use NDAttributes in a detector driver integration and for several reasons we came to the conclusion that this data was best represented by std::vector. (Maybe there is a better way to do that?)
Since there is no NDAttrDataType for std::vector, the way we could initially do this was by passing the address to the vector's pointer to pAttributeList->add() and representing it as a
NDAttrUInt64, which feels a bit ugly, but kinda works...
IN DRIVER:
##############################
std::shared_ptr<std::vector<uint32_t>> MyAttrVal = std::make_shared<std::vector<uint32_t>>;
MyAttrVal->push_back(42);
(...)
pArray->pAttributeList->add("Cool Attribute", "Description", NDAttrUInt64, &MyAttrVal); // So we are passing the address to a vector's pointer...
// Driver then doCallbacksGenericPointer, etc.
################################
IN PLUGIN:
################################
//Inside processCallbacks...
NDAttribute * pToAttr = pArray.pAttributeList->find("Cool Attribute");
std::vector<uint32_t> * pAttrVec;
pToAttr->getValue(NDAttrUInt64, &pAttrVec); //Again, addres to pointer...
// Do vector stuff...
################################
However, this only works because the shared pointer is instantiated in a "while (true)" scope, that is: the vector is always the same, being written to by the driver and read from by the plugin at the same time.
If the vector is created inside a scope that actually has an end (anything other than while (true)) then it's certain segmentation fault for us poor foolish mortals.
This is becoming messy...
We tried simply creating vectors, instead of vector pointers:
IN DRIVER:
################################
std::vector<uint32_t> MyAttrVal;
MyAttrVal.push_back(SomeIntValue);
(...)
pArray->pAttributeList->add("Even Cooler Attribute", "Description", NDAttrUInt64, &MyAttrVal); // Passing the address to the actual vector...
// Driver then doCallbacksGenericPointer, etc.
################################
IN PLUGIN
################################
//Inside processCallbacks...
NDAttribute * pToAttr = pArray.pAttributeList->find("Even Cooler Attribute");
std::vector<uint32_t> pAttrVec;
pToAttr->getValue(NDAttrUInt64, &pAttrVec); //Address to actual vector...
// Do vector stuff...
################################
This segfaulted, I assume because pAttributeList->add() calls pAttribute->setValue() which will try to cast a vector to
epicsUInt64:
case NDAttrUInt64:
this->value_.ui64 = *(epicsUInt64 *)pValue;
And then things blow up because casting back from this to vector doesn't make sense.
Well, we are left with three options, in order of my personal preference:
1- Try to add vector as attributes in ADCore. Would this be a welcome PR in the community? Does it make sense given the framework's way of operation?
2 - Add a second address to the driver, put the vectors data inside an NDArray as a generic pointer and pass it to the plugins. I don't like this, since it creates a customized structure that is not actually what an array is supposed to be. But it solves this
problem...
3 - Poke around the casting made by pAttributeList->add() and similars to see if any of those would actually give my vector back. Feels ugly and catastrophic, but might work?
Did anyone else try to do something similar? Is there any other way to do this?
Is a PR that does this welcome in the community?
Thanks for any help and discussions.
Cheers,
Marco