Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for passing primitive data type in C++ function

I'm writing a function for an avr chip to deserialize a byte stream to primitive types. I'd like to do it in as generic a way as possible and was wondering what the best practice would be to determine the type to deserialize. Ideas I have so far, include:

Choice A:

// Just write a function for each type
double deserialize_double(uint8_t *in) { }

Choice B:

// Use a template, and pass the type in when calling
// Function:
template <typename TYPE>
TYPE deserialize(uint8_t *in) {
  union {
    TYPE real;
    uint8_t base[sizeof(TYPE)];
  } u;

  for (unsigned int i = 0; i < sizeof(TYPE); i++) {
    u.base[i] = in[i];
  }
  return u.real;
}

// Call:
double value = deserialize<double>(in);

Choice C:

// Similar to B, but you pass in the type as a parameter
// Function:
TYPE deserialize(uint8_t *in, TYPE);

// Call:
double value = deserialize(in, double);

Choice D:

// Use a templated class. My main issue with this is I was hoping 
// to re-use the same object for deserializing multiple types.
template <typename TYPE>
class Serializer {
  public void serialize(uint8_t *out, TYPE value) { /* code */ }
  public TYPE deserialize(uint8_t *int) { /* code */ }
};

Any ideas on the best way to do this? Perhaps an easier method I overlooked.

like image 379
baalexander Avatar asked Nov 20 '25 22:11

baalexander


2 Answers

For starters, C and D are invalid options because types are not valid function arguments; might as well rule them out right now.

Choice B seems like the clear winner here, assuming you aren't concerned about byte ordering or other potential caveats of using a union as you are (it doesn't seem like you would be given the context of this work).

One additional thing to consider is having some feedback mechanism to advance your bytestream pointer/index as you deserialize. Perhaps you could try something like

template <typename TYPE>
int deserialize(uint8_t *in, TYPE& value) {
    union {
        TYPE real;
        uint8_t base[sizeof(TYPE)];
    } u;

    for (unsigned int i = 0; i < sizeof(TYPE); i++) {
        u.base[i] = in[i];
    }
    value = u.real;
    return sizeof(TYPE);
}

// Call:
double foo, bar;
int baz;
in += deserialize(in, foo);   // Implicit double deserialize
in += deserialize(in, bar);   // Implicit double deserialize
in += deserialize(in, baz);   // Implicit int deserialize

This has the additional advantage (as I see @Asha beat me to it already!) of allowing you to take advantage of C++ templates' type inference system; since the second argument has a known type at the call location, there is no need to explicitly specify a template argument for TYPE.

like image 196
kqnr Avatar answered Nov 23 '25 11:11

kqnr


One more option is to return the result as an "out" parameter. In that case you need not specify the type during the instantiation of the template. Something like this:

template <typename TYPE>
void deserialize(uint8_t *in, TYPE& out) {
  union {
    TYPE real;
    uint8_t base[sizeof(TYPE)];
  } u;

  for (unsigned int i = 0; i < sizeof(TYPE); i++) {
    u.base[i] = in[i];
  }
  out = u.real;
  return;
}

// Call:
double value = 0;
deserialize(in, value);
like image 43
Asha Avatar answered Nov 23 '25 13:11

Asha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!