2
votes

Macro à remplacer pour les boucles imbriquées

J'ai trouvé cette macro #define TIMES (x) for (int i1 = 0; i1 très pratique pour raccourcir le texte du code. Mais je ne sais pas comment écrire une telle macro quand j'ai des boucles imbriquées et même je ne sais pas si c'est possible. L'idée est la suivante. Est-il possible d'écrire ce code

TIMES(5) TIMES(3) TIMES(7) ....
{
    /* some code, for example to print an array printf("%d \n",a[i1][i2][i3]) */
}

comme

for(int i1=0;i1<5;i1++)
    for(int i2=0;i2<3;i2++)
        for (int i3=0;i3<7;i3++)
        /* many nested `for` loops */
{
    /* some code, for example to print an array printf("%d \n",a[i1][i2][i3]) */
}

avec une sorte de macro "récursive" qui détecte tout TIMES et les remplace par une boucle for par des compteurs i1, i2, i3, ... i'n 'loop?


8 commentaires

Notez que des macros comme celles-ci rendent le code plus difficile à lire, à comprendre et à maintenir.


Cela semble être une idée suspecte , mais j'ai appris le mot "imbriqué" donc je suis content d'être venu ici.


Veuillez ne pas faire cela ... et si vous devez au moins passer un nom de variable à la macro


@ggorlen. Presque, vous apprenez un peu de français. Imbricated => imbrique en français. Mais je modifie la question.


Je sais que cela rend le code plus difficile à lire mais je ne l'utilise que lorsqu'il y a quelques instructions dans le bloc, comme a [i1] [i2 [i3] = ... ou printf ("% d", a [i1] [i2] [i3])


Horrible idée de cacher les boucles derrière les macros


Est-ce possible ou pas?


Il s'avère que les mathématiques vous permettent d'utiliser TIMES (5 * 3 * 7) . Cependant, ne faites pas ça ...


3 Réponses :


6
votes

C'est une très mauvaise pratique, ne faites pas cela. D'autres programmeurs C sont parfaitement conscients des boucles for, mais ils sont complètement inconscients de votre langage macro privé et secret. De plus, les macros fonctionnelles comme celles-ci ont une sécurité de type médiocre et ne doivent être utilisées qu'en dernier recours.

La solution correcte n'est pas d'utiliser une macro, mais une fonction. Si vous souhaitez utiliser une programmation générique appropriée, vous pouvez l'écrire comme suit:

#include <stdio.h>

/* Generally it is bad practice to hide arrays behind typedefs like this. 
   Here it just done for illustration of generic programming in C. */
typedef int data_t[3]; 

typedef void callback_t (data_t* data);

void traverse (size_t n, data_t data[n], callback_t* callback)
{
  for(size_t i=0; i<n; i++)
  {
    callback(&data[i]);
  }
}


void print_array (int(*array)[3])
{
  int* ptr = *array;
  printf("{%d %d %d}\n", ptr[0], ptr[1], ptr[2]);
}

int main (void)
{
  int array [2][3] = { {1, 2, 3}, {4, 5, 6} };

  traverse(2, array, print_array);
}

callback est un pointeur de fonction fourni par l'appelant, qui contient la fonctionnalité réelle. Similaire au corps de la boucle dans votre macro.

Exemple complet:

#include <stdio.h>

typedef void callback_t (int data);

void traverse (size_t n, int data[n], callback_t* callback)
{
  for(size_t i=0; i<n; i++)
  {
    callback(data[i]);
  }
}


void print (int i)
{
  printf("%d ", i);
}

int main (void)
{
  int array [5] = {1, 2, 3, 4, 5};

  traverse(5, array, print);
}

EDIT:

Dans l'exemple ci-dessus , le type de données était int . Mais comme il s'agit de programmation générique, vous pouvez effectuer quelques ajustements et l'échanger contre tout autre type de données, tel qu'un tableau ou une structure. Le hic, c'est que vous devez passer le paramètre au rappel via un pointeur, au lieu de le passer par valeur. Exemple:

typedef void callback_t (int data);

void traverse (size_t n, int data[n], callback_t* callback)
{
  for(size_t i=0; i<n; i++)
  {
    callback(data[i]);
  }
}


0 commentaires

1
votes

Ceci suit de près la solution de Lundin , mais est refactorisé en quelque chose de plus généralisé.

Pour parcourir les éléments de manière générique, vous pouvez laisser vos arguments sous la forme void * , similaire à qsort et bsearch.

void
print_456 (void *base, size_t sz) {
    if (sz == 5 * 6 * sizeof(int)) {
        traverse(base, 5, 6*sizeof(int), print_456);
        puts("");
    } else if (sz == 6 * sizeof(int)) {
        traverse(base, 6, sizeof(int), print_456);
        puts("");
    } else
        printf("%d ", *(int *)base);
}

Le rappel reçoit la taille de l'élément. La fonction de rappel est censée connaître le type sous-jacent de l'objet, afin de pouvoir déduire correctement quelle dimension est traversée. Par exemple, si vous parcourez un int [4] [5] [6] :

    int array[4][5][6];
    traverse(array, 4, sizeof(*array), print_456);

Et la fonction d'impression pourrait ressembler à ceci: p>

typedef void cb_type (void *base, size_t sz);

void
traverse (void *base, size_t n, size_t sz, cb_type *cb) {
    char *p = base;
    for (size_t i = 0; i < n; ++i) {
        cb(p + i*sz, sz);
    }
}


1 commentaires

BTW, pour les macros, vous devez suivre les conseils donnés précédemment pour transmettre le nom de la variable d'indexation. Cela vous donne un bien meilleur contrôle. Les macros vous permettent d'écrire du code plus concis, mais c'est une arme à double tranchant. De manière générale, vous devriez essayer de faire en sorte que votre code prenne moins de temps à comprendre que moins de temps à taper.



-1
votes

J'ai enfin réussi à écrire cette macro. J'ai trouvé la plupart des informations pour le faire dans ce très bon article ( http://jhnet.co.uk / articles / cpp_magic ). Les messages suivants ( Pouvons-nous avoir des macros récursives? , Existe-t-il un moyen d'utiliser la stringification du préprocesseur C ++ sur les arguments de macro variadiques? , Préprocesseur C ++ __VA_ARGS__ nombre d'arguments a>, Astuce macro variadique , ...) m'aide aussi beaucoup . Cette réponse est destinée à répondre à la question. Il n'aborde pas la question de la macro et des bonnes pratiques de programmation. C'est un autre sujet.

Voici le code

#define SECOND(a, b, ...) b
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))

#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...)             _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__

#define EMPTY()

#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__

#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()

#define FIRST(a, ...) a
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0

#define MAP(m, first, ...)           \
  m(first,__VA_ARGS__)                           \
  IF_ELSE(HAS_ARGS(__VA_ARGS__))(    \
    DEFER2(_MAP)()(m, __VA_ARGS__)   \
  )(                                 \
    /* Do nothing, just terminate */ \
  )
#define _MAP() MAP

#define PP_NARG(...) \
         PP_NARG_(,##__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          z,_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#define TIMES(...) EVAL(MAP(TIME2FOR,__VA_ARGS__))

#define TIME2FOR(x,...) \
        for(int CAT(i,PP_NARG(__VA_ARGS__))=0; \
            CAT(i,PP_NARG(__VA_ARGS__))<x; \
            CAT (i,PP_NARG(__VA_ARGS__))++)

main() {

int a[3][2][4];
TIMES(3,2,4) a[i2][i1][i0]=i2*100+i1*10+i0;
TIMES (3,2,4) printf("a[%d][%d][%d] : %d\n",i2,i1,i0,a[i2][i1][i0]);
TIMES (3,2,4) {/* whatever you want : loop indexes are ...,i2,i1,i0 */}
}

Cela s'est avéré plus difficile que je ne le pensais.


0 commentaires