Home > Software engineering >  How could I do a robust mapping of multiple data types?
How could I do a robust mapping of multiple data types?

Time:09-23

I am trying to convert a long list of key and value pairs into a map so that I can just iterate over it instead of defining each of them line-by-line, but I have a mix of data types for the values (uint8_t, uint16_t, uint32_t, and uint64_t).

Here's a sample of the data I am trying to build a mapping of:

static constexpr uint8_t data1{0x01};
static constexpr uint16_t data2{0x0002};
static constexpr uint32_t data3{0x00000003};
static constexpr uint64_t data4{0x0000000000000004};

I tried doing a union and a struct:

union/struct dtypes {
uint8_t i;
uint16_t j;
uint32_t k;
uint64_t l;
};

Then here's my attempt at creating the map:

std::map<std::string, dtypes> mymap = {{"data1", 0x01},
                                       {"data2", 0x0002},
                                       {"data3", 0x00000003},
                                       {"data4", 0x0000000000000004}};

Then I try to do a for loop to populate a vector of uint8_t's (call it vec):

for (auto iter = mymap.begin(); iter != mymap.end();   iter) {
byte_pushback(vec, iter->second)
};

But the error message I am getting is:

error: could not convert "'{{"data1", 0}, {"data2", 2}, and so on..' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basis_string<char>, dtypes>'
                        {"data4", 0x0000000000000004}};
                                                     ^
                                                     |
                                                     <brace-enclosed initializer list>

How could I do this properly?

This byte_pushback() function breaks the uint16_t, uint32_t, uint64_t into a bunch of uint8_ts and does a push back, so data2 would be {0x00, 0x02}.

CodePudding user response:

You should initialize the map like this.

std::map<std::string, dtypes> mymap = {
    {"data1", {.i = 0x01}},
    {"data2", {.j = 0x0002}},
    {"data3", {.k = 0x00000003}},
    {"data4", {.l = 0x0000000000000004}},
};

Using an union or struct with properly named members helps to simplify implementations of encoding and decoding fields and is good for the readability.

CodePudding user response:

How I would do it would look something like this:

struct MyUnion {
  int type;
  uint8_t m8;
  uint16_t m16;
  uint32_t m32;
  uint64_t m64;
};

class MyMap {
public:
   void push(std::string key, uint8_t m8);
   void push(std::string key, uint16_t m16);
   void push(std::string key, uint32_t m32);
   void push(std::string key, uint64_t m64);

   bool has_key(std::string key) const;

   bool is_8(std::string key) const;
   bool is_16(std::string key) const;
   bool is_32(std::string key) const;
   bool is_64(std::string key) const;

   uint8_t get_8(std::string key) const;
   uint16_t get_16(std::string key) const;
   uint32_t get_32(std::string key) const;
   uint64_t get_64(std::string key) const;
private: 
  mutable std::map<std::string, MyUnion> mapimpl;
};

The implementations will look something like this:

void MyMap::push(std::string key, uint8_t m8) {
   MyUnion u; u.type=0; u.m8 = m8;
   mapimpl[key]=u;
}

bool MyMap::is_8(std::string key) const 
{ 
   return mapimpl[key].type==0; 
}

uint8_t MyMap::get_8(std::string key) const 
{ 
   return mapimpl[key].m8; 
}

And the remaining function has_key():

bool MyMap::has_key(std::string key) const
{
   return mapimpl.find(key)!=mapimpl.end();
}

Then populating the map will look something like this:

  MyMap m;
  m.push("key1",(uint8_t)0xff);
  m.push("key2",(uint16_t)0xffff);
  • Related