Bağımsız Değişken Sayısında Aşırı Yükleme Makrosu


184

İki makro var FOO2ve FOO3:

#define FOO2(x,y) ...
#define FOO3(x,y,z) ...

FOOAşağıdaki gibi yeni bir makro tanımlamak istiyorum :

#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)

Ancak bu işe yaramaz, çünkü makrolar argüman sayısına aşırı yüklenmez.

Değiştirmeden FOO2ve FOO3bir makro tanımlamak için bir yol yoktur FOO(kullanarak __VA_ARGS__sevk aynı etkiyi elde etmek için veya başka) FOO(x,y)için FOO2, ve FOO(x,y,z)hiç FOO3?


1
Bunun daha önce birkaç kez sorulduğuna dair çok güçlü bir his var ... [güncelleme] örneğin burada .
Kerrek SB

1
@KerrekSB: Kesinlikle bir dupe olmasa da bu ilgili olabilir.
Andrew Tomazos

Hayır, belki de değil, ama bunun gibi bir şey ayda bir kez ortaya çıkıyor ...
Kerrek SB


Yanıtlar:


265

Basit:

#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)

Bu makrolara sahipseniz:

FOO(World, !)         # expands to FOO2(World, !)
FOO(foo,bar,baz)      # expands to FOO3(foo,bar,baz)

Dördüncü bir tane istiyorsanız:

#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)

FOO(a,b,c,d)          # expeands to FOO4(a,b,c,d)

Doğal olarak, tanımlarsanız FOO2, FOO3ve FOO4çıkış tanımlanan makro olanlar ile değiştirilecektir.


5
@ Uroc327 Listeye 0 bağımsız değişkenli bir makro eklemek mümkündür, cevabımı görün.
augurar

7
Microsoft Visual Studio 2010 üzerinde çalışmaz, VA_ARGS tek bir makro bağımsız değişkene genişletilmiş gibi görünüyor.
Étienne

9
Bulunan bu cevabı o MSVC 2010. altında çalışması için
Étienne

8
Eğer kimse EXPAND@ Étienne'nin linkinde belirtilenin nasıl kullanılacağı konusunda kafanız karışırsa , temelde GET_MACROböyle #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))çağırırsınız ve msvc'de doğru sayıda argümana genişlemelidir.
vexe

3
C ++ 11, bir uyarı alırsınız Not: ISO C++11 requires at least one argument for the "..." in a variadic macro. Bunu düzeltmek için, FOO (...) tanımındaki son parametreden sonra kullanılmayan bir argüman (hatta sadece virgül) ekleyin: #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( Bkz. Coliru üzerinde çalışıyor ).
metal

49

Netcoder'ın cevabına eklemek için , aslında bunu GCC ##__VA_ARGS__uzantısı yardımıyla 0 bağımsız değişkenli bir makro ile yapabilirsiniz :

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)

1
olmadan FOO1 ve FOO2'ye izin verilebilir ancak FOO0'a izin verilmez #define FOO0 _Pragma("error FOO0 not allowed")mi?
noɥʇʎԀʎzɐɹƆ

FOO0qt + mingw32'de çalışmıyorsa, çağrıFOO0FOO1
JustWe

Çok umut verici ve basit. Ancak -std = c ++ 11 ... :-(
leonp

1
Aynı sorunu C olarak yapıyorsanız ve -std=c99veya kullanmaya çalışırsanız -std=c11. Kullanmanız gerekiyor -std=gnu99ya da -std=gnu11yerine
Michael Mrozek

1
O değiştirilmesi anlaşılmaktadır _0, ##__VA_ARGS__ile _0 __VA_OPT__(,) __VA_ARGS__bunu yapmanın yeni bir yoludur.
Wrzlprmft

36

İşte daha genel bir çözüm:

// get number of arguments with __NARG__
#define __NARG__(...)  __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __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 __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

// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)

// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)

İşlevlerinizi tanımlayın:

#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))

// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }

Artık FOO2, 3 ve 4 argümanlarıyla kullanabilirsiniz:

FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function

Sınırlamalar

  • Yalnızca 63 bağımsız değişkene kadar (ancak genişletilebilir)
  • Yalnızca GCC'de argüman olmadan işlev mümkündür

fikirler

Varsayılan argümanlar için kullanın:

#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)

// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }

Olası sonsuz sayıda bağımsız değişkene sahip işlevler için kullanın:

#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...

Not: __NARG__Laurent Deniau ve Roland Illig'ten buraya kopyalanır: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1




Makro __NARG_I_tamamen gereksiz ve gereksiz görünüyor. Sadece ekstra bir adım ve karışıklık ekler. Ben tamamen silme ve sadece tanımlama tavsiye __NARG__olarak yerine: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).
Gabriel Staples

Yoksa bu bir şekilde ön işlemeyi kırar mı? Bir şey mi kaçırıyorum?
Gabriel Staples

İle aynı _VFUNC_: sadece silin. Ardından, yerine _VFUNC: olarak tanımlayın . #define _VFUNC(name, n) name##n#define _VFUNC(name, n) _VFUNC_(name, n)
Gabriel Staples

15

Bunu kendim araştırıyordum ve buraya rastladım . Yazar, makrolar aracılığıyla C işlevleri için varsayılan bağımsız değişken desteği ekledi.

Makaleyi kısaca özetlemeye çalışacağım. Temel olarak, bağımsız değişkenleri sayabilecek bir makro tanımlamanız gerekir. Bu makro 2, 1, 0 veya destekleyebileceği her türlü argümanı döndürür. Örneğin:

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

Bununla, değişken sayıda bağımsız değişken alan, bağımsız değişkenleri sayan ve uygun makroyu çağıran başka bir makro oluşturmanız gerekir. Örnek makronuzu aldım ve makalenin örneğiyle birleştirdim. FOO1 çağrı fonksiyonu a () ve FOO2 çağrı fonksiyonu bir argüman b ile (Açıkçası, burada C ++ varsayıyorum, ama ne olursa olsun makro değiştirebilirsiniz).

#define FOO1(a) a();
#define FOO2(a,b) a(b);

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)

#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)

#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)

Eğer varsa

FOO(a)
FOO(a,b)

Önişlemci bunu

a();
a(b);

Bağlantı verdiğim makaleyi kesinlikle okurum. Çok bilgilendirici ve NARG2'nin boş argümanlar üzerinde çalışmayacağını belirtti. Bunu burada takip ediyor .


7

İşte yukarıdaki cevabın daha kompakt bir versiyonu . Örnek olarak.

#include <iostream>
using namespace std;

#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args

#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...)   N


//Example:
#define ff(...)     OVERLOADED_MACRO(ff, __VA_ARGS__)
#define ii(...)     OVERLOADED_MACRO(ii, __VA_ARGS__)

#define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
#define ff2(c, b)   ff3(c, 0, b)

#define ii2(a, b)   ff3(i, a, b)
#define ii1(n)      ii2(0, n)


int main() {
    ff (counter, 3, 5)
        cout << "counter = " << counter << endl;
    ff (abc, 4)
        cout << "abc = " << abc << endl;
    ii (3)
        cout << "i = " << i << endl;
    ii (100, 103)
        cout << "i = " << i << endl;


    return 0;
}

Çalıştırmak:

User@Table 13:06:16 /c/T
$ g++ test_overloaded_macros.cpp 

User@Table 13:16:26 /c/T
$ ./a.exe
counter = 3
counter = 4
abc = 0
abc = 1
abc = 2
abc = 3
i = 0
i = 1
i = 2
i = 100
i = 101
i = 102

Her ikisine de sahip olmanın _OVRve _OVR_EXPANDgereksiz görünebileceğini unutmayın , ancak ön işlemcinin _COUNT_ARGS(__VA_ARGS__), bir dize olarak değerlendirilen parçayı genişletmesi gerekir .


Bu çözümü seviyorum. Sıfır argümanlar alan aşırı yüklenmiş bir makroyu işleyecek şekilde değiştirilebilir mi?
Andrew


2

İşte Evgeni Sergeev'in cevabından bir geri dönüş. Bu da sıfır argüman aşırı yüklenmesini destekler !

Bunu GCC ve MinGW ile test ettim. C ++ 'ın eski ve yeni sürümleriyle çalışması gerekir. MSVC için bunu garanti edemeyeceğimi unutmayın ... Ama bazı ince ayarlarla, bununla da çalışabileceğinden eminim.

Ben de (makroutil.h adı verilen) bir başlık dosyasına yapıştırılmak üzere bu biçimlendirilmiş. Bunu yaparsanız, özelliğe ihtiyacınız olan her şeyi bu başlığı dahil edebilir ve uygulamada yer alan sıkıntıya bakamazsınız.

#ifndef MACROUTIL_H
#define MACROUTIL_H

//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
//
// used to create other macros with overloaded argument lists
//
// Example Use:
// #define myMacro(...) OVERLOADED_MACRO( myMacro, __VA_ARGS__ )
// #define myMacro0() someFunc()
// #define myMacro1( arg1 ) someFunc( arg1 )
// #define myMacro2( arg1, arg2 ) someFunc( arg1, arg2 )
//
// myMacro();
// myMacro(1);
// myMacro(1,2);
//
// Note the numerical suffix on the macro names,
// which indicates the number of arguments.
// That is the REQUIRED naming convention for your macros.
//
//-----------------------------------------------------------------------------

// OVERLOADED_MACRO
// derived from: /programming/11761703/overloading-macro-on-number-of-arguments
// replaced use of _COUNT_ARGS macro with VA_NUM_ARGS defined below
// to support of zero argument overloads
#define OVERLOADED_MACRO(M, ...) _OVR(M, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args
//#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, N, ...)   N

// VA_NUM_ARGS
// copied from comments section of:
// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
// which itself was derived from:
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define HAS_NO_COMMA(...) _ARG16(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
#define _TRIGGER_PARENTHESIS_(...) ,

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

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _HAS_ZERO_OR_ONE_ARGS(_0, _1, _2, _3) HAS_NO_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define _VA0(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA1(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA2(...) 2
#define _VA3(...) 3
#define _VA4(...) 4
#define _VA5(...) 5
#define _VA6(...) 6
#define _VA7(...) 7
#define _VA8(...) 8
#define _VA9(...) 9
#define _VA10(...) 10
#define _VA11(...) 11
#define _VA12(...) 12
#define _VA13(...) 13
#define _VA14(...) 14
#define _VA15(...) 15
#define _VA16(...) 16

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, PP_RSEQ_N(__VA_ARGS__) )
#define VA_NUM_ARGS_IMPL(...) VA_NUM_ARGS_N(__VA_ARGS__)

#define VA_NUM_ARGS_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
    _11,_12,_13,_14,_15,_16,N,...) N

#define PP_RSEQ_N(...) \
    _VA16(__VA_ARGS__),_VA15(__VA_ARGS__),_VA14(__VA_ARGS__),_VA13(__VA_ARGS__), \
    _VA12(__VA_ARGS__),_VA11(__VA_ARGS__),_VA10(__VA_ARGS__), _VA9(__VA_ARGS__), \
    _VA8(__VA_ARGS__),_VA7(__VA_ARGS__),_VA6(__VA_ARGS__),_VA5(__VA_ARGS__), \
    _VA4(__VA_ARGS__),_VA3(__VA_ARGS__),_VA2(__VA_ARGS__),_VA1(__VA_ARGS__), \
    _VA0(__VA_ARGS__)

//-----------------------------------------------------------------------------

#endif // MACROUTIL_H

2

Bu GCC, Clang ve MSVC'de iyi çalışıyor gibi görünüyor. Buradaki bazı cevapların temizlenmiş bir versiyonu

#define _my_BUGFX(x) x

#define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
#define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
#define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0

#define _my_FUNC2(name,n) name ## n
#define _my_FUNC1(name,n) _my_FUNC2(name,n)
#define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)

#define FOO(...) GET_MACRO(FOO,__VA_ARGS__)

1
@RianQuinn Bu makro sıfır argümanı ile çalışacak şekilde nasıl ayarlanır #define func0() foo? Mevcut sürüm maalesef bu durumu ele almıyor.
Jerry Ma
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.