как можно избежать использования #if в макросе печати polymorphi c - PullRequest
0 голосов
/ 11 марта 2020

Давайте попробуем запустить следующий код:

#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;
}

возвращает ошибку:

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

Хорошо, поэтому нам потребуется инструкция #if для запуска y [0] только если переменная является массивом:

#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;
}

Но она возвращает:

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

Есть ли способ вызвать оператор #if внутри макроса? если нет, как я могу это сделать?

примечание: я использую IAR 8.20.2

(эта ссылка не помогает)

Я хочу знать, почему я не хотел бы использовать 2 разных макроса, потому что мне нужно что-то вроде этого (псевдокод):

myFunction(int or array):
   doSomethingWhereIntAndArrayBehavesDifferentlyLikePrintf();
   doSomethingelse();
   doSomethingelse();
   doSomethingWhereIntAndArrayBehavesDifferentlyLikePrintf();
   doSomethingelse();
  • Это очень удобно: вы может анализировать код.
  • Это способ реализации полиморфизма.
  • Он имитирует функцию шаблона C ++.

Ответы [ 4 ]

5 голосов
/ 11 марта 2020

Можно ли в любом случае вызвать оператор #if внутри макроса?

Невозможно.

Если нет, как я могу это сделать? ?

Вы можете использовать C11 _Generi c:

#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;
}
2 голосов
/ 11 марта 2020

Позвольте мне сначала сказать, что я не думаю, что вы должны использовать макрос для этого. Вместо этого у вас должно быть две отдельные функции, возможно с дополнительным выражением _Generic, как показано в ответе Лундинса.

Однако, это можно сделать с несколькими макроопределениями:

#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])
2 голосов
/ 11 марта 2020

Вы можете использовать BOOST_PP_IF:

#include <boost/preprocessor/if.hpp>

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

Посмотреть это в прямом эфире на Coliru

0 голосов
/ 12 марта 2020

Подведите итоги, а другой обходит использование VLA и макросов.

Использование _Generi c

#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;
}

плюсы:

  • простой макрос + простые функции
  • вам не нужна библиотека C ++
  • вам больше не нужен "isArray"
  • вам не нужен # если
  • безопасный тип

минусы:

  • как получить размер массива? -> дает плохие результаты без
  • , ограниченного C11

Использование VLA (нет ##)

#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;
}

плюсы:

  • гибкая
  • совместимость: C99, C11 (опция VLA), ...
  • только один (некрасивый) макрос
  • вам не нужна библиотека C ++
  • вам больше не нужен isArray
  • вам не нужен размер массива в качестве аргумента
  • вам не нужен # если

cons:

  • небезопасен -> дает некоторые плохие результаты
  • не совместим до C99
  • нужна информация о типе

Использование VLA + ## (макрос конкатенации строк)

#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;
}

плюсы:

  • единственное решение, которое работает для всех тестовых случаев
  • довольно простые макросы
  • вам не нужна библиотека C ++
  • вам не нужна # если

минусы:

  • не совместимы до C99
  • нужен isArray
  • нужен тип информации
  • не является типобезопасным -> но не дал слишком плохой ре sults

макрос ## без 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;
}

плюсы:

  • будет совместим до C99
  • , вы не ' не нужна библиотека C ++
  • вам не нужна # если

минусы:

  • не компилируется!
  • много макросы
  • нужен isArray
  • нужен тип информации
  • не является типобезопасным
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...