0

Let's try to run the following code:

#include <stdio.h>
#define MY_MACRO1(isArray,y) do { \
                      if(isArray) \
                        printf("%d", y[0]); \
                      else \
                        printf("%d", y); \
                     }while(0)

int main()
{
    int a = 38;
    int b[]={42};

    MY_MACRO1(0,a);

    return 0;
}

it returns the error:

main.c: In function ‘main’:
main.c:12:39: error: subscripted value is neither array nor pointer nor vector
                         printf("%d", y[0]); \

Ok, so we would need a #if statement to run y[0] only if the variable is an array:

#define MY_MACRO2(isArray,y) do { \
                      #if isArray \
                      printf("%d", y[0]); \
                      #else \
                      printf("%d", y); \
                      #endif \
                     }while(0)

int main()
{
    int a = 38;
    int b[]={42};

    MY_MACRO2(0,a);

    return 0;
}

But it returns :

main.c:11:28: error: '#' is not followed by a macro parameter
 #define MY_MACRO2(isArray,y) do { \

Is there anyway to call a #if statement inside a macro? if not, how can I do such a thing?

note: I'm using IAR 8.20.2

(this link does not help)

I you want to know why I would not like to use 2 different macros is because I need to to something like this (pseudo-code):

myFunction(int or array):
   doSomethingWhereIntAndArrayBehavesDifferentlyLikePrintf();
   doSomethingelse();
   doSomethingelse();
   doSomethingWhereIntAndArrayBehavesDifferentlyLikePrintf();
   doSomethingelse();
  • It is pretty handy : you can factorize code.
  • It's a way of implementing polymorphism.
  • It mimics the C++ template feature.
Guillaume D
  • 2,202
  • 2
  • 10
  • 37

4 Answers4

5

Is there anyway to call a #if statement inside a macro?

Not possible.

if not, how can I do such a thing?

You could use C11 _Generic:

#include <stdio.h>

void int_func (int obj)
{
  printf("%d\n", obj);
}

void int_arr_func (const int* obj)
{
  printf("%d\n", obj[0]);
}

void float_func (float obj)
{
  printf("%f\n", obj);
}

#define MY_MACRO2(y) _Generic((y), \
  int:   int_func,                 \
  int*:  int_arr_func,             \
  float: float_func ) (y)


int main (void)
{
    int a = 38;
    int b[]={42};
    float pi = 3.14f;

    MY_MACRO2(a);
    MY_MACRO2(b);
    MY_MACRO2(pi);
    return 0;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • do you use it often? – Guillaume D Mar 11 '20 at 13:12
  • @GuillaumeD Not often for this kind of template programming, since type-generic code in C most often originates from programmers who've invented a solution and is now looking for a problem that it solves. I often use _Generic for type safety purposes in function-like macros though. – Lundin Mar 11 '20 at 13:15
  • Though mixing _Generic and type qualifiers isn't very well-defined by the C11 standard, so consider adding `const int*: int_arr_func,` as well. This was fixed in C17 which says: "The type of the controlling expression is the type of the expression as if it had undergone an lvalue conversion, array to pointer conversion, or function to pointer conversion.") And lvalue conversion drops type qualifiers. But for C11 compatibility, you should include type qualifiers. Both gcc and clang support C17 though. – Lundin Mar 11 '20 at 13:15
2

You can use BOOST_PP_IF:

#include <boost/preprocessor/if.hpp>

#define MY_MACRO1(isArray,y) printf("%d", BOOST_PP_IF(isArray, (y)[0], (y)))

See it live on Coliru

Quentin
  • 62,093
  • 7
  • 131
  • 191
2

Let me first say that I don't think you should use macro for this. You should have 2 separate functions instead, with possibly additional _Generic expression as shown in Lundins answer.

However, it is possible to do with multiple macro defines:

#define MY_MACRO(isArray,y) MY_MACRO_ ## isArray (y)
#define MY_MACRO_0(y) printf("%d", y)
#define MY_MACRO_1(y) printf("%d", y[0])
user694733
  • 15,208
  • 2
  • 42
  • 68
0

Sum up and another works around using VLA & macros.

Using _Generic

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

static inline void printNonArray_u32 (uint32_t obj)
{
    printf("interger >> uint32_t : %u\n",obj);
}
static inline void printNonArray_u16 (uint16_t obj)
{
    printf("interger >> uint16_t : %u\n",obj);
}
static inline void printNonArray_u8 (uint8_t obj)
{
    printf("interger >> uint8_t : %d\n",obj);
}
static inline void printArray_u32 (const uint32_t* obj)
{
    printf("array >> uint32_t : "); 
    for(uint32_t i = 0 ; i<sizeof(obj)/sizeof(uint32_t) ; i++ ) 
      printf("%d - ",(uint32_t)obj[i]); 
    printf("\n"); 
}
static inline void printArray_u16 (const uint16_t* obj)
{
    printf("array >> uint16_t : "); 
    for(uint32_t i = 0 ; i<sizeof(obj)/sizeof(uint16_t) ; i++ ) 
      printf("%d - ",(uint16_t)obj[i]); 
    printf("\n"); 
}
static inline void printArray_u8(const uint8_t* obj)
{
    printf("array >> uint8_t : "); 
    for(uint32_t i = 0 ; i<sizeof(obj)/sizeof(uint8_t) ; i++ ) 
      printf("%d - ",(uint8_t)obj[i]); 
    printf("\n"); 
}

#define MY_POLYMORPHIC_PRINT2(y)  _Generic((y), \
                                  uint32_t:   printNonArray_u32, \
                                  uint32_t*:  printArray_u32, \
                                  uint16_t:   printNonArray_u16, \
                                  uint16_t*:  printArray_u16, \
                                  uint8_t:   printNonArray_u8, \
                                  uint8_t*:  printArray_u8 ) (y)

int main()
{
    uint32_t i1 = 257;
    uint16_t i2 = 257;
    uint8_t  i3 = 25;
    uint32_t a1[]={42,43,44,257};
    uint16_t a2[]={22,23,24,257};
    uint8_t  a3[]={12,13,14,25};


    printf("MY_POLYMORPHIC_PRINT2 - _Generic\n");      
    MY_POLYMORPHIC_PRINT2(i1);           //interger >> uint32_t : 257                                             >> OK                                                                                                                                                                                                
    MY_POLYMORPHIC_PRINT2(i2);           //interger >> uint16_t : 257                                             >> OK                                                                                                                                                                              
    MY_POLYMORPHIC_PRINT2(i3);           //interger >> uint8_t : 25                                               >> OK                                                                                                                                                                                                            
    MY_POLYMORPHIC_PRINT2(a1);           //array >> uint32_t : 42 - 43 -                                          >> FAILS                                                                                                                                                                                                 
    MY_POLYMORPHIC_PRINT2(a2);           //array >> uint16_t : 22 - 23 - 24 - 257 -                               >> OK                                                                                                                                                                                    
    MY_POLYMORPHIC_PRINT2(a3);           //array >> uint8_t : 12 - 13 - 14 - 25 - 253 - 127 - 0 - 0 -             >> FAILS                             

    return 0;
}

pros:

  • simple macro + simple functions
  • you don't need C++ library
  • you don't need "isArray" anymore
  • you don't need #if
  • type-safe

cons:

  • how to get the array size? --> gives some bad results without
  • limited to C11

Using VLA (no ##)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define MY_POLYMORPHIC_PRINT1(y, type) do { \
                      if((sizeof(y)/sizeof(type) != 1) && (sizeof(y)/sizeof(type) != sizeof(y))) \
                      { \
                          type arrayBuffer[sizeof(y)/sizeof(type)]; \
                          memcpy(&arrayBuffer,&y,sizeof(y)); \
                          printf("array >> "#type" : "); \
                          for(uint32_t i = 0 ; i<sizeof(y)/sizeof(type) ; i++ ) \
                          printf("%d - ",arrayBuffer[i]); \
                          printf("\n"); \
                      } \
                      else \
                      { \
                          type intBuffer = (type)*((type*)(&y)); \
                          printf("integer >> "#type" : %d\n",intBuffer); \
                      } \
                     }while(0)

int main()
{
    uint32_t i1 = 257;
    uint16_t i2 = 257;
    uint8_t  i3 = 25;
    uint32_t a1[]={42,43,44,257};
    uint16_t a2[]={22,23,24,257};
    uint8_t  a3[]={12,13,14,25};

    printf("MY_POLYMORPHIC_PRINT1 - VLA\n");
    MY_POLYMORPHIC_PRINT1(i1,uint32_t);// integer >> uint32_t : 257                                               >> OK                                                                                                     
    MY_POLYMORPHIC_PRINT1(i2,uint16_t);// integer >> uint16_t : 257                                               >> OK  
    MY_POLYMORPHIC_PRINT1(i3,uint8_t); // integer >> uint8_t : 25                                                 >> OK  
    MY_POLYMORPHIC_PRINT1(i1,uint8_t); // integer >> uint8_t : 1                                                  >> POK  wrong type, casting is working                                                                                        
    MY_POLYMORPHIC_PRINT1(a1,uint32_t);// array >> uint32_t : 42 - 43 - 44 - 257 -                                >> OK
    MY_POLYMORPHIC_PRINT1(a2,uint16_t);// array >> uint16_t : 22 - 23 - 24 - 257 -                                >> OK
    MY_POLYMORPHIC_PRINT1(a3,uint8_t); // integer >> uint8_t : 12                                                 >> FAILS
    MY_POLYMORPHIC_PRINT1(a1,uint16_t); // integer >> uint16_t : 42 - 0 - 43 - 0 - 44 - 0 - 257 - 0 -             >> POK  wrong type, casting is somewhat working

    return 0;
}

pros:

  • flexible
  • compatibility : C99, C11 (VLA option), ...
  • just one (ugly) macro
  • you don't need C++ library
  • you don't need "isArray" anymore
  • you don't need array size as an argument
  • you don't need #if

cons:

  • not type-safe --> gives some bad results
  • not compatible before C99
  • need type info

Using VLA + ## (string concatenation macro)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define MY_VLA_PRINT_ARRAY(y, type) do{\
                          type arrayBuffer[sizeof(y)/sizeof(type)]; \
                          memcpy(&arrayBuffer,&y,sizeof(y)); \
                          printf("array >> "#type" : "); \
                          for(uint32_t i = 0 ; i<sizeof(y)/sizeof(type) ; i++ ) \
                          printf("%d - ",arrayBuffer[i]); \
                          printf("\n"); \
                          }while(0)

#define MY_VLA_PRINT_NOT_ARRAY(y, type) do{\
                          type intBuffer = (type)*((type*)(&y)); \
                          printf("integer >> "#type" : %d\n",intBuffer); \
                          }while(0)

#define MY_POLYMORPHIC_PRINT3( y, isArray, type) do { \
                      MY_VLA_PRINT_ ## isArray (y, type); \
                     }while(0)

int main()
{
    uint32_t i1 = 257;
    uint16_t i2 = 257;
    uint8_t  i3 = 25;
    uint32_t a1[]={42,43,44,257};
    uint16_t a2[]={22,23,24,257};
    uint8_t  a3[]={12,13,14,25};


    printf("MY_POLYMORPHIC_PRINT3 -  ## + VLA\n");
    MY_POLYMORPHIC_PRINT3(i1,NOT_ARRAY,uint32_t); // integer >> uint32_t : 257                                    >> OK                                                                                                     
    MY_POLYMORPHIC_PRINT3(i2,NOT_ARRAY,uint16_t); // integer >> uint16_t : 257                                    >> OK  
    MY_POLYMORPHIC_PRINT3(i3,NOT_ARRAY,uint8_t);  // integer >> uint8_t : 25                                      >> OK  
    MY_POLYMORPHIC_PRINT3(i1,NOT_ARRAY,uint8_t);  // integer >> uint8_t : 1                                       >> POK  wrong type, casting is working                                                                                        
    MY_POLYMORPHIC_PRINT3(i1,ARRAY,uint8_t);      // array >> uint8_t : 1 - 1 - 0 - 0 -                           >> POK (does not crash)                                                                                       
    MY_POLYMORPHIC_PRINT3(a1,ARRAY,uint32_t);     // array >> uint32_t : 42 - 43 - 44 - 257 -                     >> OK
    MY_POLYMORPHIC_PRINT3(a2,ARRAY,uint16_t);     // array >> uint16_t : 22 - 23 - 24 - 257 -                     >> OK
    MY_POLYMORPHIC_PRINT3(a3,ARRAY,uint8_t);      // array >> uint8_t : 12 - 13 - 14 - 25 -                       >> OK
    MY_POLYMORPHIC_PRINT3(a1,ARRAY,uint16_t);     // array >> uint16_t : 42 - 0 - 43 - 0 - 44 - 0 - 257 - 0 -     >> POK  wrong type, casting is somewhat working
    MY_POLYMORPHIC_PRINT3(a1,NOT_ARRAY,uint16_t); // integer >> uint16_t : 42                                     >> POK (does not crash)

    return 0;
}

pros:

  • only solution that works for all test cases
  • quite simple macros
  • you don't need C++ library
  • you don't need #if

cons:

  • not compatible before C99
  • need isArray
  • need type info
  • is not type-safe --> but did not give some too bad results

macro ## without VLA:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define MY_NON_VLA_PRINT_ARRAY_U32(y) do{\
                          printf("array >> uint32_t : "); \
                          for(uint32_t i = 0 ; i< sizeof(y) / sizeof(uint32_t)  ; i++ ) \
                            printf("%d - ",(uint32_t)*((uint32_t*)&y+(uint32_t*)i)); \
                          printf("\n"); \
                          }while(0)
#define MY_NON_VLA_PRINT_ARRAY_U16(y) do{\
                          printf("array >> uint16_t : "); \
                          for(uint32_t i = 0 ; i< sizeof(y) / sizeof(uint16_t)  ; i++ ) \
                            printf("%d - ",(uint16_t)*((uint16_t*)&y+(uint16_t*)i)); \
                          printf("\n"); \
                          }while(0)
#define MY_NON_VLA_PRINT_ARRAY_U8(y) do{\
                          printf("array >> uint8_t : "); \
                          for(uint32_t i = 0 ; i< sizeof(y) / sizeof(uint8_t)  ; i++ ) \
                            printf("%d - ",(uint8_t)*((uint8_t*)&y+(uint8_t*)i)); \
                          printf("\n"); \
                          }while(0)
#define MY_NON_VLA_PRINT_NOT_ARRAY_U32(y) do{\
                          printf("integer >> uint32_t : %d\n",(uint32_t)y); \
                          }while(0)
#define MY_NON_VLA_PRINT_NOT_ARRAY_U16(y) do{\
                          printf("integer >> uint16_t : %d\n",(uint16_t)y); \
                          }while(0)
#define MY_NON_VLA_PRINT_NOT_ARRAY_U8(y) do{\
                          printf("integer >> uint8_t : %d\n",(uint8_t)y); \
                          }while(0)


#define MY_POLYMORPHIC_PRINT4( y, isArray, type) do { \
                      MY_NON_VLA_PRINT_ ## isArray ## _ ## type (y); \
                     }while(0)


int main()
{
    uint32_t i1 = 257;
    uint16_t i2 = 257;
    uint8_t  i3 = 25;
    uint32_t a1[]={42,43,44,257};
    uint16_t a2[]={22,23,24,257};
    uint8_t  a3[]={12,13,14,25};


    printf("MY_POLYMORPHIC_PRINT4 -  ## + no VLA\n");
    MY_POLYMORPHIC_PRINT4(i1,NOT_ARRAY,U32);                                                                                         
    MY_POLYMORPHIC_PRINT4(i2,NOT_ARRAY,U16);      
    MY_POLYMORPHIC_PRINT4(i3,NOT_ARRAY,U8);       
    MY_POLYMORPHIC_PRINT4(i1,NOT_ARRAY,U8);                                                                                      
    MY_POLYMORPHIC_PRINT4(i1,ARRAY,U8);                                                                          
    MY_POLYMORPHIC_PRINT4(a1,ARRAY,U32);          
    MY_POLYMORPHIC_PRINT4(a2,ARRAY,U16);          
    MY_POLYMORPHIC_PRINT4(a3,ARRAY,U8);           
    MY_POLYMORPHIC_PRINT4(a1,ARRAY,U16);          
    MY_POLYMORPHIC_PRINT4(a1,NOT_ARRAY,U16);      

    //does not compile:
    // error: invalid operands to binary + (have ‘uint32_t * {aka unsigned int *}’ and ‘uint32_t * {aka unsigned int *}’)
    //                        printf("%d - ",(uint32_t)*((uint32_t*)&y+(uint32_t*)i)); \
    // error: invalid operands to binary + (have ‘uint16_t * {aka  short unsigned int *}’ and ‘uint16_t * {aka short unsigned int *}’)
    //                        printf("%d - ",(uint16_t)*((uint16_t*)&y+(uint16_t*)i)); \
    // error: invalid operands to binary + (have ‘uint8_t * {aka unsigned char *}’ and ‘uint8_t * {aka unsigned char *}’)
    //                        printf("%d - ",(uint8_t)*((uint8_t*)&y+(uint8_t*)i)); \

    return 0;
}

pros:

  • would be compatible before C99
  • you don't need C++ library
  • you don't need #if

cons:

  • does not compile!
  • a lot of macros
  • need isArray
  • need type info
  • is not type-safe
Guillaume D
  • 2,202
  • 2
  • 10
  • 37