سؤال

كنت أتساءل ما إذا كان من الممكن تكرار الحجج التي تم تمريرها إلى ماكرو المتغير في C99 أو باستخدام أي ملحقات دولية؟

على سبيل المثال، من الممكن كتابة ماكرو عام يأخذ بنية ويمليها حقولها كحجج وطباعة إزاحة كل حقل داخل الهيكل؟

شيء من هذا القبيل:

بنية {int a؛ int ب int ج } / * سيتم طباعة PRN_STRESTR_OFFSETS طباعة إزاحة كل مجال من الحقول داخل الهيكل كوسيطة الأولى. * / int الرئيسية (int argc، archv archv []) {prn_sent_offsets (بنية A، A، B، C)؛ العودة 0؛ }
هل كانت مفيدة؟

المحلول

إليكم واجبي المدرسي في اليوم، إنه يعتمد على الحيل الكلية واليوم تعلمت بشكل خاص __VA_NARG__ اخترعها لوران دينياو. وبعد على أي حال، يعمل نموذج التعليمات البرمجية التالي حتى 8 حقلا من أجل الوضوح. ما عليك سوى إطالة التعليمات البرمجية عن طريق التكرار إذا كنت بحاجة إلى المزيد (هذا لأن Preprocessor لا يتميز بتثلية، حيث يقرأ الملف مرة واحدة فقط).

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

الذي يطبع:

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

تحرير: إليك نسخة مختلفة قليلا تحاول أن تكون أكثر عام. ال FOR_EACH(what, ...) ماكرو ينطبق what إلى كل حجة أخرى في قائمة الوساطة المتغيرة.

لذلك، عليك فقط تحديد ماكرو يأخذ حجة واحدة مثل هذا:

#define DO_STUFF(x) foo(x)

والتي سيتم تطبيقها على كل حجة في القائمة. لذلك، من أجل مثالك النموذجي، تحتاج إلى اختراق قليلا ولكنه لا يزال موجزا:

#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)

وتطبقه مثل هذا:

FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);

أخيرا، برنامج نموذجي كامل:

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

نصائح أخرى

في خطر كسب شارة عالم الآثار، أعتقد أن هناك تحسنا بسيطا لإجابة غريغوري أعلاه باستخدام التقنية من التحميل الزائد ماكرو على عدد الحجج

مع 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 يولد:

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

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

إذا تم وصف هيكلك X-Macros., ، إذن، من الممكن كتابة وظيفة، أو ماكرو لتكرار أكثر من جميع مجالات الهيكل وطباعة إزاحةهم.

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

عمل حل غريغوري باكوسز عظيم. ولكن كان لدي مشاكل طفيفة في ذلك:

  1. تجميع مع الخيار المحدد حصلت على تحذير: "ISO99 يتطلب حجج الراحة لاستخدامها". يحدث هذا حجج Variad في أول ماكرو for_each_1. إزالة تلك وتغيير المكالمة إلى for_each_1 في for_each_2 إزالة هذا التحذير.

    #define FOR_EACH_1(what, x) 
    #define FOR_EACH_2(what, x, ...)\
        what(x);                    \
        FOR_EACH_1(what);
    
  2. منذ أن استخدمتها بطريقة عامة للغاية، كان علي أحيانا أن أسمي الماكرو المتكرر مع حجة واحدة فقط. (أعرف أنه لا معنى لتكرار عنصر 1 مرات؛)). لحسن الحظ الحل لهذه المشكلة كان بسيطا جدا. مجرد إزالة المعلمة X من for_each ماكرو.

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

هنا القائمة كاملة مع التغييرتين:

#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__)

ربما استخدم Varargs كعمليات تهيئة صفيف، وتكرر أكثر من Countof (صفيف)؟ أي sizeof (صفيف) / sizeof (صفيف [0]). يمكن أن تكون الصفيف صفيف مجهول C99.

لا أستطيع التفكير في طريقة أخرى للتكرار فوق Var-Args من ماكرو، لأنني لا أعرف كيفية القيام بأي شيء على نص كل عنصر Var-Arg. قد يكون جزء Var-Args أيضا حجة واحدة لديها فواصل فيها، على كل ما يمكنك فعله به مع CPP، AFAIK.

ولكن هنا فكرتي للتكرار على 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)

هذا هو الحل الخاص بي لهذا
استمتع

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

هذا هو أفضل ما يمكنني التفكير فيه، مع 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;
}

أنا أضيف هذا كجواب آخر. هنا جرب في القيام بذلك مع C ++ 0x، تم تجميعها مع 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;
}

طباعة البرنامج

123

ومع ذلك، مع هذا النهج، تحتاج جميع المعلمات التي تمر بها إلى تعبير Lambda إلى أن يكون لها نفس النوع، int في المثال أعلاه. ومع ذلك، فإن Lambdas تسمح لك بالتقاط المتغيرات مثل:

int main()
{
  int offset = 10;

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

  return 0;
}

الذي يطبع:

11
12
13

إذا كنت تستهدف Objective-C... تحقق من رهيبة ksvarargs على github.

KSVARARGS هي مجموعة من وحدات الماكرو المصممة لجعل التعامل مع الوسائط المتغيرة أسهل في الهدف - ج. تفترض جميع وحدات الماكرو أن قائمة Varargs تحتوي فقط على كائنات موضوعية أو هياكل تشبه الكائنات (قابلة للتعديل لكتابة الهوية). KSVA_iterate_List () QSVA_ITERATE_LID () CASE KSVA_ITERATE_LID () تكرار عبر الوسائط المتغيرة، استدعاء كتلة لكل وسيطة، حتى تصادف nil إنهاء. وحدات الماكرو الأخرى هي للراحة عند التحويل إلى المجموعات المشتركة.

/*! @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);
     }];    
  }];
}

مثال على الاستخدام:

[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
]; 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top