├── .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 | [![License](https://img.shields.io/badge/license-boost%201.0-blue.svg)](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 | | [![Build Status](https://travis-ci.org/willwray/VA_OPT.svg?branch=master)](https://travis-ci.org/willwray/VA_OPT) | [![Build status](https://ci.appveyor.com/api/projects/status/oy5q387u5c9pt0rx?svg=true)](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 | --------------------------------------------------------------------------------