Thread: Dynamic variable types

  1. #1
    Registered User
    Join Date
    Jan 2011
    Posts
    222

    Dynamic variable types

    Hi,

    I have a question about dynamic variable types. I know that this is possible when using generic programming, but since i am doing this in regular c under gcc i was wondering is there a simple trick or something to achieve this.

    So the problem is that i have a function ,a rather large one, that works on a set of arrays that contain the integers of size 64bits. now i would like to use the same function on a integer array where each entry is encoded as 32bit integer. the way i am dealing with it right now, is by having two exact functions where one takes as an input 64bit ints and the other one 32bit integers.

    Is there a way to have just one function that can handle both types? And yes converting 32bit integers into 64 and then feeding it to the 64 bit function is not an option.

    thank you

    baxy

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    One approach is to work via a pointer to void. This is the approach that qsort takes. Another approach is to turn the function into a macro instead, but this can be error prone if it is feasible in the first place.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    don t know how 'large' the function - no code posted is but if it is like a couple of screens length then you might ask questions about it - If it is large and still only accomplishes one or maybe two tasks then ok maybe it has to be that way, but if it is doing lots of jobs then it probably should be looked at.
    Thought for the day:
    "Are you sure your sanity chip is fully screwed in sir?" (Kryten)
    FLTK: "The most fun you can have with your clothes on."

    Stroustrup:
    "If I had thought of it and had some marketing sense every computer and just about any gadget would have had a little 'C++ Inside' sticker on it'"

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    A third approach is to write macros that declare and define the functions. This is NOT a technique I recommend (overuse of this type of technique can cause some very curly maintenance problems) and, realistically, you would be better off learning C++ and using templates than using this technique.

    But, to illustrate, if you want to create a set of related functions ....
    Code:
    #define CREATE_FUNCTION(type)  type get_##type() {type x;   /* do general things with x */    return x;}
    
    CREATE_FUNCTION(int)
    CREATE_FUNCTION(float)
    CREATE_FUNCTION(unsigned)
    
    int main()
    {
         float f = get_float();
         int i = get_int();
         unsigned u = get_unsigned();
    
            /*   do things here */
    
         return 0;
    }
    The important things to note are;

    1) It is necessary for the macro to create function names that depend on the type, since C does not support function overloading. Hence the token pasting (## operator).

    2) The macros can only be invoked at file scope (since they define functions). There should not be semi-colons at the end of the lines.

    3) Some more tricks are needed to ensure compliance with the one-definition rule. Either use static functions (to make them local to each compilation unit) or use such macro tricks both for function declarations (aka prototypes in header files) and, in exactly one source file, for the definitions (i.e. implementations of the functions).
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  5. #5
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by baxy View Post
    So the problem is that i have a function ,a rather large one, that works on a set of arrays that contain the integers of size 64bits. now i would like to use the same function on a integer array where each entry is encoded as 32bit integer. the way i am dealing with it right now, is by having two exact functions where one takes as an input 64bit ints and the other one 32bit integers.
    Yes, there is. You use a separate file, macros for the data type and function name, and include the file more than once.

    For example, helper-internal.c:
    Code:
    HELPER_TYPE HELPER_FUNC (const HELPER_TYPE a, const HELPER_TYPE b)
    {
        return a + b;
    }
    Then, in the actual helper.c file, you have
    Code:
    #include <stdint.h>
    
    #define   HELPER_TYPE   uint32_t
    #define   HELPER_FUNC   add_u32
    #include "helper-internal.c"
    #undef    HELPER_FUNC
    #undef    HELPER_TYPE
    
    #define   HELPER_TYPE   uint64_t
    #define   HELPER_FUNC   add_u64
    #include "helper-internal.c"
    #undef    HELPER_FUNC
    #undef    HELPER_TYPE
    which causes both add_u32() and add_u64() to be implemented.

    If you have a group of functions, or need more than one symbol, it becomes very tedious to define all those symbols. In that case, you should use a preprocessor macro to generate symbol names with a fixed prefix or suffix. I prefer suffix.

    Ouch, that sounds complicated. An example helper-internal.c hopefully clears this up:
    Code:
    HELPER_TYPE HELPER_NAME(add) (const HELPER_TYPE a, const HELPER_TYPE b)
    {
        return a + b;
    }
    with the corresponding helper.c being:
    Code:
    #include <stdint.h>
    
    #define   HELPER_TYPE       uint32_t
    #define   HELPER_NAME(base) base ## _u32
    #include "helper-internal.c"
    #undef    HELPER_NAME
    #undef    HELPER_TYPE
    
    #define   HELPER_TYPE       uint64_t
    #define   HELPER_NAME(base) base ## _u64
    #include "helper-internal.c"
    #undef    HELPER_NAME
    #undef    HELPER_TYPE
    This is much better because then your helper-internal.c is quite free to implement whatever it needs to, without any changes in helper.c. New symbols, new functions -- no problem.

    You can use the same approach to produce a suitable header file, if the function or functions are part of a library. And if you use GCC, you can always run gcc -E helper.c to see the actual preprocessed code the compiler sees, so this should not be at all difficult to implement.

    It's nowhere as clean as proper generics, and is quite "messy-looking", but it works fine.

  6. #6
    Registered User
    Join Date
    Jan 2011
    Posts
    222
    Hi,

    well i have been playing around with helper functions like you guys suggested. and for small examples it works but for my particular example i don't know how to make it work. so i have a queue(array based) and a want to place in ti numbers and the kick them out. But the numbers can be either 64 or 32 bit integers.

    Example:

    main.c

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
     
    #define   HELPER_TYPE       uint32_t
    #define   HELPER_NAME(base) base ## _u32
    #include "main-helper.c"
    #undef    HELPER_NAME
    #undef    HELPER_TYPE
     
    #define   HELPER_TYPE       uint64_t
    #define   HELPER_NAME(base) base ## _u64
    #include "main-helper.c"
    #undef    HELPER_NAME
    #undef    HELPER_TYPE
    
    
    
    void main(){
    
      long int64[10];
      int int32[10];
      int i f,k;
      long j,l;
      for(i=0;i<10;i++){
        int32[i]=i; 
      }
      for(j=0;j<10;j++){
        int64[j]=j;
      }
      
      QueueCnt_u32 *queue = NULL;
      queue = (QueueCnt_u32 *)emalloc(sizeof(QueueCnt_u32));
      queue->front =0; queue->rear = 0; queue->count = 0; queue->last = 0;
      
      for(i=0;i<10;i++){
    	  printf("add:%d\n",int32[i]);
    	  add_u32(int32[i],queue);
    	  if(i==5){
    		  f= checkFront_u32(queue);
    		  printf("checkF:%d\n",f);
    		  f =checkRear_u32(queue);
    		  printf("checkR:%d\n",f);
    		  for(k=0;k<=5;k++){
    		    f= del_u32(queue);
    		    printf("del:%d\n",f);
    		}
    	  }
      }
      f= checkFront_u32(queue);
      printf("checkF:%d\n",f);
      f =checkRear_u32(queue);
      printf("checkR:%d\n",f);
      for(k=0;k<5;k++){
        f= del_u32(queue);
        printf("del:%d\n",f);
     }
      
      QueueCnt_u64 *queueX = NULL;
      queueX = (QueueCnt_u64 *)emalloc(sizeof(QueueCnt_u64));
      queueX->front =0; queueX->rear = 0; queueX->count = 0; queueX->last = 0;
      
      for(i=0;i<10;i++){
    	  printf("add:%ld\n",int64[i]);
    	  add_u64(int64[i],queueX);
    	  if(i==5){
    		  l= checkFront_u64(queueX);
    		  printf("checkF:%ld\n",l);
    		  l =checkRear_u64(queueX);
    		  printf("checkR:%ld\n",f);
    		  for(k=0;k<=5;k++){
    		    l= del_u64(queueX);
    		    printf("del:%ld\n",l);
    		}
    	  }
      }
      l= checkFront_u64(queueX);
      printf("checkF:%ld\n",l);
      l =checkRear_u64(queueX);
      printf("checkR:%ld\n",l);
      for(k=0;k<5;k++){
        l= del_u64(queueX);
        printf("del:%ld\n",l);
     }
      
      
    }
    main-helper.c

    Code:
    #include <stdlib.h>
    
    #define MAXWSIZE 15
    
    typedef struct HELPER_NAME(QueueCnt) {            /* queue */
    
       HELPER_TYPE q[MAXWSIZE];               /* wheel */
       HELPER_TYPE front, rear, count, last;  /* counters */
    
    }HELPER_NAME(QueueCnt);
    
    
    
    HELPER_TYPE HELPER_NAME(addone)  (const HELPER_TYPE i) {
    	return ((i+1) % MAXWSIZE);
    }
    
    void HELPER_NAME(add)(const HELPER_TYPE a,HELPER_NAME(QueueCnt) *cnt){
    
    	if(cnt->rear>MAXWSIZE){
    		printf("QUEUE FULL\n");
    		return;
    	}
    	else{
    		cnt->count++;
    		cnt->q[cnt->rear]=a;
    		cnt->last = cnt->rear;
    		cnt->rear = addone(cnt->rear);
    	}
    }
    
    HELPER_TYPE HELPER_NAME(del)(HELPER_NAME(QueueCnt) *cnt){
    	HELPER_TYPE a;
    	
    	if(cnt->front == cnt->rear){
    		printf("QUEUE EMPTY\n");
    		return(0);
    	}
    	else{
    		cnt->count--;
    		a=cnt->q[cnt->front];
    		cnt->front= addone(cnt->front);
    	}
    	return(a);
    }
    
    HELPER_TYPE HELPER_NAME(checkFront)(HELPER_NAME(QueueCnt) *cnt){
    	HELPER_TYPE a;
    	a=cnt->q[cnt->front];
    	return (a);
    }
    
    HELPER_TYPE HELPER_NAME(checkRear)(HELPER_NAME(QueueCnt) *cnt){
    	HELPER_TYPE a;
    	a=cnt->q[cnt->last];
    	return (a);
    }
    Oh yes and the error is :

    /tmp/ccPdRHLm.o: In function `add_u32':
    deal.c.text+0x91): undefined reference to `addone'
    /tmp/ccPdRHLm.o: In function `del_u32':
    deal.c.text+0x101): undefined reference to `addone'
    /tmp/ccPdRHLm.o: In function `add_u64':
    deal.c.text+0x214): undefined reference to `addone'
    /tmp/ccPdRHLm.o: In function `del_u64':
    deal.c.text+0x29c): undefined reference to `addone'
    /tmp/ccPdRHLm.o: In function `main':
    deal.c.text+0x35b): undefined reference to `emalloc'
    deal.c.text+0x506): undefined reference to `emalloc'
    collect2: ld returned 1 exit status

    Last edited by baxy; 10-22-2012 at 05:19 AM. Reason: syntax errors, error report

  7. #7
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    You have some syntax errors in your main.c ; let's fix those first to get it to at least compile.

    Quote Originally Posted by baxy View Post
    Code:
      int i f,k;
    You're missing a comma there.

    Quote Originally Posted by baxy View Post
    Code:
      queue = (QueueCnt_u32 *)emalloc(sizeof(QueueCnt_u32));
    emalloc? Shouldn't that be just normal malloc()?

    You also have a typo in main-helper.c:

    Quote Originally Posted by baxy View Post
    Code:
       HELPER_TYPEfront, rear, count, last;  /* counters */
    You're missing a space before front there.

    Also, in two places you are calling addone() directly:
    Quote Originally Posted by baxy View Post
    Code:
            cnt->rear = addone(cnt->rear);
    Since you are invoking the addone helper variant, that should use the HELPER_NAME() macro:
    Code:
            cnt->rear = HELPER_NAME(addone)(cnt->rear);
    With these changes, the code compiles.

    I don't see you having any issues with the file inclusion method I described -- aside from forgetting to use the HELPER_NAME() macro to invoke a variant --, just general programming problems.

  8. #8
    Registered User
    Join Date
    Jan 2011
    Posts
    222
    wow, thank you for checking the code. i was afraid that if a helper function calls another helper function i need to create again macro for it. but i don't , yes i corrected the errors and it works after i do this
    Code:
    cnt->rear = HELPER_NAME(addone)(cnt->rear);
    . (emalloc is just safe malloc that i forgot to include )

    ok i understand how this works now

    thank you

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Struct types and dynamic arrays
    By simone.marras in forum C Programming
    Replies: 6
    Last Post: 03-14-2009, 10:56 AM
  2. variable types
    By pktcperlc++java in forum C++ Programming
    Replies: 2
    Last Post: 12-11-2004, 10:30 PM
  3. dynamic ptr types
    By subdene in forum C++ Programming
    Replies: 4
    Last Post: 07-25-2004, 08:54 AM
  4. variable types
    By chavezbri in forum C Programming
    Replies: 3
    Last Post: 01-16-2003, 02:39 PM
  5. Variable types
    By CodeMonkey in forum C++ Programming
    Replies: 3
    Last Post: 11-22-2001, 06:58 PM