C ++ ön işlemci __VA_ARGS__ bağımsız değişken sayısı


100

İnternette cevabını bulamadığım basit soru. Değişken bağımsız değişken makrolarında, bağımsız değişkenlerin sayısı nasıl bulunur? Çözümü varsa, destek ön işlemcisi ile sorun yok.

Bir fark yaratırsa, daha fazla yeniden işleme için önişlemci sırasını, listeyi veya diziyi artırmak için değişken sayıda makro bağımsız değişkenini dönüştürmeye çalışıyorum.


Açık olmak gerekirse - değişken C fonksiyonları oluşturmak için kullanılan makrolar değil, değişken makrolar hakkında soru soruyorsunuz?

2
argümanlar aynı türden mi? eğer öyleyse ve tür biliniyorsa, bileşik sabit değerler aracılığıyla standart bir C çözümü vardır; bilinmiyorsa, __typeof__en azından bazı derleyicilerde çalışmasını sağlamak için kullanabilirsiniz
Christoph

1
Tartışma, Boost önişlemci dizisi vb. Hakkında olduğu için, C ++ olmalıdır (bu yüzden Q'yu yeniden etiketledim - ancak soru başlığını değiştirmedim) ... Oops; Ben düzelteceğim.
Jonathan Leffler

@JonathanLeffler True, Boost bir C ++ kitaplığıdır. Ancak Boost.Preprocessor, C. AFAIK ile birlikte kullanılabilir, kullandığı hiçbir şey C ++ 'ya özgü değildir.
Justin

Yanıtlar:


90

Bu aslında derleyiciye bağlıdır ve herhangi bir standart tarafından desteklenmez.

Ancak burada, önemli olan bir makro uygulamanız var:

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _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

/* Some test cases */


PP_NARG(A) -> 1
PP_NARG(A,B) -> 2
PP_NARG(A,B,C) -> 3
PP_NARG(A,B,C,D) -> 4
PP_NARG(A,B,C,D,E) -> 5
PP_NARG(1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3) -> 63

.... ancak şimdi C ++ 0x'de standarttır ve daha önce olmalıydı çünkü değişken işlevleri bozuk çağrılardan korumak için harika bir yol sağlar (yani, değişken öğelerden sonra değerleri aktarabilirsiniz. Bu aslında bir yoldur
eskiden kullandığım sayıyı elde etmekle ilgili

Cevap başka bir siteye bağlanır. Ayrıca bağlantı doğru cevabı göstermiyor gibi görünüyor. Ve amaçlanan cevabı bulmayı başarsam bile, derlenecek olan sabit kodlu bir "-1" içerdiği için zayıf bir cevap gibi görünüyor. Daha iyi yöntemler var.
ceztko

2
Teşekkürler! bu benim için Visual Studio 2013'te çalıştı: #define EXPAND(x) x #define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N #define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0))`` ``
mchiasson

1
PP_NARG()0 döndürülemez. GET_ARG_COUNT()& Y_TUPLE_SIZE()çözümler çalışır.
PSkocik

1
" PP_NARG()0'ı döndüremiyor" ... mutlaka bir sorun değil. Bir söyleyebiliriz PP_NARG() gereken aynı nedenle 1 dönmek PP_NARG(,)2. dönmelidir Tespitlere simge ilk pasteable olması gerektiren (aslında bazı durumlarda kullanışlı olabilir 0, ancak çözüm ya daha az genel görünmektedir; hangi olabilir veya olmayabilir olmak tamam ne için kullandığınıza bağlı olarak) veya uygulamaya özgü (örneğin, gnu'nun virgül kaldırma-yapıştırma numarası gerektirmesi).
H Walters

100

Bu makroyu genellikle birkaç parametre bulmak için kullanırım:

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))

Tam örnek:

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define SUM(...)  (sum(NUMARGS(__VA_ARGS__), __VA_ARGS__))

void sum(int numargs, ...);

int main(int argc, char *argv[]) {

    SUM(1);
    SUM(1, 2);
    SUM(1, 2, 3);
    SUM(1, 2, 3, 4);

    return 1;
}

void sum(int numargs, ...) {
    int     total = 0;
    va_list ap;

    printf("sum() called with %d params:", numargs);
    va_start(ap, numargs);
    while (numargs--)
        total += va_arg(ap, int);
    va_end(ap);

    printf(" %d\n", total);

    return;
}

Tamamen geçerli C99 kodudur. Bununla birlikte, bir dezavantajı vardır - makroyu SUM()parametreler olmadan çalıştıramazsınız , ancak GCC'nin buna bir çözümü vardır - buraya bakın .

Dolayısıyla, GCC durumunda makroları şu şekilde tanımlamanız gerekir:

#define       NUMARGS(...)  (sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1)
#define       SUM(...)  sum(NUMARGS(__VA_ARGS__), ##__VA_ARGS__)

ve boş parametre listesi ile bile çalışacaktır


4
UM, OP için işe yaramaz, derleme zamanında çalışan BOOST_PP için boyuta ihtiyacı var.
Kornel Kisielewicz

5
Zekice! Ne zaman da işe yarıyor sizeof(int) != sizeof(void *)mu?
Adam Liss

3
@Kornel Her makro gibi, derleme zamanında değerlendirilir. Boost hakkında hiçbir fikrim yok ama yine de Boost gerekli değil.
qrdl

4
@Adam Yayınladığım {__VA_ARGS__}için int[], int[]gerçek içeriğine bakılmaksızın sadece__VA_ARGS__
qrdl

3
Zarif çözüm! VS2017'de çalışır. ##Boş olarak VS2017 gerekli değildir __VA_ARGS__otomatik önceki herhangi bir virgül kaldırır.
poby

39

C ++ 11 kullanıyorsanız ve C ++ derleme zamanı sabiti olarak değere ihtiyacınız varsa, çok şık bir çözüm şudur:

#include <tuple>

#define MACRO(...) \
    std::cout << "num args: " \
    << std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value \
    << std::endl;

Lütfen dikkat: sayma tamamen derleme zamanında gerçekleşir ve değer, derleme zamanı tamsayı gerektiğinde kullanılabilir, örneğin std :: array için bir şablon parametresi olarak.


2
Harika çözüm! Ve sizeof((int[]){__VA_ARGS__})/sizeof(int)yukarıda önerilenden farklı olarak , argümanların tümü kullanılamadığında bile çalışır int.
Wim

Kabul. Harika çözüm! ++.
davernator

Şablonlarla çalışmaz, yani NUMARGS (toplam <1,2>); bkz. godbolt.org/z/_AAxmL
jorgbrown

1
Sanırım bu aslında onun lehine bir nokta olabilir , @jorgbrown, en azından ortaya çıktığı çoğu durumda. Sayımı yapmak için önişlemci yerine derleyiciye dayandığından, derleyici tarafından görüldüğü şekliyle argümanların sayısını verir ve bu, çoğu programcının beklediği şeyle muhtemelen eşleşecektir. Yine de , önişlemci açgözlülüğünü hesaba katmasını bekliyorsanız sorun yaratacaktır .
Justin Time - Monica'yı

Harika cevap. Bir makroya koyabilirsiniz#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Richard Whitehead

23

Kolaylık sağlamak için, 0 ila 70 bağımsız değişken için çalışan ve Visual Studio, GCC ve Clang'da çalışan bir uygulama burada verilmiştir . Visual Studio 2010 ve sonrasında çalışacağına inanıyorum, ancak yalnızca VS2013'te test ettim.

#ifdef _MSC_VER // Microsoft compilers

#   define GET_ARG_COUNT(...)  INTERNAL_EXPAND_ARGS_PRIVATE(INTERNAL_ARGS_AUGMENTER(__VA_ARGS__))

#   define INTERNAL_ARGS_AUGMENTER(...) unused, __VA_ARGS__
#   define INTERNAL_EXPAND(x) x
#   define INTERNAL_EXPAND_ARGS_PRIVATE(...) INTERNAL_EXPAND(INTERNAL_GET_ARG_COUNT_PRIVATE(__VA_ARGS__, 69, 68, 67, 66, 65, 64, 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 INTERNAL_GET_ARG_COUNT_PRIVATE(_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, _64, _65, _66, _67, _68, _69, _70, count, ...) count

#else // Non-Microsoft compilers

#   define GET_ARG_COUNT(...) INTERNAL_GET_ARG_COUNT_PRIVATE(0, ## __VA_ARGS__, 70, 69, 68, 67, 66, 65, 64, 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 INTERNAL_GET_ARG_COUNT_PRIVATE(_0, _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, _64, _65, _66, _67, _68, _69, _70, count, ...) count

#endif

static_assert(GET_ARG_COUNT() == 0, "GET_ARG_COUNT() failed for 0 arguments");
static_assert(GET_ARG_COUNT(1) == 1, "GET_ARG_COUNT() failed for 1 argument");
static_assert(GET_ARG_COUNT(1,2) == 2, "GET_ARG_COUNT() failed for 2 arguments");
static_assert(GET_ARG_COUNT(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, 64, 65, 66, 67, 68, 69, 70) == 70, "GET_ARG_COUNT() failed for 70 arguments");

IMHO Microsoft değişkeni sıfır bağımsız değişken için başarısız olur.
Vroomfondel

@Vroomfondel Microsoft varyantı sıfır bağımsız değişken için çalışır. Yukarıdaki örnekteki ilk static_assert, sıfır bağımsız değişken durumu için özel bir testtir ve bunu Visual Studio 2017 v15.8.9'da derledim ve çalıştırdım.
Chris Kline

İlginç - Microsoft olmayan bir derleyicide Microsoft varyantını kullanmak işe yaramıyor - M $ ön işlemcisinin kodun tersi şekilde çalışmasını sağlayan neyi farklı yaptığını biliyor musunuz? BTW C ++ 'ı değil, C'yi denedim;
Vroomfondel

Bunun nedeni, MSVC'nin "sıfır uzunluk __VA_ARGS__" (C ++ 'da teknik olarak C ++ 20'ye kadar (neredeyse evrensel, fiili standart) bir derleyici uzantısıdır) hakkında biraz daha hoş olmasından kaynaklanıyor . En (? Tümü) Derleyiciler sıfır uzunluk sağlar, ancak liste halinde arka virgül boğulmaya olan (ve aşırı yük boş ##bir protokoller olarak __VA_OPT__bu durumda virgül çıkarmak için); Uzantısının MSVC versiyonu sadece virgül boğulursun vermez (ama olacak aşırı boğulursun ##). MSVC'yi MSVC unused, __VA_ARGS__olmayanlarla karşılaştırın 0, ## __VA_ARGS__; hiçbiri daha doğru değil, sorun onların farklı olmaları.
Justin Time - Monica'yı yeniden

Bunun C'de aynı olup olmadığından emin değilim @Vroomfondel, çünkü yer işaretimi en son taslakta kaybettim.
Justin Time - Monica'yı yeniden

11

Derleme sırasında argüman sayısını bulmak için bazı C ++ 11 çözümleri var, ancak hiç kimsenin bu kadar basit bir şey önermediğini görünce şaşırdım:

#define VA_COUNT(...) detail::va_count(__VA_ARGS__)

namespace detail
{
    template<typename ...Args>
    constexpr std::size_t va_count(Args&&...) { return sizeof...(Args); }
}

Bu, <tuple>başlığın dahil edilmesini de gerektirmez .


1
"ama neden sadece değişken bir şablon ve sizeof ... kullanmıyoruz (kendi cevabımda olduğu gibi)" c ++ bir canavar haline geldi. Çok fazla özelliği vardır ve bunların çoğu, çeşitli şablonlar gibi, nadiren kullanılır. Okuyorsunuz, bazı örnekler yazıyorsunuz ve sonra unutuyorsunuz. Bu nedenle, doğru zamanda doğru fikir bulmak zordur. Sizin çözümünüz benimkinden daha iyi bir seçenek gibi göründüğü için doğal seleksiyonun çalışmasına izin vereceğim ve çözümümü sileceğim.
zdf

1
@ZDF anlaşılabilir, ancak sürekli olarak çeşitli şablonlar kullanıyorum. Programlarım C ++ 11'den beri çok daha sağlam hale geldi ve bu, bunun ana nedenlerinden biri. Cevabınızı silmenize gerek yok sanırım.
monkey0506

1
Gibi işe yaramaz VA_COUNT(&,^,%). Ayrıca, bir işlev aracılığıyla sayıyorsanız, makro yapmanın bir anlamı yok.
Qwertiy

Bu çözüm bir soru olarak kalır: VA_COUNT parametrelerinin tümü henüz bir değişken veya başka bir şey olarak tanımlanmamış tanımlayıcılardır ve '*** değişken tanımlanmadı' hatasına neden olur. Bunu düzeltmenin bir yolu var mı?
ipid

7

bu gcc / llvm ile 0 bağımsız değişkenle çalışır. [bağlantılar aptaldır]

/*
 * we need a comma at the start for ##_VA_ARGS__ to consume then
 * the arguments are pushed out in such a way that 'cnt' ends up with
 * the right count.  
 */
#define COUNT_ARGS(...) COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0)
#define COUNT_ARGS_(z,a,b,c,d,e,f,cnt,...) cnt

#define C_ASSERT(test) \
    switch(0) {\
      case 0:\
      case test:;\
    }

int main() {
   C_ASSERT(0 ==  COUNT_ARGS());
   C_ASSERT(1 ==  COUNT_ARGS(a));
   C_ASSERT(2 ==  COUNT_ARGS(a,b));
   C_ASSERT(3 ==  COUNT_ARGS(a,b,c));
   C_ASSERT(4 ==  COUNT_ARGS(a,b,c,d));
   C_ASSERT(5 ==  COUNT_ARGS(a,b,c,d,e));
   C_ASSERT(6 ==  COUNT_ARGS(a,b,c,d,e,f));
   return 0;
}

Visual Studio, boş bağımsız değişkeni tüketmek için kullanılan ## operatörünü yok sayıyor gibi görünüyor. Muhtemelen bunun etrafından dolanabilirsiniz.

#define CNT_ COUNT_ARGS
#define PASTE(x,y) PASTE_(x,y)
#define PASTE_(x,y) x ## y
#define CNT(...) PASTE(ARGVS,PASTE(CNT_(__VA_ARGS__),CNT_(1,##__VA_ARGS__)))
//you know its 0 if its 11 or 01
#define ARGVS11 0
#define ARGVS01 0
#define ARGVS12 1
#define ARGVS23 2
#define ARGVS34 3

Bunu Visual Studio 2008 için test ettim ve 0 bağımsız değişken için çalışmadı COUNT_ARGS () = 1.
user720594

Bağlantı kopmuş görünüyor.
Jan Smrčina

sabit bağlantı. VS her zamanki gibi farklı bir şey yapıyor olmalı :). Yakın zamanda C99'u tam olarak destekleyeceklerini sanmıyorum.
user1187902

2
Er, ##__VA_ARGS__eğer __VA_ARGS__boşsa virgülden önce yemek bir GCC uzantısıdır. Bu standart davranış değil.
Fund Monica'nın Davası

6

Msvc uzantısı ile:

#define Y_TUPLE_SIZE(...) Y_TUPLE_SIZE_II((Y_TUPLE_SIZE_PREFIX_ ## __VA_ARGS__ ## _Y_TUPLE_SIZE_POSTFIX,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 Y_TUPLE_SIZE_II(__args) Y_TUPLE_SIZE_I __args

#define Y_TUPLE_SIZE_PREFIX__Y_TUPLE_SIZE_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0

#define Y_TUPLE_SIZE_I(__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,...) __n

0 - 32 argüman için çalışır. Bu sınır kolaylıkla uzatılabilir.

DÜZENLEME: Basitleştirilmiş sürüm (VS2015 14.0.25431.01 Güncelleme 3 ve gcc 7.4.0'da çalışır) kopyalayıp yapıştırmak için en fazla 100 bağımsız değişken:

#define COUNTOF(...) _COUNTOF_CAT( _COUNTOF_A, ( 0, ##__VA_ARGS__, 100,\
    99, 98, 97, 96, 95, 94, 93, 92, 91, 90,\
    89, 88, 87, 86, 85, 84, 83, 82, 81, 80,\
    79, 78, 77, 76, 75, 74, 73, 72, 71, 70,\
    69, 68, 67, 66, 65, 64, 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 _COUNTOF_CAT( a, b ) a b
#define _COUNTOF_A( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9,\
    a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,\
    a20, a21, a22, a23, a24, a25, a26, a27, a28, a29,\
    a30, a31, a32, a33, a34, a35, a36, a37, a38, a39,\
    a40, a41, a42, a43, a44, a45, a46, a47, a48, a49,\
    a50, a51, a52, a53, a54, a55, a56, a57, a58, a59,\
    a60, a61, a62, a63, a64, a65, a66, a67, a68, a69,\
    a70, a71, a72, a73, a74, a75, a76, a77, a78, a79,\
    a80, a81, a82, a83, a84, a85, a86, a87, a88, a89,\
    a90, a91, a92, a93, a94, a95, a96, a97, a98, a99,\
    a100, n, ... ) n

4
sadece ben mi yoksa bu kod koku kurallarını çiğniyor mu ..?
osirisgothra

Benim için en az VS2012'ye kadar VC ++ ve temel testlerimde GCC ve clang ile çalışıyor.
ThreeBit

@osirisgothra, tam olarak neden kokuyor?
ceztko

Bu makro geniş derleyici desteğine sahip olsa da, makro argümanlar gibi bir dizge ile çalışmaz Y_TUPLE_SIZE("Hello"), onu oldukça olanaksız kılar. @Osirisgothra'ya katılıyorum.
ceztko

1
Bu makro sizin için çalışabilir ancak ciddi kusurları vardır. Çok fazla araştırma yaptım ve GCC ve VS'de işe yarayan daha temiz yaklaşımlar buldum. Bunları benzer bir soruya verdiğim cevapta bulabilirsiniz .
ceztko

3

VA_ARGS için her bir bağımsız değişkenin virgülle ayrılmış olacağını varsayıyorum . Eğer öyleyse, bunun bunu yapmanın oldukça temiz bir yolu olduğunu düşünüyorum.

#include <cstring>

constexpr int CountOccurances(const char* str, char c) {
    return str[0] == char(0) ? 0 : (str[0] == c) + CountOccurances(str+1, c);
}

#define NUMARGS(...) (CountOccurances(#__VA_ARGS__, ',') + 1)

int main(){
    static_assert(NUMARGS(hello, world) == 2, ":(")  ;
    return 0;
}

Benim için clang 4 ve GCC 5.1 için godbolt üzerinde çalıştı. Bu, derleme zamanında hesaplanır, ancak önişlemci için değerlendirilmez. Öyleyse , FOR_EACH yapmak gibi bir şey yapmaya çalışıyorsanız , o zaman bu işe yaramayacaktır.


Bu cevap küçümseniyor. İçin bile çalışacak NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)!!! Her arg dize diğer bazı semboller gibi olamaz ','olsa
pterodragon

Parenler için ince ayar yapılması gerekiyor, çünkü int count = NUMARGS( foo(1, 2) );1 yerine 2 üretir. Godbolt.org/z/kpBuOm
jorgbrown

Bu, lambdalarda, işlev çağrılarında veya parametrelerde fazladan virgül içerebilecek herhangi bir şeyde beklendiği gibi çalışmayacaktır.
Nandee

2

burada VA_ARGS'nin 0 veya daha fazla argümanını saymanın basit bir yolu , benim örneğim maksimum 5 değişken varsayar, ancak isterseniz daha fazlasını ekleyebilirsiniz.

#define VA_ARGS_NUM_PRIV(P1, P2, P3, P4, P5, P6, Pn, ...) Pn
#define VA_ARGS_NUM(...) VA_ARGS_NUM_PRIV(-1, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)


VA_ARGS_NUM()      ==> 0
VA_ARGS_NUM(19)    ==> 1
VA_ARGS_NUM(9, 10) ==> 2
         ...

Maalesef, VA_ARGS_NUMmakro ile kullanıldığında yaklaşım yanlış çalışıyor : eğer varsa #define TEST(yani boşsa TEST) ve şu konumda kullanıldığında VA_ARGS_NUM(TEST)0 (sıfır) #if
döndürmüyorsa

@AntonK tam olarak ne yaptığını gönderebilir misin lütfen?
elhadi dp ıpɐɥ ן ǝ

0

Belirteçleri dizebilir ve sayabilirsiniz:

int countArgs(char *args)
{
  int result = 0;
  int i = 0;

  while(isspace(args[i])) ++i;
  if(args[i]) ++result;

  while(args[i]) {
    if(args[i]==',') ++result;
    else if(args[i]=='\'') i+=2;
    else if(args[i]=='\"') {
      while(args[i]) {
        if(args[i+1]=='\"' && args[i]!='\\') {
          ++i;
          break;
        }
        ++i;
      }
    }
    ++i;
  }

  return result;
}

#define MACRO(...) \
{ \
  int count = countArgs(#__VA_ARGS__); \
  printf("NUM ARGS: %d\n",count); \
}

2
Bu cevabı bekleyen düzenlemeye bir göz attım - iki hesabınız varmış gibi görünüyor. Birine sadık kalırsanız, kendi gönderilerinizi onaya gitmeden düzenleyebilirsiniz.
J Richard Snape

0

Boost Ön İşlemcisi, aslında Boost 1.49'dan itibaren buna sahiptir. BOOST_PP_VARIADIC_SIZE(...) . 64 bedene kadar çalışır.

Başlık altında, temelde Kornel Kisielewicz'in cevabı ile aynı .


@CarloWood Gerçekten. Önişlemci gerçekten "sıfır bağımsız değişken" kavramına sahip değil. "Sıfır argüman" olarak düşündüğümüz şey, önişlemcideki "boş bir argüman" dır. Ancak önceki virgülün kaldırılması için C ++ 20'ler__VA_OPT__ veya derleyici uzantıları kullanılarak düzeltilebilir ##__VA_ARGS__, örneğin: godbolt.org/z/X7OvnK
Justin

0

Burada cevaplar buldum hala eksik.

Buradan bulduğum en yakın taşınabilir uygulama şudur: C ++ önişlemci __VA_ARGS__ argüman sayısı

Ancak, en azından GCC'deki sıfır bağımsız değişkenlerle çalışmaz. -std=gnu++11 komut satırı parametresi .

Bu yüzden bu çözümü bununla birleştirmeye karar verdim: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

#define UTILITY_PP_CONCAT_(v1, v2) v1 ## v2
#define UTILITY_PP_CONCAT(v1, v2) UTILITY_PP_CONCAT_(v1, v2)

#define UTILITY_PP_CONCAT5_(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4

#define UTILITY_PP_IDENTITY_(x) x
#define UTILITY_PP_IDENTITY(x) UTILITY_PP_IDENTITY_(x)

#define UTILITY_PP_VA_ARGS_(...) __VA_ARGS__
#define UTILITY_PP_VA_ARGS(...) UTILITY_PP_VA_ARGS_(__VA_ARGS__)

#define UTILITY_PP_IDENTITY_VA_ARGS_(x, ...) x, __VA_ARGS__
#define UTILITY_PP_IDENTITY_VA_ARGS(x, ...) UTILITY_PP_IDENTITY_VA_ARGS_(x, __VA_ARGS__)

#define UTILITY_PP_IIF_0(x, ...) __VA_ARGS__
#define UTILITY_PP_IIF_1(x, ...) x
#define UTILITY_PP_IIF(c) UTILITY_PP_CONCAT_(UTILITY_PP_IIF_, c)

#define UTILITY_PP_HAS_COMMA(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
#define UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_(...) ,

#define UTILITY_PP_IS_EMPTY(...) UTILITY_PP_IS_EMPTY_( \
    /* test if there is just one argument, eventually an empty one */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__),                                \
    /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
    /* test if the argument together with a parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__ ()),                             \
    /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()))

#define UTILITY_PP_IS_EMPTY_(_0, _1, _2, _3) UTILITY_PP_HAS_COMMA(UTILITY_PP_CONCAT5_(UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_0001 ,

#define UTILITY_PP_VA_ARGS_SIZE(...) UTILITY_PP_IIF(UTILITY_PP_IS_EMPTY(__VA_ARGS__))(0, UTILITY_PP_VA_ARGS_SIZE_(__VA_ARGS__, UTILITY_PP_VA_ARGS_SEQ64()))
#define UTILITY_PP_VA_ARGS_SIZE_(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__))

#define UTILITY_PP_VA_ARGS_TAIL(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14, x, ...) x
#define UTILITY_PP_VA_ARGS_SEQ64() 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0

#define EATER0(...)
#define EATER1(...) ,
#define EATER2(...) (/*empty*/)
#define EATER3(...) (/*empty*/),
#define EATER4(...) EATER1
#define EATER5(...) EATER2
#define MAC0() ()
#define MAC1(x) ()
#define MACV(...) ()
#define MAC2(x,y) whatever

static_assert(UTILITY_PP_VA_ARGS_SIZE() == 0, "1");
static_assert(UTILITY_PP_VA_ARGS_SIZE(/*comment*/) == 0, "2");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a) == 1, "3");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b) == 2, "4");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c) == 3, "5");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d) == 4, "6");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d, e) == 5, "7");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void)) == 1, "8");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void), b, c, d) == 4, "9");
static_assert(UTILITY_PP_VA_ARGS_SIZE(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_) == 1, "10");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER0) == 1, "11");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER1) == 1, "12");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER2) == 1, "13");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER3) == 1, "14");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER4) == 1, "15");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC0) == 1, "16");
// a warning in msvc
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC1) == 1, "17");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MACV) == 1, "18");
// This one will fail because MAC2 is not called correctly
//static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC2) == 1, "19");

https://godbolt.org/z/3idaKd

  • c++11, msvc 2015, gcc 4.7.1,clang 3.0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.