├── .appveyor.yml
├── .travis.yml
├── LICENSE_1_0.txt
├── VA_OPT.hpp
├── VA_OPT_TEST.cpp
├── VA_OPT_UNDEF.hpp
├── meson.build
└── readme.md
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | build:
2 | verbosity: detailed
3 |
4 | branches:
5 | only:
6 | - master
7 |
8 | platform: x64
9 |
10 | environment:
11 | matrix:
12 | # Visual Studio 2019
13 | - GENERATOR: Visual Studio 16 2019 Win64
14 | STDFLAGS: /std:c++latest
15 | SCRIPT: support/vs.py
16 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
17 |
18 | install:
19 | - git submodule -q update --init
20 | # Install Ninja
21 | - cmd: mkdir C:\ninja-build
22 | - ps: (new-object net.webclient).DownloadFile('https://github.com/mesonbuild/cidata/raw/master/ninja.exe', 'C:\ninja-build\ninja.exe')
23 | # Set paths to dependencies (based on architecture)
24 | - cmd: set PYTHON_ROOT=C:\python37-x64
25 | # Print out dependency paths
26 | - cmd: echo Using Python at %PYTHON_ROOT%
27 | # Add neccessary paths to PATH variable
28 | - cmd: set PATH=%cd%;C:\ninja-build;%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH%
29 | # Install meson
30 | - cmd: pip install meson
31 | - cmd: meson --version
32 | - cmd: ninja --version
33 |
34 | before_build:
35 | # Enable the Visual C++ toolset for command-line builds.
36 | - IF "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (
37 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
38 | )
39 |
40 | build_script:
41 | - cmd: echo Building on %arch% with %compiler%
42 | - cmd: meson --backend=vs2019 --buildtype=release -Dcpp_args=/experimental:preprocessor -Dcpp_std=c++11 c++11
43 | - cmd: meson --backend=vs2019 --buildtype=release -Dcpp_args=/experimental:preprocessor -Dcpp_std=c++17 c++17
44 | #- cmd: meson --backend=vs2019 --buildtype=release -Dcpp_args=/experimental:preprocessor -Dcpp_std=c++latest c++latest
45 | - cmd: msbuild c++11\test_VA_OPT@exe.vcxproj /p:configuration=release
46 | - cmd: msbuild c++17\test_VA_OPT@exe.vcxproj /p:configuration=release
47 | #- cmd: msbuild c++latest\test_VA_OPT@exe.vcxproj /p:configuration=release
48 |
49 | test_script:
50 | #- cmd: ninja -C c++11 test
51 | #- cmd: ninja -C c++17 test
52 | #- cmd: ninja -C c++latest test
53 |
54 | only_commits:
55 | files:
56 | - .appveyor.yml
57 | - meson.build
58 | - VA_OPT.hpp
59 | - VA_OPT_UNDEF.hpp
60 | - VA_OPT_TEST.cpp
61 | - test/
62 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: xenial
2 | language: python
3 | python: 3.6
4 | sudo: required
5 |
6 | matrix:
7 | include:
8 | ##############
9 | # GCC
10 | ##############
11 | - os: linux
12 | compiler: gcc
13 | env: GCC_VERSION=8
14 | - CC=gcc-8
15 | - CXX=g++-8
16 | addons:
17 | apt:
18 | sources:
19 | - sourceline: 'ppa:ubuntu-toolchain-r/test'
20 | packages: ['g++-8', 'ninja-build']
21 |
22 | ##############
23 | # CLANG
24 | ##############
25 | - os: linux
26 | compiler: clang
27 | env: CLANG_VERSION=7
28 | - CC=clang-7
29 | - CXX=clang++-7
30 | addons:
31 | apt:
32 | sources:
33 | - llvm-toolchain-xenial
34 | - llvm-toolchain-trusty-7
35 | - sourceline: 'ppa:ubuntu-toolchain-r/test'
36 | packages: ['clang-7', 'libc++-7-dev', 'libc++abi-7-dev', 'ninja-build']
37 |
38 | ##############
39 | # OSX / APPLECLANG (Python download failing)
40 | ##############
41 | #- os: osx
42 | # osx_image: xcode10.1
43 | # env:
44 | # - CXX=clang
45 | # - OSX=1
46 |
47 | before_install:
48 | - echo "Before install"
49 | - |
50 | git submodule init
51 | git submodule update
52 | - |
53 | if [ "${CLANG_VERSION}" == "7" ]; then
54 | export CXXFLAGS="-stdlib=libc++"
55 | fi
56 |
57 | install:
58 | - mkdir -p build
59 | - pip install meson
60 |
61 | script:
62 | - which $CXX
63 | - $CXX --version
64 | - meson -Dcpp_std=c++11 c++11
65 | - meson -Dcpp_std=c++17 c++17
66 | - meson -Dcpp_std=c++2a c++2a
67 | - ninja -C c++11 test
68 | - ninja -C c++17 test
69 | - ninja -C c++2a test
70 |
--------------------------------------------------------------------------------
/LICENSE_1_0.txt:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/VA_OPT.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Will Wray https://keybase.io/willwray
2 | //
3 | // Distributed under the Boost Software License, Version 1.0.
4 | // http://www.boost.org/LICENSE_1_0.txt
5 | //
6 | // Repo: https://github.com/willwray/VA_OPT
7 |
8 | #pragma once
9 |
10 | /*
11 | VA_OPT.hpp
12 | ==========
13 |
14 | Preprocessor utilities for testing emptiness of macro arguments
15 | and for conditional expansion based on the emptiness of ARGS.
16 |
17 | VA_OPT_SUPPORT(?)
18 | 1 if __VA_OPT__ support is detected else 0 (see platform note).
19 |
20 | C++20's __VA_OPT__ finally provides a reliable test for empty ARGS.
21 | The following macros use __VA_OPT__, if detected, otherwise they
22 | provide 'polyfill' fallbacks (though with some failing edge cases).
23 |
24 | IS_EMPTY(...)
25 | 1 if the ... ARGS is empty else 0.
26 |
27 | IFN(...)
28 | If ... ARGS are not empty then a trailing 'paren expression' (X)
29 | is deparenthesized to X else the trailing (X) term is consumed.
30 | E.g. IFN()(N) vanishes, while IFN(NotEmpty)(N) -> N
31 |
32 | In other words, IFN(ARGS) is like __VA_OPT__, but with explicit
33 | (ARGS) in place of an implicit __VA_ARGS__ check.
34 |
35 | IFE(...)
36 | If ... ARGS is empty expand trailing 'paren expression' (X) to X
37 | else if ARGS are not empty consume the trailing paren expression.
38 | E.g. IFE(NotEmpty)(E) vanishes, while IFE()(E) -> E
39 |
40 | IFNE(...)(N,E...)
41 | If ... ARGS are not empty expands to N else expands to E...
42 | E.g. IFNE(ARG)(X,()) is equivalent to IFN(ARG)(X)IFE(ARG)(())
43 | both put back a terminating () removed by the outer macro call.
44 |
45 | Without VA_OPT_SUPPORT these 'emptiness' macros are not perfect;
46 | IS_EMPTY, IFN, IFE, IFNE may cause a compile error ('too few args')
47 | if the argument is a function-like macro name that expects ARG(s).
48 |
49 | IBP(...)
50 | IS_BEGIN_PARENS macro to test if an argument is parenthesised:
51 | 1 if ... ARGS begins with a 'paren expression' else 0.
52 |
53 | Platform note: Current Sept 2019 __VA_OPT__ support:
54 | -------------
55 | Clang -std=c++2a enables it. GCC has it enabled without -std=c++2a
56 | but warns "__VA_OPT__ is not available until C++2a" if another -std
57 | flag is supplied along with -pedantic (dont know how to supress it).
58 | MSVC TBD
59 |
60 | Credits
61 | -------
62 | Props to pre-pro pioneers, particularly Paul Mensonides.
63 | The 'emptiness' methods are adapted from BOOST_VMD_IS_EMPTY which,
64 | . in turn, depends on BOOST Preprocessor's BOOST_PP_IS_BEGIN_PARENS
65 | (adapted and exposed here as IBP 'Is Begin Parens'):
66 | www.boost.org/doc/libs/1_71_0/libs/vmd
67 | www.boost.org/doc/libs/1_71_0/libs/preprocessor
68 | */
69 |
70 | #define VA_ARG1(A0,A1,...) A1
71 | // VA_EMPTY works only if __VA_OPT__ is supported, else always -> 1
72 | #define VA_EMPTY(...) VA_ARG1(__VA_OPT__(,)0,1,)
73 |
74 | // VA_OPT_SUPPORT helper macro for __VA_OPT__ feature detection.
75 | // Adapted from https://stackoverflow.com/a/48045656/7443483
76 | // Use as #if VA_OPT_SUPPORT(?)
77 | #define VA_OPT_SUPPORT ! VA_EMPTY
78 |
79 | #if VA_OPT_SUPPORT(?)
80 |
81 | # define IS_EMPTY(...) VA_EMPTY(__VA_ARGS__)
82 | # define IFN(...) VA_EAT __VA_OPT__(()VA_IDENT)
83 | # define IFE(...) VA_IDENT __VA_OPT__(()VA_EAT)
84 | # define IFNE(...) VA_ARGTAIL __VA_OPT__((,)VA_ARG0)
85 |
86 | #else
87 |
88 | # define IS_EMPTY(...) IFP(IBP(__VA_ARGS__))(IE_GEN_0,IE_IBP)(__VA_ARGS__)
89 | # define IFN(...) IFP(IBP(__VA_ARGS__))(GEN_IDENT,EAT_OR_IDENT)(__VA_ARGS__)
90 | # define IFE(...) IFP(IBP(__VA_ARGS__))(GEN_EAT,IDENT_OR_EAT)(__VA_ARGS__)
91 | # define IFNE(...) IFP(IBP(__VA_ARGS__))(GEN_ARGTAIL,ARG0_OR_TAIL)(__VA_ARGS__)
92 |
93 | #endif
94 |
95 | #define VA_EAT(...)
96 | #define VA_IDENT(...) __VA_ARGS__
97 | #define VA_ARG0_(A0,...) A0
98 | #define VA_ARG0(...) VA_ARG0_(__VA_ARGS__)
99 | #define VA_ARGTAIL_(A0,...) __VA_ARGS__
100 | #define VA_ARGTAIL(...) VA_ARGTAIL_(__VA_ARGS__)
101 |
102 | // IFP helper macros to test IBP for IFN and IS_EMPTY
103 | #define IFP_0(T,...) __VA_ARGS__
104 | #define IFP_1(T,...) T
105 |
106 | #define IFP_CAT(A,...) A##__VA_ARGS__
107 | #define IFP(BP) IFP_CAT(IFP_,BP)
108 |
109 | // IS_BEGIN_PAREN helper macros adapted from BOOST VMD
110 | #define IBP_CAT_(A,...) A##__VA_ARGS__
111 | #define IBP_CAT(A,...) IBP_CAT_(A,__VA_ARGS__)
112 |
113 | #define IBP_ARG0_(A,...) A
114 | #define IBP_ARG0(...) IBP_ARG0_(__VA_ARGS__)
115 |
116 | #define IBP_IS_ARGS(...) 1
117 |
118 | #define IBP_1 1,
119 | #define IBP_IBP_IS_ARGS 0,
120 |
121 | // IBP IS_BEGIN_PAREN returns 1 or 0 if ... ARGS is parenthesised
122 | #define IBP(...) IBP_ARG0(IBP_CAT(IBP_, IBP_IS_ARGS __VA_ARGS__))
123 |
124 | // IFN, IFE, IFNE and IF_EMPTY helpers without __VA_OPT__ support
125 | #if ! VA_OPT_SUPPORT(?)
126 |
127 | # define IBP_(T,...) IBP_ARG0(IBP_CAT(IF##T##_, IBP_IS_ARGS __VA_ARGS__))
128 |
129 | // IS_EMPTY helper macros, depend on IBP
130 | # define IE_REDUCE_IBP(...) ()
131 | # define IE_GEN_0(...) 0
132 | # define IE_IBP(...) IBP(IE_REDUCE_IBP __VA_ARGS__ ())
133 |
134 | # define GEN_IDENT(...) VA_IDENT
135 | # define GEN_EAT(...) VA_EAT
136 | # define GEN_ARGTAIL(...) VA_ARGTAIL
137 | # define GEN_ARG0(...) VA_ARG0
138 |
139 | // IFN, IFE, IFNE helper macros
140 | # define EAT_OR_IDENT(...) IBP_(N,IE_REDUCE_IBP __VA_ARGS__ ())
141 | # define IFN_1 VA_EAT,
142 | # define IFN_IBP_IS_ARGS VA_IDENT,
143 |
144 | # define IDENT_OR_EAT(...) IBP_(E,IE_REDUCE_IBP __VA_ARGS__ ())
145 | # define IFE_1 VA_IDENT,
146 | # define IFE_IBP_IS_ARGS VA_EAT,
147 |
148 | # define ARG0_OR_TAIL(...) IBP_(NE,IE_REDUCE_IBP __VA_ARGS__ ())
149 | # define IFNE_1 VA_ARGTAIL,
150 | # define IFNE_IBP_IS_ARGS VA_ARG0,
151 |
152 | #endif // IFN and IF_EMPTY defs
153 |
--------------------------------------------------------------------------------
/VA_OPT_TEST.cpp:
--------------------------------------------------------------------------------
1 | #include "VA_OPT.hpp"
2 |
3 | /*
4 | Tests for VA_OPT.hpp 'emptiness' macros.
5 |
6 | IS_EMPTY test cases are mostly lifted from BOOST VMD.
7 | Current tests are static_assert-able.
8 | Test cases for preprocessor fail -> compile-fail
9 | will require a test framework with test runner;
10 | the USE_DOCTEST switch is a start.
11 | */
12 |
13 | #if defined(USE_DOCTEST)
14 |
15 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
16 | #include "doctest.h"
17 |
18 | #else
19 |
20 | #define TEST_CASE(...) namespace test
21 | #define CHECK(...) static_assert(__VA_ARGS__,"")
22 | int main() {}
23 |
24 | #endif
25 |
26 | #define DATA
27 | #define EMPTY()
28 | #define OBJECT OBJECT2
29 | #define OBJECT2
30 | #define FUNC(x) FUNC2(x)
31 | #define FUNC2(x)
32 |
33 | TEST_CASE("IS_EMPTY ") {
34 |
35 | CHECK(IS_EMPTY(EMPTY()));
36 | CHECK(IS_EMPTY(DATA EMPTY()));
37 | CHECK(!IS_EMPTY(x EMPTY()));
38 | CHECK(IS_EMPTY(OBJECT EMPTY()));
39 | CHECK(IS_EMPTY(FUNC(z) EMPTY()));
40 |
41 | #define FUNC_GEN() ()
42 | #define FUNC_GEN2(x) ()
43 | #define FUNC_GEN3() anything
44 | #define FUNC_GEN4(x) anything
45 |
46 | #define FUNC_GEN5() (&)
47 | #define FUNC_GEN6(x) (y)
48 | #define FUNC_GEN7() (y,z)
49 |
50 | CHECK(!IS_EMPTY(FUNC_GEN));
51 | CHECK(!IS_EMPTY(FUNC_GEN2));
52 |
53 | CHECK(!IS_EMPTY(FUNC_GEN3));
54 | CHECK(!IS_EMPTY(FUNC_GEN4));
55 |
56 | CHECK(!IS_EMPTY(FUNC_GEN5));
57 | CHECK(!IS_EMPTY(FUNC_GEN6));
58 | CHECK(!IS_EMPTY(FUNC_GEN7));
59 |
60 | #define FUNC_GEN8(x,y) ()
61 | #define FUNC_GEN9(x,y) anything
62 |
63 | // Clang correctly rejects with "too few arguments".
64 | // GCC accepts.
65 | // MSVC?
66 | //CHECK(!IS_EMPTY(FUNC_GEN8));
67 | //CHECK(!IS_EMPTY(FUNC_GEN9));
68 |
69 | }
70 |
71 | TEST_CASE("IFN IFE IFNE")
72 | {
73 | CHECK(!bool{IFN()(1)} );
74 | CHECK( bool{IFN(NEMPTY)(1)} );
75 | CHECK( bool{IFE()(1)} );
76 | CHECK(!bool{IFE(NEMPTY)(1)} );
77 | CHECK( bool{IFNE()(0,1}) );
78 | CHECK( bool{IFNE(NEMPTY)(1,0)} );
79 | }
80 |
81 | #include "VA_OPT_UNDEF.hpp"
--------------------------------------------------------------------------------
/VA_OPT_UNDEF.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | VA_OPT_UNDEF.hpp
3 | ================
4 |
5 | #undef for each name #define'd in VA_OPT.hpp
6 | */
7 |
8 | #undef VA_ARG1
9 | #undef VA_EMPTY
10 | #undef VA_OPT_SUPPORT
11 |
12 | #undef IS_EMPTY
13 | #undef IFN
14 | #undef IFE
15 | #undef IFNE
16 |
17 | #undef VA_EAT
18 | #undef VA_IDENT
19 | #undef VA_ARG0_
20 | #undef VA_ARG0
21 | #undef VA_ARGTAIL_
22 | #undef VA_ARGTAIL
23 |
24 | #undef IFP_0
25 | #undef IFP_1
26 | #undef IFP_CAT
27 | #undef IFP
28 |
29 | #undef IBP_CAT_
30 | #undef IBP_CAT
31 | #undef IBP_ARG0_
32 | #undef IBP_ARG0
33 | #undef IBP_IS_ARGS
34 | #undef IBP_1
35 | #undef IBP_IBP_IS_ARGS
36 | #undef IBP
37 |
38 | #undef IBP_
39 |
40 | #undef IE_REDUCE_IBP
41 | #undef IE_GEN_0
42 | #undef IE_IBP
43 |
44 | #undef GEN_IDENT
45 | #undef GEN_EAT
46 | #undef GEN_ARGTAIL
47 | #undef GEN_ARG0
48 |
49 | #undef EAT_OR_IDENT
50 | #undef IFN_1
51 | #undef IFN_IBP_IS_ARGS
52 |
53 | #undef IDENT_OR_EAT
54 | #undef IFE_1
55 | #undef IFE_IBP_IS_ARGS
56 |
57 | #undef ARG0_OR_TAIL
58 | #undef IFNE_1
59 | #undef IFNE_IBP_IS_ARGS
60 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('VA_OPT', 'cpp',
2 | version : '0.1'
3 | )
4 |
5 | VA_OPT_dep = declare_dependency(
6 | include_directories : include_directories('.')
7 | )
8 |
9 | test('test VA_OPT',
10 | executable('test_VA_OPT', 'VA_OPT_TEST.cpp')
11 | )
12 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # **`VA_OPT`**
2 |
3 | ## Emptiness testing for C++ Preprocessor macro arguments
4 |
5 | Copyright © 2019 Will Wray. Distributed under the Boost Software License, V1.0
6 |
7 | ### **Boost Software License** - Version 1.0 - August 17th, 2003
8 |
9 | ```txt
10 | Permission is hereby granted, free of charge, to any person or organization
11 | obtaining a copy of the software and accompanying documentation covered by
12 | this license (the "Software") to use, reproduce, display, distribute,
13 | execute, and transmit the Software, and to prepare derivative works of the
14 | Software, and to permit third-parties to whom the Software is furnished to
15 | do so, all subject to the following:
16 |
17 | The copyright notices in the Software and this entire statement, including
18 | the above license grant, this restriction and the following disclaimer,
19 | must be included in all copies of the Software, in whole or in part, and
20 | all derivative works of the Software, unless such copies or derivative
21 | works are solely in the form of machine-executable object code generated by
22 | a source language processor.
23 |
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
27 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
28 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
29 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 | DEALINGS IN THE SOFTWARE.
31 | ```
32 |
33 | [](https://www.boost.org/LICENSE_1_0.txt)
34 |
35 | Also at [boost.org](http://www.boost.org/LICENSE_1_0.txt) and accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt)
36 |
37 |
38 |
39 | | Linux Travis| Windows Appveyor|
40 | | :---: | :---: |
41 | |gcc-9, clang-7|MSVC 19.21.27702.2|
42 | | [](https://travis-ci.org/willwray/VA_OPT) | [](https://ci.appveyor.com/project/willwray/VA_OPT) |
43 |
44 | ----
45 |
46 | ### **`VA_OPT.hpp`**
47 |
48 | C++ preprocessor utilities for testing emptiness of macro arguments
49 | and for expansion conditional on the emptiness of ARGS.
50 |
51 | 1. For portability
52 | 2. For handy utils that wrap `__VA_OPT__`
53 |
54 | This is mostly a 'polyfill' library providing `__VA_OPT__`alike functionality
55 | where support is not detected (so either not present or not selected).
56 | The utilities are also useful `__VA_OPT__` wrappers in their own right.
57 |
58 |
59 |
60 | __VA_OPT__
details
61 |
62 | ----
63 |
64 | C++20 introduces a special function-like macro `__VA_OPT__`
65 | for use only within a variadic macro's replacement list, in which;
66 | >IF `__VA_ARGS__` is empty
67 | THEN `__VA_OPT__(X...)` 'vanishes' - i.e. expands to nothing -
68 | ELSE it expands to `X...`
69 |
70 | ```c++
71 | #define VARIADIC_MACRO(...) __VA_OPT__(NOT EMPTY)
72 | VARIADIC_MACRO() // Expands to nothing
73 | VARIADIC_MACRO(?) // Expands to NOT EMPTY
74 | ```
75 |
76 | ----
77 |
78 |
79 |
80 | Prior to C++20's `__VA_OPT__` there is no reliable test for empty ARGS.
81 |
82 | Such 'emptiness' testing is so useful that it shouldn't be limited to C++20.
83 | Unfortunately, some C++20 compliant compilers hide the feature behind
84 | a `std=c++2a` flag and there is also no `std` feature-test for `__VA_OPT__`.
85 |
86 | This `VA_OPT` library auto-detects preprocessor support for `__VA_OPT__`
87 | and, if detected, uses it as a robust way to implement:
88 |
89 | * **`IS_EMPTY(...)`**
90 | * 1 if the ... ARGS is empty else 0.
91 |
92 | * **`IFN(...)`**
93 | * IF `...` ARGS are not empty
94 | THEN a trailing 'paren expression' `(X)` is 'deparenthesized' to `X`
95 | ELSE the trailing paren expression `(X)` is consumed.
96 |
97 | `IFN(ARGS)` is like `__VA_OPT__` but with explicit (ARGS)
98 | in place of an implicit `__VA_ARGS__` check.
99 |
100 | * **`IFE(...)`**
101 | * IF `...` ARGS is empty
102 | THEN expand trailing 'paren expression' `(X)` to `X`
103 | ELSE consume the trailing paren expression.
104 |
105 | * **`IFNE(...)(N,E...)`**
106 | * IF `...` ARGS are not empty
107 | THEN expands to `N`
108 | ELSE expands to `E...`
109 |
110 | If `__VA_OPT__` support is **NOT** detected then the above macros
111 | are provided using a fallback method (with some failing edge cases).
112 |
113 | ## Platform note:
114 |
115 | Current Sept 2019 `__VA_OPT__` support:
116 |
117 | * Clang 6 introduced it, enabled with `-std=c++2a`
118 | (don't know how best to enable it with earlier `-std`)
119 | * GCC 8 has partial support (no `#__VA_OPT__`)
120 | it is enabled by default without `-std=++2a` but,
121 | if an earlier `-std` flag is supplied along with `-pedantic`
122 | then it warns "`__VA_OPT__` is not available until C++2a"
123 | (don't know how to silence that without dropping `-pedantic`).
124 | * MSVC's experimental preprocessor is required to compile VA_OPT;
125 | use /experimental:preprocessor flag in vs2017 or vs2019.
126 | There's no `__VA_OPT__` support yet but conformance is improving.
127 | (I do not intend to support non-conforming preprocessors.)
128 |
129 | ## Credits
130 |
131 | Props to pre-pro pioneers, particularly Paul Mensonides.
132 |
133 | The fallback techniques are taken from BOOST libraries:
134 | www.boost.org/doc/libs/1_71_0/libs/vmd
135 | www.boost.org/doc/libs/1_71_0/libs/preprocessor
136 | The 'emptiness' methods are adapted from BOOST_VMD_IS_EMPTY
137 | dependent on BOOST Preprocessor's BOOST_PP_IS_BEGIN_PARENS
138 | (adapted and exposed here as undocumented `IBP` 'Is Begin Parens'):
139 |
140 | `__VA_OPT__` feature detection is adapted from
141 | https://stackoverflow.com/a/48045656/7443483
142 |
--------------------------------------------------------------------------------