开发者

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

开发者 https://www.devze.com 2023-02-23 15:15 出处:网络
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 determi

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 fa开发者_Go百科r, 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.


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.


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);


A word of caution:

Using C++ for an AVR is not usually recommended for a number of reasons, and a some things flat out won't work at all -- so be careful to test your code on your target chip before putting any significant time into going down any specific path.

But that doesn't mean you have to give up any significant functionality. Just adapt your code to suit the available language features.


In my view Choice B is the best. It adds more readability and easy to use. Moreover, is TYPE can be some bigger class/struct also ? Because you are returning by value from your deserialize() method !


You should use Choice A because:

  1. It introduces the least complexity.
  2. Is compatible with both C and C++.
  3. Provides straight-forward behavior if the type is not supported.

Note that if you desire the syntax of Choice B, it is always possible to implement that on top of the code for Choice A at a later date (by providing specializations for the different types).

0

精彩评论

暂无评论...
验证码 换一张
取 消