[Documentation] [TitleIndex] [WordIndex

(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Using Converters

Description: Demonstrates usage of the ecl converters.

Keywords: ecl converters

Tutorial Level: INTERMEDIATE

Next Tutorial: Formatters

Converters provide a fast means of doing type conversions. Though they are sometimes useful directly (e.g. the byte array converters for io devices), they are more often used as a tool inside ecl_formatters.

Instantiation

Converters are functors - functions with state. You instantiate a converter type and then make use of it as if it were a function call.

   1 Converter<char*,float> floatToCharString;
   2 Converter<string,int> intToString;

For some types, it is possible to instantiate an object which incorporates all of the currently convertable fundamental types together. This is sometimes convenient when converting from many different types or if the converter requires a specific piece of machinery. Character strings are the perfect example, as you will often wish to convert from many different formats and because they require a buffer. If the buffer is shared, it saves either expensive instantiations for each conversion type or complicated buffer passing between the converters.

   1 Converter<char*> toCharString;
   2 Converter<string> toString;

Type Conversion Fallback

If no specialisation exists, the primary converter template classes will inspect the output type's class definition for a typedef'd converter (in a similar fashion to iterators).

Buffered Type Conversion [char*]

These are a fast means of doing conversions to a text format, but they require particular care with buffering and ensuring you do not read past arrays. The default implementation for character strings abstracts this implementation behind a class wrapper. It will set up a buffer and do the buffer manipulation behind the wrapper for you.

The conversion algorithm for integral types is hand-specified, so it avoids the formatting checks made by the other converters. I'd like to do the same for the float types, but haven't managed to implement this well yet, so it defaults back to c's sprintf function, which is still as fast or faster than most other implementations.

   1 Converter<char*> convertCharString;
   2 char* charString;
   3 int i = 255;
   4 
   5 charString = toCharString(i);
   6 cout << charString << endl;

The location of the character string buffer for the above code is important. In this case, the buffer is stored in the Converter<char*> object. As the character strings are pointing to this buffer, you must ensure that the Converter hangs around long enough to use the result.

If you wish to reduce the footprint of the converter, you can also manually instruct it to use a buffer of a certain size:

   1 Converter<char*> toCharString(30);
   2 }}
   3 
   4 Alternatively you can pass the converter an external buffer to use:
   5 
   6 {{{
   7 #!cplusplus
   8 char buffer[30];
   9 Converter<char*> toCharString1(buffer,buffer+29);
  10 
  11 char *s1,*s2;
  12 s1 = buffer+10;
  13 s2 = buffer+20;
  14 Converter<char*> toCharString1(s1,s2);

Float to char* converters are the one exception in this family, in that they do have a small formatting capability, namely, the number of decimal places which will get converted.

   1 ecl::Converter<char*,float> converter;
   2 std::cout << converter(2.134) << std::endl;    // defaults to as large as needs be
   3 std::cout << converter2(2.134,2) << std::endl; // prints only 2.13
   4 

Note that if the precision causes the assigned buffer to be exceeded, the converter will just lop off any extra decimal places of precision.

Byte Array Converters

These convert to and from byte arrays (char arrays), which are very useful in reading/writing to and from i/o devices. They'll typically work with any container with the usual c++ style handles, though the obvious is for std::vector containers:

   1 typedef std::vector<unsigned char> ByteArray;
   2 Converter< ByteArray, ecl::int32 > toByteArray;
   3 ByteArray byte_array(4); // ensure memory reserved matches integral type size
   4 // these require a buffer for speed and practicality (aka char string converters)
   5 toByteArray(byte_array, ecl::int32(363)); // converts to 0x6b 0x01 0x00 0x00
   6 toByteArray(byte_array, ecl::int32(-2));  // converts to 0xfe 0xff 0xff 0xff
   7 Converter< int, ByteArray > fromByteArray;
   8 int i = fromByteArray(byte_array);
   9 std::cout << std::dec << "i: " << i << std::endl;

Performance

Of particular interest is the conversions from a number type to character string as these are used to build up the formatters for use with ecl_streams. There is a benchmarking program for string conversions in the ecl_core_apps package.

In general, the converters outperform sprintf and iostream (simply because no formatting checks are made in most cases), in some cases they outperform these considerably. The only case which is missing is that of an optimised algorithm for float to char string conversions - it just falls back to sprintf for now.

Writing Your Own Converters

You can add specialisations for individual conversions. A typical specialisation would have the following code structure,

   1 namespace ecl {
   2 
   3 template <>
   4 class Converter<Matrix,Vector> {
   5     public:
   6         inline Matrix operator()(Vector &input) {
   7             // implementation
   8             return Matrix(M);
   9         }
  10 };
  11 }; // namespace ecl
  12 

Usage would be as follows,

   1 Converter<Matrix,Vector> toMatrix;
   2 Converter<string,Matrix> toString;
   3 
   4 string s;
   5 Matrix m;
   6 Vector v;
   7 m = toMatrix(v);
   8 s = toString(m);

Another alternative is to make use of the default parent template structure, which, in the absence of a specialisation will look in the input type's class definition for a typedef'd Converter.

The above example is a little contrived as the same effect could be achieved with member functions in the matrix and vector classes. Shims really become useful only when they requires some additional internal structure such as is the case for character string converters - these require initialisation and


2019-12-14 12:34