Home › Forum › SOFA › Programming with SOFA › [SOLVED] Create my own "DataTypes" for template components in SOFA
- This topic has 2 replies, 1 voice, and was last updated 7 years, 9 months ago by Bruno Marques.
-
AuthorPosts
-
6 March 2017 at 12:41 #8757Bruno MarquesBlocked
Hi,
I have a few utility engines I implemented in SOFA, that performs type conversions of vector values. (for example SofaVec2iTocvKeypoint and vice versa, where cvKeypoint is a data structure holding a 2D point).
I am not a big fan of my implementation as I have a lot of code duplication for each of these components. I thought of have a single component, with an OptionsGroup to specift Source type and Destination type, but it’s not the nicest implementation either.
What I would like is to use template specialization, like it’s could be done with Sofa’s DataTypes, but I’m struggling with the ObjectFactory, that has quite a lot of introspection/reflexion mechanism. I got lost digging…
Basically, what I have right now is the following:
A template structure that looks like this:
template <class TSrcType, class TDstType> struct ConversionType { typedef TSrcType SrcType; typedef TDstType DstType; static const char* Name() { return "unknown"; } };
2 specializations of this structure:
typedef ConversionType<common::cvKeypoint, defaulttype::Vec2i> cvKeypoint2Vec2i; template <> inline const char* cvKeypoint2Vec2i::Name() { return "cvKeypoint2Vec2i"; } typedef ConversionType<defaulttype::Vec2i, common::cvKeypoint> Vec2i2cvKeypoint; template <> inline const char* Vec2i2cvKeypoint::Name() { return "Vec2i2cvKeypoint"; }
and my component class templated on ConversionType:
template <class ConversionType> class VectorConverter : public DataEngine { public: SOFA_CLASS(SOFA_TEMPLATE(VectorConverter, ConversionType), DataEngine); VectorConverter(); void init(); void update(); // INPUTS Data<helper::vector<typename VectorTypes::SrcType> > d_src; // OUTPUTS Data<helper::vector<typename VectorTypes::DstType> > d_dst; };
and finally in my component’s cpp file:
SOFA_DECL_CLASS(VectorConverter) int VectorConverterClass = core::RegisterObject( "Converts vector of cvKeyPoints to sofa vectors, and vice versa") .add<VectorConverter<cvKeypoint2Vec2i> >(true) .add<VectorConverter<Vec2i2cvKeypoint> >();
The ObjectFactory is not working and tells me:
Template <Vec2i2cvKeypoint> incorrect, used <ConversionTypes<cvKeypoint, Vec<2, int> > >>
, so I must be missing something. I found some DataTypeInfo and AbstractDataTypeInfo while looking for a fix, and it seems to be something I am currently missing in my existing code, but I don’t know if I’m at all on the good track, so asking on the forum seemed to be the best approach.
Could you help me out?
Thanks in advance!
– Bruno7 March 2017 at 11:15 #8760Bruno MarquesBlockedHi,
After a bit more research, I found out I over-complicated things:
I managed to get my templates working by templating my component over the source and destination type directly:
template<class SrcType, class DstType>
and for my own types (the cvKeypoint data structure) I implemented a DataTypeInfo as such:
template <> struct DataTypeInfo<OR::common::cvKeypoint> : public MyTypeInfo<OR::common::cvKeypoint> { static const char* name() { return "unsigned short"; } };
and MyTypeInfo like so:
template<class TDataType> struct MyTypeInfo { typedef TDataType DataType; typedef DataType BaseType; typedef DataType ValueType; typedef long long ConvType; typedef MyTypeInfo<TDataType> BaseTypeInfo; typedef MyTypeInfo<TDataType> ValueTypeInfo; enum { ValidInfo = 1 }; enum { FixedSize = 1 }; enum { ZeroConstructor = 1 }; enum { SimpleCopy = 1 }; enum { SimpleLayout = 1 }; enum { Integer = 0 }; enum { Scalar = 1 }; enum { Text = 0 }; enum { CopyOnWrite = 0 }; enum { Container = 0 }; enum { Size = 1 }; static size_t size() { return 1; } static size_t byteSize() { return sizeof(DataType); } static size_t size(const DataType& /*data*/) { return 1; } static bool setSize(DataType& /*data*/, size_t /*size*/) { return false; } template <typename T> static void getValue(const DataType &data, size_t index, T& value) { if (index != 0) return; // value = (T)data; } template<typename T> static void setValue(DataType &data, size_t index, const T& value ) { if (index != 0) return; // data = (DataType)value; } static void getValueString(const DataType &data, size_t index, std::string& value) { if (index != 0) return; std::ostringstream o; o << data; value = o.str(); } static void setValueString(DataType &data, size_t index, const std::string& value ) { if (index != 0) return; std::istringstream i(value); i >> data; } static const void* getValuePtr(const DataType& data) { return &data; } static void* getValuePtr(DataType& data) { return &data; } };
It is probably possible to make it simpler, but at least it works.
Though it is far from perfect: for some reason, in order to use my template from the XML, I have to type the following value for my attribute “template”:
template="Vec<2, int>, cvKeypoint>"
The ‘>’ at the end doesn’t make sense, and also, I would like to be able to write “Vec2i” and not “Vec<2, int>” Additionally, this syntax is too sensitive, as if I mistype the whitespaces for instance, my template won’t be recognized.
Could you tell me what I am missing?
Cheers,8 March 2017 at 15:43 #8761Bruno MarquesBlockedHi,
I finally have the solution to my problem:
In order to create template components in Sofa, there is nothing more to do than overloading the templateName() method, inherited from Base.
The templateName function must simply send back, for each template class, the name you want to use in your scene file, coma separated.usually, your implementation of templateName will look like this:
template<class T> std::string MyComponent<T>::templateName(const MyComponent<T>* nullptr = NULL) { return std::string(defaulttype::DataTypeName<T>::name(); }
This will work with SOFA types like defaulttype::Mat*, defaulttype::Vec* etc.. but won’t with your own data types if you created any, as DataTypeName would not have been specialized on your Data type.
The solution is to add to the file describing your data structure (in my case cvKeypoint) the following lines:
namespace sofa { namespace defaulttype { template <> struct DataTypeName<myDataType> { static const char* name() { return "myDataType"; } }; } // namespace defaulttype } // namespace sofa
-
AuthorPosts
- You must be logged in to reply to this topic.