Pergunta

Eu queria saber se é possível interagir sobre argumentos passados ??para a macro variádica em C99 ou usar quaisquer extensões do CCG?

Por exemplo é possível escrever uma macro genérica que leva uma estrutura e seus campos passados ??como argumentos e imprime deslocamento de cada campo dentro da estrutura?

Algo parecido com isto:

struct a {
    int a;
    int b;
    int c;
};

/* PRN_STRUCT_OFFSETS will print offset of each of the fields 
   within structure passed as the first argument.
*/

int main(int argc, char *argv[])
{
    PRN_STRUCT_OFFSETS(struct a, a, b, c);

    return 0;
}
Foi útil?

Solução

Aqui está a minha lição de casa do dia, ele é baseado em truques de macro e hoje eu particularmente aprendi sobre __VA_NARG__ inventado por Laurent Deniau . De qualquer forma, o seguinte código de exemplo funciona até 8 campos para o bem da clareza. Apenas estender o código duplicando se precisar de mais (isso é porque o pré-processador não possui recursividade, como ele lê o arquivo apenas uma vez).

#include <stdio.h>
#include <stddef.h>

struct a
{
  int a;
  int b;
  int c;
};

struct b
{
  int a;
  int b;
  int c;
  int d;
};

#define STRINGIZE(arg)  STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

/* PRN_STRUCT_OFFSETS will print offset of each of the fields 
 within structure passed as the first argument.
 */
#define PRN_STRUCT_OFFSETS_1(structure, field, ...) printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS_2(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_1(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_3(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_2(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_4(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_3(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_5(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
 PRN_STRUCT_OFFSETS_4(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_6(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_5(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_7(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_6(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_8(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_7(structure, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS_NARG(...) PRN_STRUCT_OFFSETS_NARG_(__VA_ARGS__, PRN_STRUCT_OFFSETS_RSEQ_N())
#define PRN_STRUCT_OFFSETS_NARG_(...) PRN_STRUCT_OFFSETS_ARG_N(__VA_ARGS__) 
#define PRN_STRUCT_OFFSETS_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define PRN_STRUCT_OFFSETS_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define PRN_STRUCT_OFFSETS_(N, structure, field, ...) CONCATENATE(PRN_STRUCT_OFFSETS_, N)(structure, field, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS(structure, field, ...) PRN_STRUCT_OFFSETS_(PRN_STRUCT_OFFSETS_NARG(field, __VA_ARGS__), structure, field, __VA_ARGS__)

int main(int argc, char *argv[])
{
  PRN_STRUCT_OFFSETS(struct a, a, b, c);
  printf("\n");
  PRN_STRUCT_OFFSETS(struct b, a, b, c, d);

  return 0;
}

que imprime:

struct a:a-0
struct a:b-4
struct a:c-8

struct b:a-0
struct b:b-4
struct b:c-8
struct b:d-12

EDIT:.. Aqui está uma versão ligeiramente diferente que tenta ser mais genérico A macro FOR_EACH(what, ...) aplica what a cada outro argumento na lista de argumentos variável

Assim, você só tem que definir uma macro que leva um único argumento como este:

#define DO_STUFF(x) foo(x)

que vai ser aplicado a todos os argumentos na lista. Assim, por seu exemplo típico é preciso cortar um pouco, mas ainda continua a ser conciso:

#define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field)

E você aplicá-lo como este:

FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);

Finalmente, um programa de amostra completa:

#include <stdio.h>
#include <stddef.h>

struct a
{
  int a;
  int b;
  int c;
};

#define STRINGIZE(arg)  STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

#define FOR_EACH_1(what, x, ...) what(x)
#define FOR_EACH_2(what, x, ...)\
  what(x);\
  FOR_EACH_1(what,  __VA_ARGS__);
#define FOR_EACH_3(what, x, ...)\
  what(x);\
  FOR_EACH_2(what, __VA_ARGS__);
#define FOR_EACH_4(what, x, ...)\
  what(x);\
  FOR_EACH_3(what,  __VA_ARGS__);
#define FOR_EACH_5(what, x, ...)\
  what(x);\
 FOR_EACH_4(what,  __VA_ARGS__);
#define FOR_EACH_6(what, x, ...)\
  what(x);\
  FOR_EACH_5(what,  __VA_ARGS__);
#define FOR_EACH_7(what, x, ...)\
  what(x);\
  FOR_EACH_6(what,  __VA_ARGS__);
#define FOR_EACH_8(what, x, ...)\
  what(x);\
  FOR_EACH_7(what,  __VA_ARGS__);

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) 
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__)
#define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field)

int main(int argc, char *argv[])
{
  FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
  printf("\n");

  return 0;
}

Outras dicas

Com o risco de ganhar um distintivo de arqueólogo, eu acho que há uma pequena melhoria para a resposta de Gregory acima usando a técnica de Sobrecarga Macro no número de argumentos

Com foo.h:

// Make a FOREACH macro
#define FE_1(WHAT, X) WHAT(X) 
#define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__)
//... repeat as needed

#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME 
#define FOR_EACH(action,...) \
  GET_MACRO(__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__)

// Example
// Some actions
#define QUALIFIER(X) X::
#define OPEN_NS(X)   namespace X {
#define CLOSE_NS(X)  }
// Helper function
#define QUALIFIED(NAME,...) FOR_EACH(QUALIFIER,__VA_ARGS__)NAME

// Emit some code
QUALIFIED(MyFoo,Outer,Next,Inner)  foo();

FOR_EACH(OPEN_NS,Outer,Next,Inner)
  class Foo;
FOR_EACH(CLOSE_NS,Outer,Next,Inner)

CPP foo.h gera:

Outer::Next::Inner::MyFoo foo();

namespace Outer {namespace Next {namespace Inner {
   class Foo;
}}}

Se a sua estrutura é descrita com X-Macros , então é possível escrever uma função , ou uma macro para iterar sobre todos os campos da estrutura e imprimir a sua compensação.

#include <stddef.h>   // offsetof macro

//--- first describe the structure, the fields, their types
#define X_FIELDS \
    X(int,    field1) \
    X(int,    field2) \
    X(char,   field3) \
    X(char *, field4)

//--- define the structure, the X macro will be expanded once per field
typedef struct {
#define X(type, name) type name;
    X_FIELDS
#undef X
} mystruct;

//--- "iterate" over all fields of the structure and print out their offset
void print_offset(mystruct *aStruct)
{
#define X(type, name) printf("offset of %s is %d\n", #name, offsetof(mystruct, name));
        X_FIELDS
#undef X
}

//--- demonstrate
int main(int ac, char**av)
{
    mystruct a = { 0, 1, 'a', "hello"};
    print_offset(&a);

    return 0;
}

A solução de Gregory Pakosz funcionou muito bem. Mas eu tinha dois pequenos problemas com isso:

  1. Compilar com a opção pedante recebi o aviso: "ISO99 requer argumentos de descanso para ser usado". Isto é causado pelos argumentos variad na primeira macro FOR_EACH_1. Removendo os e alterando a chamada para FOR_EACH_1 em FOR_EACH_2 removido este aviso.

    #define FOR_EACH_1(what, x) 
    #define FOR_EACH_2(what, x, ...)\
        what(x);                    \
        FOR_EACH_1(what);
    
  2. Uma vez que eu usei-o de uma forma muito genérica, às vezes eu tive que chamar a macro repita com apenas 1 argumento. (Eu sei que não faz sentido repetir um item 1 vezes;)). Felizmente, a solução para este problema era muito simples. Apenas remover o parâmetro x do macro for_each.

    #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
    

Aqui a lista completa com as duas alterações:

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

#define FOR_EACH_1(what, x)         \
    what(x)

#define FOR_EACH_2(what, x, ...)    \
    what(x);                        \
    FOR_EACH_1(what, __VA_ARGS__);

#define FOR_EACH_3(what, x, ...)    \
    what(x);                        \
    FOR_EACH_2(what, __VA_ARGS__);

#define FOR_EACH_4(what, x, ...)    \
    what(x);                        \
    FOR_EACH_3(what,  __VA_ARGS__);

#define FOR_EACH_5(what, x, ...)    \
    what(x);                        \
    FOR_EACH_4(what,  __VA_ARGS__);

#define FOR_EACH_6(what, x, ...)    \
  what(x);                          \
  FOR_EACH_5(what,  __VA_ARGS__);

#define FOR_EACH_7(what, x, ...)    \
    what(x);                        \
    FOR_EACH_6(what,  __VA_ARGS__);

#define FOR_EACH_8(what, x, ...)    \
    what(x);                        \
    FOR_EACH_7(what,  __VA_ARGS__);

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) 
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define FOR_EACH_(N, what, ...) CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__)
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)

Talvez usar o varargs como um inicializador de array, e iterar sobre countof (matriz)? isto é, sizeof (matriz) / sizeof (array [0]). A matriz pode ser potencialmente uma matriz C99 anônimo.

Eu não consigo pensar em outra maneira de interagir sobre VAR-args de uma macro, desde que eu não sei como fazer nada para o texto de cada elemento var-arg. A parte var-arg poderia muito bem ser um único argumento que tem vírgulas nele, por tudo o que você pode fazer para com CPP, AFAIK.

Mas aqui está a minha ideia para iteração sobre var-args:

#define countof(a) ( sizeof(a)/sizeof((a)[0]) )
#define MACRO(fd, format, ...) do { int ar_[] = { __VA_ARGS__ }; \
for(int i=0; i<countof(ar_) ; ++i){ \
    fprintf(fd, format, ar_[i]); \
} } while(0)

Esta é a minha solução para este
desfrutar

#include <stdlib.h>
#include <stdio.h>

#define ITERATE_OVER_VARADICT_MACROS( str, ...)\
do{\
    int i, _arr_[] = {__VA_ARGS__};\
    fprintf(stderr,"msg =%s\n",  str); \
    for(i=0; i<sizeof(_arr_)/sizeof(int) ; i++){ \
    fprintf(stderr,"_arr_[%d]= %d\n", i, _arr_[i] ); \
    }\
}while(0)


int main(int argc, char* argv[])
{
    ITERATE_OVER_VARADICT_MACROS("Test of iterate over arguments in variadic macros", 10,12, 34);
    return 0;
}

Este é o melhor que eu posso pensar, com padrão C:

#include <stddef.h>
#include <stdio.h>

// prints a single offset
#define PRN_STRUCT_OFFSET(x, a) printf("&" #x "." #a " = %d\n", offsetof(x, a));

// prints a struct with one member
#define PRN_STRUCT_OFFSETS_1(x, a) PRN_STRUCT_OFFSET(x, a)

// prints a struct with two members
#define PRN_STRUCT_OFFSETS_2(x, a, b) \
            PRN_STRUCT_OFFSET(x, a) \
            PRN_STRUCT_OFFSET(x, b)

// and so on until some N.
// Boost.Preprocessor might help here, I'm not sure

struct some_struct
{
    int a;
    void* c;
};

int main(void)
{
    PRN_STRUCT_OFFSETS_2(struct some_struct, a, c);

    return 0;
}

Eu estou adicionando isso como uma outra resposta. Aqui está uma tentativa de fazê-lo com C ++ 0x, compilado com g ++ 4.5.0

#include <iostream>
using namespace std;

template<typename L>
inline void for_each(L l)
{
}

template<typename L, typename P, typename... Q>
inline void for_each(L l, P arg, Q... args)
{
  l(arg);
  for_each(l, args...);
}

int main()
{
  for_each([] (int x) { cout << x; }, 1, 2, 3);

  return 0;
}

O programa imprime

123

No entanto, com esta abordagem, todos os parâmetros que você passa para a necessidade expressão lambda para ter o mesmo tipo, int no exemplo acima. No entanto, lambdas permitem variáveis ??de captura como:

int main()
{
  int offset = 10;

  for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3);

  return 0;
}

que imprime:

11
12
13

Se você está direcionando Objective-C ... confira o IMPRESSIONANTE KSVarArgs no Github

KSVarArgs é um conjunto de macros projetados para tornar a lidar com argumentos variáveis ??mais fáceis em Objective-C. Todas as macros assumir que a lista varargs contém apenas objetos Objective-C ou estruturas objeto-like (atribuíveis ao tipo de ID). O ksva_iterate_list macro base () itera sobre os argumentos variáveis, invocando um bloco para cada argumento, até encontrar um nulo de terminação. As outras macros são por conveniência ao converter para coleções comuns.

/*! @param firstNote NSString that is the only known arg 
 */

- (void) observeWithBlocks:(NSString*)firstNote,...{

  /*! ksva_list_to_nsarray puts varargs into 
      new array, `namesAndBlocks` 
   */
  ksva_list_to_nsarray(firstNote, namesAndBlocks);

  /// Split the array into Names and Blocks

  NSArray *names = [namesAndBlocks subArrayWithMembersOfKind:NSString.class],
     *justBlocks = [namesAndBlocks arrayByRemovingObjectsFromArray:names];

  [names eachWithIndex:^(id obj, NSInteger idx) {
     [self observeName:obj usingBlock:^(NSNotification *n) {    
        ((void(^)())justBlocks[idx])(n);
     }];    
  }];
}

Exemplo de uso:

[NSNotificationCenter.defaultCenter observeWithBlocks: 
  NSViewFrameDidChangeNotification, /// first, named arg
  ^(NSNotification *m){ [self respondToFrameChange]; }, // vararg
  NSTextViewDidChangeSelectionNotification, // vararg
  ^(NSNotification *z){ [z.infoDict[@"textView"] save]; }, // vararg
  nil // must nil-terminate
]; 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top