1

im just came into the question with dynamic type of variable (not really dynamic, but should be determind in the runtime), the situation is like this:

i have a function which accept a double array convert it into the integer and write to a file, the integer can have different Bitlength, like 8, 16 and 32. As it is a array, i want to use a pointer to access the final result (array). So i use the void pointer with malloc and switch case now, but it will be needed to add switch case every where when i trying to access or modify this array, my question is, is there a better way to do this?

current code is like:

void foo(double * arr, int len, int iBits, FILE *fh)
{
      void * newArr;
      int iBytePerElement, iBase,i;

      iBytePerElement = iBits / 8;
      iBase = (1 << (iBits - 1)) - 1;

      switch (iBytePerElement)
      {
             case 1:
                  {
                         newArr = (int8_t *) malloc(sizeof(int8_t)*len);
                         break;
                  }
             case 2:
                  {
                         newArr = (int16_t *) malloc(sizeof(int16_t)*len);
                         break;
                  }
             case 4:
                  {
                         newArr = (int32_t *) malloc(sizeof(int32_t)*len);
                         break;
                  }
      }

      for (i = 0; i < len; ++i)
      {
              switch (iBitPerElement)
              {
                     case 1:
                          {
                                ((int8_t *)newArr)[i] = (int8_t)(arr[i]*iBase);
                                 break;
                          }
                     case 2:
                          {
                                ((int16_t *)newArr)[i] = (int16_t)(arr[i]*iBase);
                                 break;
                          }
                     case 4:
                          {
                                ((int32_t *)newArr)[i] = (int32_t)(arr[i]*iBase);
                                 break;
                          }
              }
      }
      fwrite(newArr, iBytePerElement, iBytePerElement*len,fh);
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
tyker1
  • 123
  • 8
  • Functions. Make a set of functions for handling your special "generic" array. Then the code using the array only have to use your functions and don't have to bother with the details of how it is implemented. Then you can quite easily make it even *more* generic, to store any kind of homogeneous data (including structures). – Some programmer dude Jun 13 '18 at 09:48
  • 1
    On an unrelated note, remember that [`malloc`](http://en.cppreference.com/w/c/memory/malloc) returns a `void *`, so the cast is not needed (and you shouldn't really [cast the result of `malloc` anyway](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc)). – Some programmer dude Jun 13 '18 at 09:49
  • In addition to Prog. Dude: May be, [`qsort()`](http://en.cppreference.com/w/c/algorithm/qsort) can provide some inspiration. It implements the sorting of arrays in C-like generic way - sounds a bit similar to what you intend to do. – Scheff's Cat Jun 13 '18 at 10:05
  • How do you return `newArr` from this function? It's also unclear why you need to multiply the floating point number with anything (`iBase`?) before casting? What it the exact input and output of this function? – vgru Jun 13 '18 at 10:20
  • Having a single function do three different things looks like there are three smaller functions trying to get out. Consider using `foo8(x,y)` instead of `foo(x,y,8)`. – Bo Persson Jun 13 '18 at 10:25
  • thx for the answers with possible solution. I personal perfer the qsort and Prog. Dude's solution, since they make my code stay clean... – tyker1 Jun 13 '18 at 11:43
  • @Groo i would not like to put the whole code since the rest part isn't relativ to this question as well as they may confuse the people with where the problem is. I multiply iBase before casting is just because i am transforming the floating format of a audiodate to fixpoint format, newArr will be written to a file and if i need the result, i will add a pointer as parameter to get newArr. But again, do u really need these information in order to help with "how to save a switch case" or "how to implement a dynamic type of variable"? – tyker1 Jun 13 '18 at 11:47

1 Answers1

0

Making a few assumptions about what you probably meant where your example code is undefined (commented), here's how I might do it:

#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
void *foo(double * arr, int len, int iBits)  //should return the malloced array not void
{
      void * newArr;
      int iBytePerElement, iBase, i;

      iBytePerElement = iBits / 8;
      iBase = (1 << (iBits - 1)) - 1;

      if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL; //replace the first switch
      //which obviously meant to alloc sizeof(int16_t)*len in the case 2 branch (not (sizeof(int8_t)*len) etc

      for (i = 0; i < len; ++i) {
          switch(iBytePerElement){
          break; case 1: ((int8_t *)newArr)[i] = arr[i]*iBase; 
          break; case 2: ((int16_t *)newArr)[i] = arr[i]*iBase;
          break; case 4: ((int32_t *)newArr)[i] = arr[i]*iBase;
          }
      }
      return newArr;
}

Basically, you only need the second switch.

If you wanted to get rid of the 2nd switch, you could replace it with some function pointer play. For example:

#include <stdint.h>
#include <stdlib.h>
#include <limits.h>

#define MK_FN(Bits) \
void to##Bits(void *newArr, double const*arr, int len) \
{ \
    int i; for(i=0; i < len; i++) ((int##Bits##_t *)newArr)[i] = arr[i]*((1<<(Bits-1)-1));  \
} 
MK_FN(8)
MK_FN(16)
MK_FN(32)



void *foo(double * arr, int len, int iBits)  //should return the malloced array not void
{
      void * newArr;
      int iBytePerElement = iBits / 8;

      if(NULL==(newArr = malloc(iBits/CHAR_BIT))) return NULL;
      ((void (*[])(void *,double const*, int)){ [1]=to8, [2]=to16, [4]=to32, })[iBits](newArr,arr,len);
      return newArr;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Anyway, please try and post well-defined, compilable code next time. You allocing `sizeof(int8_t)*len` where you likely meant `sizeof(int16_t)*len` (or you go out of bounds in the second switch) is confusing. – Petr Skocik Jun 13 '18 at 10:38
  • thank with the reply, yes it was a mistake (meant sizeof(int16_t)*len). could u pls explain me, what have your second code block done? especially the ((void (*[])(void *,double const*, int)){ [1]=to8, [2]=to16, [4]=to32, })[iBits](newArr,arr,len); it kinda confusing for me to understand what happens there, thx again – tyker1 Jun 13 '18 at 11:51
  • `MK_FN` makes the functions `to8`, `to16`, and `to32` (`##`-does token concatenation, but I think the macro part should be clear; run `gcc -E` to see the pp output if it isn'). The functions incorporate the loop too because with function call overhead, keeping the loop would slow things down unnecessarily. Now to the weird part. It could be written as `void (*functions[5])(void *,double const*, int) = { [0]=NULL, [1]=to8, [2]=to16, [3]=NULL, [4]=to32 }; (functions[iBits])(newArr,arr,len);` I'm just using a compound literal, inferred array sizing and implicit zeroing to make things compact. – Petr Skocik Jun 13 '18 at 11:58
  • @user152531 The second piece is indeed a little convoluted, which is why I think the switch is preferable. – Petr Skocik Jun 13 '18 at 12:00
  • I'm going easy on the casts too. C casts are powerful and dangerous (why C++ splits them into static_cast's and reinterpret_cast's). It's best to avoid them as much as possible. – Petr Skocik Jun 13 '18 at 12:03
  • ah yes, macro part is clear i know that but barely use that. the part of void (*functions[5]) is still not understandable for me, it seems a new syntax for me, do u have any links or literature for help? thx – tyker1 Jun 13 '18 at 12:04
  • It's not new syntax. It's the old convoluted declaration syntax C has always had. (the initializer with the targeted indices (`[5]=something`) is newer) `typedef void (*the_fn_ptr_tp)(void *, const double*, int); the_fn_ptr_tp functions[5];` is how you could rewrite it with a `typedef`. In human speak, it's an array of 5 functions pointers (some NULL) and you use `iBits` as index to select the one you want. – Petr Skocik Jun 13 '18 at 12:08
  • ah, ok i understand now, just haven't thought in the way of typedef – tyker1 Jun 13 '18 at 12:11