Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 14.08.11 11:39
Оценка: 47 (5)
Уже не раз поднимался вопрос о получении количества аргументов в __VA_ARGS__ (для случая 0 аргументов) и итеративном переборе каждого элемента в последовательности, например: темы (1)
Автор: Владислав Курмаз
Дата: 12.08.11
(посты 1
Автор: jazzer
Дата: 12.08.11
, 2
Автор: Владислав Курмаз
Дата: 13.08.11
) и (2)
Автор: jyuyjiyuijyu
Дата: 25.02.11
. Полного решения я так и не нашел.

Возможно, заинтересует следующее решение:
1. макрос VA_SIZE(...) — возвращает количество переданных аргументов в диапазоне от 0 до 10 (лимит может быть легко увеличен):
— VA_SIZE() возвращает 0;
— VA_SIZE(a) возвращает 1;
— VA_SIZE(a,b) возвращает 2;
— и т.д.
Реализация:
#define VA_SIZE(...) INVOKE( VA_GET_SIZE VA_OB INVOKE(VA_SPEC##__VA_ARGS__()), 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 VA_CB )

#define VA_OB (
#define VA_CB )
#define VA_SPEC() 1,2,3,4,5,6,7,8,9,10,11
#define VA_GET_SIZE(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_,n,...) n

#define INVOKE( ... ) INVOKE_A( __VA_ARGS__ )
#define INVOKE_A( ... ) __VA_ARGS__

2. макрос VA_FOR(macro,data,...) вызывает macro(data, x) для каждого элемента последовательности __VA_ARGS__, где x — элемент последовательности:
— VA_FOR(MACRO, Y,) преобразуется в пустую строку (т.е. ни во что)
— VA_FOR(MACRO, Y, a) -> MACRO(Y,a)
— VA_FOR(MACRO, Y, a, b) -> MACRO(Y,a) MACRO(Y,b)
— и т.д.
Реализация
#define VA_FOR(macro,data,...) INVOKE( CAT(VA_FOR, VA_SIZE(__VA_ARGS__)) ( macro, data, (__VA_ARGS__) ) )

#define VA_APPLY(x) x
#define VA_FIRST(a, ...) a
#define VA_WO_FIRST(a, ...) __VA_ARGS__

#define VA_FOR0(m,d,x)
#define VA_FOR1(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )
#define VA_FOR2(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR1( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR3(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR2( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR4(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR3( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR5(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR4( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR6(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR5( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR7(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR6( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR8(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR7( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR9(m,d,x)  m( d, VA_APPLY(VA_FIRST x) )  VA_FOR8( m, d, (VA_APPLY(VA_WO_FIRST x)))
#define VA_FOR10(m,d,x) m( d, VA_APPLY(VA_FIRST x) )  VA_FOR9( m, d, (VA_APPLY(VA_WO_FIRST x)))

#define CAT(x,y) CAT_A(x, y)
#define CAT_A(x,y) x##y

Протестировано на MS Visual C++ 2010 и MinGW GCC 4.5.2.
Буду рад, если кому-нибудь пригодится...
Re: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Владислав Курмаз Украина http://tis-method.org/
Дата: 15.08.11 09:12
Оценка: 4 (1)
Хочу дополнить.

Есть в разработке буста вот такая библиотека VMD,
http://svn.boost.org/svn/boost/sandbox/variadic_macro_data/boost/variadic_macro_data

и там присутствует аналогичный VA_SIZE макрос BOOST_VMD_DATA_SIZE
Re[2]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 09:38
Оценка:
Здравствуйте, Владислав Курмаз, Вы писали:

ВК>Хочу дополнить.


ВК>Есть в разработке буста вот такая библиотека VMD,

ВК>http://svn.boost.org/svn/boost/sandbox/variadic_macro_data/boost/variadic_macro_data

ВК>и там присутствует аналогичный VA_SIZE макрос BOOST_VMD_DATA_SIZE


Спасибо, буду знать.
Re[2]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 09:43
Оценка:
Здравствуйте, Владислав Курмаз, Вы писали:

ВК>Хочу дополнить.


ВК>Есть в разработке буста вот такая библиотека VMD,

ВК>http://svn.boost.org/svn/boost/sandbox/variadic_macro_data/boost/variadic_macro_data

ВК>и там присутствует аналогичный VA_SIZE макрос BOOST_VMD_DATA_SIZE


Вырезка из vmd_data.hpp
/** \def BOOST_VMD_DATA_SIZE(...)

    \brief Expands to the number of comma-separated variadic macro data arguments.

    ... = variadic macro data.

    returns = the number of comma-separated variadic macro data
              arguments being passed to it.
    
    The value returned can be between 1 and 64.
    
*/

Ограничение на данную реализацию: от 1 аргумента и выше.
Re: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Masterkent  
Дата: 15.08.11 09:44
Оценка: 2 (1)
SX:

SX>- VA_SIZE() возвращает 0;


Это нестандартное использование. C99 и C++0x требуют (ХЗ почему) передачи по меньшей мере одного аргумента в такой макрос:

C99 (N1256) — 6.10.3/4:

If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).


C++0x (N3290) — 16.3/4:

If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).

Re[2]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 09:48
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>SX:


SX>>- VA_SIZE() возвращает 0;


M>Это нестандартное использование. C99 и C++0x требуют (ХЗ почему) передачи по меньшей мере одного аргумента в такой макрос:


M>C99 (N1256) — 6.10.3/4:

M>C++0x (N3290) — 16.3/4:

Тогда, реализацию можно изменить с учетом такого вызова:
— VA_SIZE( () ) возвращает 0;
— VA_SIZE( (a) ) -> 1;
— VA_SIZE( (a,b) ) -> 2;
— и т.д.
Использование:
VA_SIZE( (__VA_ARGS__) ) или по аналогии.
Re[3]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 09:58
Оценка:
SX>Здравствуйте, Masterkent, Вы писали:

SX>>>- VA_SIZE() возвращает 0;


M>>Это нестандартное использование. C99 и C++0x требуют (ХЗ почему) передачи по меньшей мере одного аргумента в такой макрос:


M>>C99 (N1256) — 6.10.3/4:

M>>C++0x (N3290) — 16.3/4:

Или так:
— VA_SIZE(, ) возвращает 0;
— VA_SIZE(, a ) -> 1;
— VA_SIZE(, a,b ) -> 2;
— и т.д.
Использование:
VA_SIZE(, __VA_ARGS__ ) или по аналогии.

Но за описание проблемы, спасибо!
Re[3]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Владислав Курмаз Украина http://tis-method.org/
Дата: 15.08.11 11:05
Оценка: 4 (1)
Здравствуйте, SX, Вы писали:

SX>Ограничение на данную реализацию: от 1 аргумента и выше.

Да, да, я видел
Если либа попадёт в буст, то я думаю автор поправит это.
А ваш код я унёс к себе в библиотеку, спасибо.
Конечно с указанием авторства и ссылкой на рсдн.
Re[2]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Masterkent  
Дата: 15.08.11 12:53
Оценка: 1 (1)
Masterkent:

M>Это нестандартное использование. C99 и C++0x требуют (ХЗ почему) передачи по меньшей мере одного аргумента в такой макрос:


Впрочем, вероятно, я поторопился с выводами Вроде, нигде не сказано, что аргумент для ... не может состоять из пустой последовательности токенов (для обычных параметров пустая последовательность точно допустима). Если пустая последовательность — тоже корректный аргумент, то VA_SIZE() можно рассматривать как использование макроса с одним аргументом (состоящим из пустой последовательности токенов), и тогда всё законно (правда, VA_SIZE(), получается, означает не количество переданных аргументов, т.к. их не 0, а 1 ).

#define MACRO(x, ...)
MACRO(a, b) // законное использование
MACRO(a,)   // законное использование
MACRO(a)    // незаконное использование
Re: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Владислав Курмаз Украина http://tis-method.org/
Дата: 15.08.11 13:52
Оценка:
Здравствуйте, SX, Вы писали:

SX>Буду рад, если кому-нибудь пригодится...

Попробовал использовать. Маленькое пожелание, сделать имена макросов более семантически нагруженными, чтобы избежать коллизий
INVOKE -> SX_VA_INVOKE и т.д.
Re[3]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 15:43
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Если пустая последовательность — тоже корректный аргумент, то VA_SIZE() можно рассматривать как использование макроса с одним аргументом (состоящим из пустой последовательности токенов), и тогда всё законно (правда, VA_SIZE(), получается, означает не количество переданных аргументов, т.к. их не 0, а 1 ).


M>
#define MACRO(x, ...)
M>MACRO(a, b) // законное использование
M>MACRO(a,)   // законное использование
M>MACRO(a)    // незаконное использование


Попытаюсь объяснить свою точку зрения, по которой вызов VA_SIZE() должен возвращать именно 0, а не 1.
Предположим у нас имеются кортежи, оформленные в коде, как:
#define TUPLE_A (a, b, c) 
#define TUPLE_B (a)       
#define TUPLE_C () // пустой кортеж
#define TUPLE_D (   ) // тоже пустой кортеж

Отсутствие элементов в кортеже (или пустая последовательность токенов в нём) по здравой логике трактуется как кортеж с 0 аргументами.
Для определения количества элементов в кортеже нужно именно такое поведение VA_SIZE. Пример кода:
#define TUPLE_SIZE(tuple) INVOKE_B( VA_SIZE VA_OB TUPLE_TO_VA tuple VA_CB )
#define TUPLE_TO_VA(...) __VA_ARGS__
#define INVOKE_B(...) INVOKE_C( __VA_ARGS__ )
#define INVOKE_C(...) __VA_ARGS__

// Результаты
TUPLE_SIZE( TUPLE_A ) // -> 3
TUPLE_SIZE( TUPLE_B ) // -> 1
TUPLE_SIZE( TUPLE_C ) // -> 0
TUPLE_SIZE( TUPLE_D ) // -> 0

Чего и ожидали...

С уважением, Сергей.
Re[2]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 15.08.11 16:00
Оценка:
Здравствуйте, Владислав Курмаз, Вы писали:

ВК>Попробовал использовать. Маленькое пожелание, сделать имена макросов более семантически нагруженными, чтобы избежать коллизий

ВК>INVOKE -> SX_VA_INVOKE и т.д.

По поводу коллизий имен — это, конечно, правильно (особенно для препроцессора). Жаль, что для него нет аналогов пространств имен.

Код приведен, большей частью для описания идеи. А использование префикса SX_ уж очень не скромно, на мой взгляд . Думаю, нет смысла навязывать конкретный префикс пока это просто пара макросов. А так, достаточно и PP_.

Спасибо Вам за интерес к теме.

С уважением, Сергей.
Re[4]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: Masterkent  
Дата: 16.08.11 07:26
Оценка: 4 (1)
SX:

SX>Предположим у нас имеются кортежи, оформленные в коде, как:

SX>
SX>#define TUPLE_A (a, b, c) 
SX>#define TUPLE_B (a)       
SX>#define TUPLE_C () // пустой кортеж
SX>#define TUPLE_D (   ) // тоже пустой кортеж
SX>

SX>Отсутствие элементов в кортеже (или пустая последовательность токенов в нём) по здравой логике трактуется как кортеж с 0 аргументами.

Твоя реализация VA_SIZE всё же накладывает дополнительные ограничения на первый аргумент макроса. Применение ## должно образовывать valid preprocessing
token, поэтому в качестве первого аргумента такого VA_SIZE, например, нельзя использовать (a) или "a".
Re[5]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 16.08.11 09:44
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Твоя реализация VA_SIZE всё же накладывает дополнительные ограничения на первый аргумент макроса. Применение ## должно образовывать valid preprocessing

M>token, поэтому в качестве первого аргумента такого VA_SIZE, например, нельзя использовать (a) или "a".

Согласен. Нужно думать как быть в этом случае. Если решение есть, то, думаю, мы обязательно о нем узнаем в будущем...

offtop
А топики можно править?
Re: Новая версия
От: SX Россия  
Дата: 17.08.11 10:06
Оценка: 3 (1)
Доброго времени суток!

Предлагается обновленная реализация макроса VA_SIZE().
В ней снимаются ограничения на использование в качестве первого аргумента последовательностей вида "xxx" и (yyy).

Ограничения на текущую реализацию:


Реализация:
#define PP_VA_SIZE(...) PP_CAT(PP_VA_SIZE_, PP_VA_GET(__VA_ARGS__,1N,1N,1N,1N,1N,1N,1N,1N,1N,01,_) )(,__VA_ARGS__)

#define PP_VA_SIZE_1N(_,...) PP_VA_GET(__VA_ARGS__,10,9,8,7,6,5,4,3,2,1)

#define PP_VA_SIZE_01(_,x) PP_VA_GET( PP_VA_INVOKE( PP_VA_SPEC_B, PP_VA_INVOKE(PP_VA_SPEC_A, x (N1)) PP_VA_NONE (N0)),0,1,1,1,1,1,1,1,1,1,__)

#define PP_VA_SPEC_A(...)
#define PP_VA_SPEC_B(x) PP_CAT(PP_VA_SPEC_, x)
#define PP_VA_SPEC_N0 1,2,3,4,5,6,7,8,9,10

#define PP_VA_GET(...) PP_VA_INVOKE( PP_VA_GET_A, (__VA_ARGS__) )
#define PP_VA_GET_A(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,R,...) R
#define PP_VA_NONE

#define PP_VA_INVOKE(m,a) PP_VA_INVOKE_A( m a )
#define PP_VA_INVOKE_A(...) __VA_ARGS__

#define PP_CAT(x,y) PP_CAT_A(x,y)
#define PP_CAT_A(x,y) x##y


Результат работы:
#define VA_A 
#define VA_B a
#define VA_C "1"
#define VA_D ()
#define VA_E (aaa,a)
#define VA_F 1,2

VA_SIZE(VA_A)    -> 0
VA_SIZE(VA_B)    -> 1
VA_SIZE(VA_C)    -> 1
VA_SIZE(VA_D)    -> 1
VA_SIZE(VA_E)    -> 1
VA_SIZE(VA_F)    -> 2


Протестировано на MS Visual C++ 2010 и MinGW GCC 4.5.2.
Буду рад, если кому-нибудь пригодится...

С уважением, Сергей.
Re[6]: Снова __VA_ARGS__. Теперь для 0 аргументов.
От: SX Россия  
Дата: 17.08.11 10:06
Оценка:
Здравствуйте, SX, Вы писали:

SX>Здравствуйте, Masterkent, Вы писали:


M>>Твоя реализация VA_SIZE всё же накладывает дополнительные ограничения на первый аргумент макроса. Применение ## должно образовывать valid preprocessing

M>>token, поэтому в качестве первого аргумента такого VA_SIZE, например, нельзя использовать (a) или "a".

SX>Согласен. Нужно думать как быть в этом случае. Если решение есть, то, думаю, мы обязательно о нем узнаем в будущем...


Будущее уже наступило
offtop: Вчера вечером лег спать около 23:00. Долго не мог уснуть. Приблизительно в 23:45 придумалось РЕШЕНИЕ! Вывод: нужно больше отдыхать...

Для того, чтобы в качестве первого параметра принималась последовательность вида "a", необходимо убрать из реализации VA_SIZE преобразование ##.
Вместо
  #define VA_SIZE(...) INVOKE( VA_GET_SIZE VA_OB INVOKE(VA_SPEC##__VA_ARGS__()), 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 VA_CB )
надо написать
  #define VA_SIZE(...) INVOKE( VA_GET_SIZE VA_OB INVOKE(VA_SPEC __VA_ARGS__()), 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 VA_CB )

Но это не снимает ограничение на последовательность вида (a). Но и это не предел. Это последовательность тоже может быть обработана.
Реализация здесь
Автор: SX
Дата: 17.08.11
.

Правда, одно ограничение все же есть, а именно:
последовательность символов, подаваемая в качестве аргумента(ов) VA_SIZE, не может содержать один элемент — макрос
MACROS
, где MACROS — имя макроса.

В то же время последовательности, содержащие более одного макроса:
MACROS1, MACROS2
MACROS1, MACROS2, MACROS3
и т.д.
являются корректными.

С уважением, Сергей.
Re[2]: Новая версия
От: SX Россия  
Дата: 17.08.11 10:09
Оценка:
SX>Предлагается обновленная реализация макроса VA_SIZE().

Не уточнил: PP_VA_SIZE — новое имя для VA_SIZE.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.