0

I am trying to create a std::vector that could take in any kind of numeric data type (char, short, int, long, float and double) in C++ .

In java, I could create List<Number> list = ArrayList<Number>();

Is there an equivalent in C++ (Preferably <=C++03). If not, what is the best way to achieve this?

PS - I really do not want to have one vector for each data type :/

Garf365
  • 3,619
  • 5
  • 29
  • 41
Vivek V K
  • 1,100
  • 2
  • 15
  • 33
  • 1
    How about type-erasure? –  Aug 25 '16 at 06:25
  • 1
    Possible duplicate of [C++ Class wrapper around fundamental types](http://stackoverflow.com/questions/17793298/c-class-wrapper-around-fundamental-types) – Adrian Jałoszewski Aug 25 '16 at 06:26
  • 3
    Unions are maybe something for you. – Hannes Hauptmann Aug 25 '16 at 06:27
  • @HannesHauptmann I looked at Unions. But this would mean I always allocate 64 bits for every vector element. If someone wants to read in just 1GB of 8 bit data, I would take up 8GB of RAM which I do not want to do. Is this the case or have I misunderstood how unions work? – Vivek V K Aug 25 '16 at 06:40
  • 3
    You cannot store elements of different sizes in a vector. Neither in C++ nor in Java. Java actually stores references of the actual objects in this case. So you also have to store 64 bits for each entry. In C++ you could use pointers, but I am not sure if that is worth it. – Frank Puffer Aug 25 '16 at 06:44
  • Ok I did not word that properly. What I meant was, even if I store the pointer to the union in the vector, the union itself will take up 64 bits for each entry -- even though most of the time I need only 8 or 16 bits. In java, I could create the object of that particular data type and store its reference in a list. – Vivek V K Aug 25 '16 at 06:47
  • I asked something kind of similar regarding statically and dynamically typed languages here: http://stackoverflow.com/questions/36561492/c-and-dynamically-typed-languages. Have a look to the answers, they might help you. – FrankS101 Aug 25 '16 at 07:10
  • 4
    You could put union itself in vector. In Java, you could use `Number` but remember that `Number` is object, and every object adds 8 bytes of overhead, then 4 bytes for reference. Add padding, and your "8 bit integer" boxed in Java takes 16 bytes + 4 bytes for reference = 160 bits (on 32-bit Java; on 64-bit it's even worse). C++ union still wins by large margin. – el.pescado - нет войне Aug 25 '16 at 07:40
  • See: http://stackoverflow.com/a/258150/226648 – el.pescado - нет войне Aug 25 '16 at 08:14

1 Answers1

1

A simple way for what you want is:

enum datatype
{
    dt_none,
    dt_char,
    dt_uchar,
    dt_short,
    dt_ushort,
    dt_long,
    dt_ulong,
    dt_float,
    dt_double
};

struct Any
{
      datatype type; // To know what is stored inside union, not mandatory if you know that in another way
      union
      {
          char vchar;
          unsigned char vuchar;
          short vshort;
          unsigned short vushort;
          long vlong;
          unsigned long vulong;
          float vfloat;
          double vdouble;
      } data;
};

std::vector<Any> mydata;

You can avoid using an union, but it take more memory (pointer for every value + real size) and it's more complicated to use:

struct Any
{
     private:
         void * data;
         datatype type;

     public:
         Any():
             data(NULL),
             type(dt_none)
         {}

         ~Any()
         {
             deleteData();  
         }

         void deleteData()
         {
             // Because deleting a void * is UB, we have to check real datatype
             if (data != NULL)
             {
                 if (type == dt_char)
                     delete static_cast<char *>(data);
                 if (type == dt_uchar)
                     delete static_cast<unsigned char *>(data);
                 ....
             }   
         }

         datatype getType() const
         {
             return type;
         }

         void setChar(char v)
         {
             deleteData(); 
             data = new char(v);
             type = dt_char;
         } 


         char toChar() const
         {
             return *static_cast<char *>(data);
         } 

         .....

         void setDouble(double v)
         {
             deleteData();
             data = new double(v);
         }

         double toDouble() const
         {
             return *static_cast<double*>(data);
         }
};

A possible optimization, is using pointer to store small type data, to save some bytes (but do it carefully!). But if you store a lot of big type data, union still better. Also, size of a pointer is platform dependent, so it's difficult to choose which type can be store directly in pointer and have a portable code. Also, on 64bits computer, size of pointer is 8 bytes, which is also common size of double (so, is this case, store directly every type inside pointer, which is same memory consumption than union but in a really more complicated and unsecure way ):

struct Any
{
     private:
         void * data;
         datatype type;

     public:
         Any():
             data(NULL),
             type(dt_none)
         {}

         ~Any()
         {
             deleteData();
         }

         void deleteData()
         {
             // Because deleting a void * is UB, we have to check real datatype
             if (type != dt_char && type != dt_uchar && data != NULL)
             {
                 if (type == dt_double)
                     delete static_cast<double *>(data);
                 ....
             } 
             data = NULL;
         }

         datatype getType() const
         {
             return type;
         }

         void setChar(char v)
         {
             data = reinterpret_cast<void *>(v);
         } 


         char toChar() const
         {
             return static_cast<char>(reinterpret_cast<intptr_t>(data));
         } 

         .....

         void setDouble(double v)
         {
             deleteData();
             data = new double(v);
         }

         double toDouble() const
         {
             return *static_cast<double*>(data);
         }
};

All that to say you have only 2 simple solutions in your case (IMHO):

  • if you really want to save memory, your have to use a vector per type.
  • or use union

PS: You can also take a look at third party library like boost::any.

Garf365
  • 3,619
  • 5
  • 29
  • 41