├── .gitignore ├── test.sh ├── CMakeLists.txt ├── run_tests.sh ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── Jamroot.jam ├── appveyor.yml ├── .travis.yml ├── test_fully_visitable.cpp ├── generate_pp_map.py ├── include └── visit_struct │ ├── visit_struct_boost_hana.hpp │ ├── visit_struct_boost_fusion.hpp │ └── visit_struct_intrusive.hpp ├── test_visit_struct_intrusive.cpp ├── test_visit_struct_boost_hana.cpp ├── test_visit_struct_boost_fusion.cpp ├── test_visit_struct.cpp ├── IMPLEMENTATION_NOTES.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | *.o 3 | *.gch 4 | /bin 5 | /stage 6 | /find_and_replace.sh 7 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf bin 6 | rm -rf stage 7 | ./run_tests.sh $@ 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 2 | 3 | project(visit_struct) 4 | 5 | set(CMAKE_BUILD_TYPE Release) 6 | 7 | file(GLOB headers ${PROJECT_SOURCE_DIR}/include/visit_struct/*.hpp) 8 | 9 | install(FILES ${headers} DESTINATION include/visit_struct) 10 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if hash b2 2>/dev/null; then 6 | b2 "$@" 7 | elif hash bjam 2>/dev/null; then 8 | bjam "$@" 9 | else 10 | echo >&2 "Require b2 or bjam but it was not found. Aborting." 11 | exit 1 12 | fi 13 | 14 | for file in stage/* 15 | do 16 | echo ${file} "..." 17 | if hash gdb 2>/dev/null; then 18 | gdb -return-child-result -batch -ex "run" -ex "thread apply all bt" -ex "quit" --args ./${file} 19 | else 20 | ./${file} 21 | fi 22 | done 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | # See docu here: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-including-and-excluding-branches 7 | # it seems pretty similar to .gitignore 8 | paths: 9 | - '!**.md' 10 | - '**' 11 | pull_request: 12 | paths: 13 | - '!**.md' 14 | - '**' 15 | 16 | jobs: 17 | # builds and tests c++ code 18 | build-and-test: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: install boost, gcc, clang 23 | run: sudo apt-get install libboost1.83-dev libboost1.83-tools-dev clang gcc 24 | - name: print versions 25 | run: gcc --version && clang --version 26 | - name: cat test 27 | run: ls -al && cat run_tests.sh 28 | - name: test gcc 29 | run: ./run_tests.sh --toolset=gcc 30 | - name: test clang 31 | run: ./run_tests.sh --toolset=clang 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Jamroot.jam: -------------------------------------------------------------------------------- 1 | project : default-build off debug ; 2 | 3 | INSTALL_LOC = stage/ ; 4 | 5 | ### Boost directory 6 | 7 | import os ; 8 | 9 | local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; 10 | if $(BOOST_ROOT) { 11 | # echo "BOOST_ROOT = '" $(BOOST_ROOT) "'" ; 12 | BOOST_INCLUDE_DIR = $(BOOST_ROOT) ; 13 | } else { 14 | echo "BOOST_ROOT variable not set!" ; 15 | BOOST_INCLUDE_DIR = "/usr/include" ; 16 | } 17 | 18 | # Try to find boost/version.hpp 19 | 20 | if [ GLOB $(BOOST_INCLUDE_DIR)/boost/ : version.hpp ] { 21 | echo "Using BOOST_INCLUDE_DIR = '" $(BOOST_INCLUDE_DIR) "'" ; 22 | } else { 23 | echo "Could not find boost, skipping boost tests" ; 24 | BOOST_INCLUDE_DIR = ; 25 | } 26 | 27 | # Check TEST_HANA env variable 28 | 29 | local TEST_CXX14 = [ os.environ TEST_CXX14 ] ; 30 | local SKIP_INTRUSIVE = [ os.environ SKIP_INTRUSIVE ] ; 31 | 32 | ### Setup visit_struct target 33 | 34 | alias visit_struct : : : : include/ ; 35 | 36 | ### Setup boost includes 37 | 38 | alias boost : : : : $(BOOST_INCLUDE_DIR) ; 39 | 40 | ### Build tests 41 | 42 | # TODO: Work around this on windows 43 | GNU_FLAGS = "-Wall -Werror -Wextra -pedantic -std=c++11" ; 44 | FLAGS = gcc:$(GNU_FLAGS) clang:$(GNU_FLAGS) msvc:"off" ; 45 | 46 | exe test_visit_struct : test_visit_struct.cpp visit_struct : $(FLAGS) ; 47 | exe test_visit_struct_boost_fusion : test_visit_struct_boost_fusion.cpp visit_struct boost : $(FLAGS) ; 48 | 49 | install install-bin : test_visit_struct test_visit_struct_boost_fusion : $(INSTALL_LOC) ; 50 | 51 | if $(SKIP_INTRUSIVE) { 52 | echo "Skipping intrusive syntax test" ; 53 | } else { 54 | exe test_visit_struct_intrusive : test_visit_struct_intrusive.cpp visit_struct : $(FLAGS) ; 55 | install install-intrusive : test_visit_struct_intrusive : $(INSTALL_LOC) ; 56 | } 57 | 58 | if $(TEST_CXX14) { 59 | 60 | GNU_FLAGS = "-Wall -Werror -Wextra -pedantic -std=c++14" ; 61 | FLAGS = gcc:$(GNU_FLAGS) clang:$(GNU_FLAGS) msvc:"off" ; 62 | exe test_visit_struct_boost_hana : test_visit_struct_boost_hana.cpp visit_struct boost : $(FLAGS) ; 63 | exe test_fully_visitable : test_fully_visitable.cpp visit_struct : $(FLAGS) ; 64 | 65 | install install-hana : test_fully_visitable test_visit_struct_boost_hana : $(INSTALL_LOC) ; 66 | } 67 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # scripts that are called at very beginning, before repo cloning 2 | init: 3 | - git config --global core.autocrlf input 4 | 5 | # clone directory 6 | clone_folder: C:\projects\visit_struct 7 | 8 | # fetch repository as zip archive 9 | shallow_clone: true # default is "false" 10 | 11 | build: off 12 | 13 | environment: 14 | BOOST_VER: "boost_1_61_0" 15 | BOOST_URL: "https://sourceforge.net/projects/boost/files/boost/1.61.0/boost_1_61_0.zip" 16 | 17 | matrix: 18 | - os: Visual Studio 2017 19 | # TEST_CXX14: true 20 | # ^ CXX14 Tests not passing yet as of July 11 2017 21 | # But, the headers compile even with CXX14 constexpr enabled 22 | # What is the minimum MSVC version that allows tests to pass too? 23 | - os: Visual Studio 2015 24 | - os: Visual Studio 2013 25 | SKIP_INTRUSIVE: true 26 | 27 | cache: 28 | - C:\projects\boost_1_61_0.zip 29 | 30 | install: 31 | - ps: >- 32 | If($true) { 33 | Add-Type -assembly "system.io.compression.filesystem" 34 | 35 | $boost_path = "C:\projects\" + $env:BOOST_VER 36 | $boost_zip = $boost_path + ".zip" 37 | 38 | If(Test-Path $boost_zip) { 39 | Write-Host "Found cached boost" 40 | } Else { 41 | Write-Host "Downloading boost..." 42 | (new-object net.webclient).DownloadFile($env:BOOST_URL, $boost_zip) 43 | } 44 | 45 | Write-Host "Extracting boost..." 46 | [io.compression.zipfile]::ExtractToDirectory($boost_zip, "C:\projects\") 47 | 48 | Set-Location $boost_path 49 | Write-Host "Bootstrapping..." 50 | & ".\bootstrap.bat" 51 | 52 | $env:BOOST_ROOT = $boost_path 53 | $env:Path += ";" + $boost_path + ";" 54 | 55 | Set-Location "C:\projects\visit_struct" 56 | Write-Host "Building test executables" 57 | & b2 --toolset=msvc 58 | } 59 | 60 | test_script: 61 | - ps: >- 62 | If($true) { 63 | $env:Path += ";C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\;" 64 | $env:Path += ";C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\;" 65 | $SymPath = "C:\projects\visit_struct\test\stage;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols" 66 | 67 | cmd /r mkdir c:\symbols 68 | 69 | Write-Host "Running test executables.." 70 | $files = Get-ChildItem C:\projects\visit_struct\stage\*.exe 71 | ForEach($file in $files) { 72 | Write-Host "" 73 | Write-Host $file.fullName " ... " 74 | cdb -y $SymPath -c "|;g;kv;q" -o $file.fullName 75 | Write-Host "Exit code: " $lastExitCode 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | 5 | cache: 6 | directories: 7 | - ${TRAVIS_BUILD_DIR}/deps 8 | 9 | matrix: 10 | include: 11 | - compiler: gcc 12 | addons: 13 | apt: 14 | sources: 15 | - ubuntu-toolchain-r-test 16 | packages: 17 | - g++-4.9 18 | env: COMPILER=g++-4.9 BOOST_VERSION=1.61.0 19 | - compiler: gcc 20 | addons: 21 | apt: 22 | sources: 23 | - ubuntu-toolchain-r-test 24 | packages: 25 | - g++-5 26 | env: COMPILER=g++-5 BOOST_VERSION=1.61.0 27 | - compiler: clang 28 | addons: 29 | apt: 30 | sources: 31 | - ubuntu-toolchain-r-test 32 | packages: 33 | - g++-4.9 34 | env: COMPILER=clang++ BOOST_VERSION=1.61.0 35 | - compiler: gcc 36 | addons: 37 | apt: 38 | sources: 39 | - ubuntu-toolchain-r-test 40 | packages: 41 | - g++-6 42 | env: COMPILER=g++-6 TEST_CXX14=true BOOST_VERSION=1.61.0 43 | 44 | before_install: 45 | - sudo apt-get update -qq 46 | - sudo apt-get install -qq gdb libboost-dev 47 | 48 | install: 49 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" 50 | - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} 51 | 52 | - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi 53 | - if [[ "${BOOST_VERSION}" == "default" ]]; then BOOST_VERSION=1.60.0; fi; 54 | 55 | - if [[ ! -d ${DEPS_DIR}/boost ]]; then 56 | mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}; 57 | 58 | if [[ "${BOOST_VERSION}" == "1.60.0" ]]; then BOOST_URL="http://sourceforge.net/projects/boost/files/boost/1.60.0/boost_1_60_0.tar.gz"; fi; 59 | if [[ "${BOOST_VERSION}" == "1.61.0" ]]; then BOOST_URL="http://sourceforge.net/projects/boost/files/boost/1.61.0/boost_1_61_0.tar.gz"; fi; 60 | if [[ "${BOOST_URL}" != "" ]]; then mkdir boost && travis_retry wget -O - ${BOOST_URL} | tar --strip-components=1 -xz -C boost; fi; 61 | 62 | cd boost/tools/build && ./bootstrap.sh && ./b2 install --prefix=${DEPS_DIR}/b2; 63 | cd ${TRAVIS_BUILD_DIR}; 64 | fi 65 | 66 | - export PATH=${DEPS_DIR}/b2/bin:${PATH} 67 | - export BOOST_ROOT=${DEPS_DIR}/boost 68 | 69 | - cd ${DEPS_DIR}/boost 70 | - pwd 71 | - ls 72 | - ls boost/mpl/range_c.hpp 73 | 74 | - cd ${TRAVIS_BUILD_DIR} 75 | - pwd 76 | - ls 77 | 78 | script: 79 | - echo ${COMPILER} 80 | - echo ${CXX} 81 | - $CXX --version 82 | - |- 83 | JAM="using clang : : ${CXX} ;" 84 | 85 | - touch project-config.jam 86 | - echo "${JAM}" > project-config.jam 87 | 88 | - echo "With Boost ${BOOST_VERSION}" 89 | - ./test.sh --toolset=clang 90 | -------------------------------------------------------------------------------- /test_fully_visitable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /*** 8 | * Code in `namespace ext` implements a type trait, "is_fully_visitable". 9 | * 10 | * This is a compile-time test which attempts to verify that a visitable structure 11 | * has all of its fields registered. 12 | * 13 | * It does this by checking the sizeof the structure vs what the size should be 14 | * if the visitable fields are all of the fields. 15 | * 16 | * This is an illustration of how visit_struct can be used to do 17 | * some nontrivial compile-time metaprogramming. 18 | * 19 | * (Old versions required C++14, for constexpr visitation, but using 20 | * visit_struct::type_at, we can now do it with only C++11.) 21 | * 22 | * Potentially, you could use it to catch bugs at compile-time which occur 23 | * if e.g. a member is added to a struct but the programmer forgets to add it 24 | * to the VISITABLE_STRUCT macro also. 25 | * 26 | * Tested against gcc 4.8, 4.9, 5.4, 6.2 and clang 3.5, 3.8 27 | */ 28 | 29 | namespace ext { 30 | 31 | // C++11 replacement for std::index_sequence 32 | template 33 | struct seq {}; 34 | 35 | // Concatenate sequences 36 | template 37 | struct cat_s; 38 | 39 | template 40 | struct cat_s, seq> { 41 | using type = seq; 42 | }; 43 | 44 | template 45 | using cat = typename cat_s::type; 46 | 47 | // Count 48 | 49 | template 50 | struct count_s { 51 | using type = cat::type, seq>; 52 | }; 53 | 54 | template <> 55 | struct count_s<0> { 56 | using type = seq<>; 57 | }; 58 | 59 | template 60 | using count = typename count_s::type; 61 | 62 | 63 | // It turns out it is implementation-defined whether `std::tuple` lists its members 64 | // in order specified or in reverse order, and some standard libraries do it differently. 65 | // So we have to make it up a bit here for our intended application, and we don't need 66 | // full tuple interface anyways, we only need the size to be correct. 67 | // This is based on libc++ implementation. 68 | 69 | // Note: Extra std::size_t parameter is here to avoid "duplicate base type is invalid" error 70 | template 71 | struct mock_tuple_leaf { T t; }; 72 | 73 | template 74 | struct mock_tuple; 75 | 76 | template 77 | struct mock_tuple, Ts...> : mock_tuple_leaf ... {}; 78 | 79 | template 80 | using mock_tuple_t = mock_tuple, Ts...>; 81 | 82 | // Build mock from a visitable structure 83 | // Does the work 84 | 85 | template 86 | struct mock_maker { 87 | template 88 | struct helper; 89 | 90 | template 91 | struct helper> { 92 | using type = mock_tuple_t ...>; 93 | }; 94 | 95 | using type = typename helper()>>::type; 96 | static constexpr std::size_t size = sizeof(type); 97 | }; 98 | 99 | // Final result 100 | 101 | template 102 | constexpr bool is_fully_visitable() { 103 | return sizeof(T) == mock_maker::size; 104 | } 105 | 106 | } // end namespace ext 107 | 108 | 109 | 110 | 111 | // Tests 112 | 113 | struct foo { 114 | int a; 115 | int b; 116 | int c; 117 | }; 118 | 119 | VISITABLE_STRUCT(foo, a, b, c); 120 | 121 | static_assert(sizeof(foo) == 3 * sizeof(int), ""); 122 | static_assert(visit_struct::field_count() == 3, ""); 123 | static_assert(ext::mock_maker::size == 3 * sizeof(int), ""); 124 | 125 | static_assert(ext::is_fully_visitable(), ""); 126 | 127 | struct bar { 128 | int a; 129 | int b; 130 | int c; 131 | }; 132 | 133 | VISITABLE_STRUCT(bar, a, b); 134 | 135 | static_assert(!ext::is_fully_visitable(), ""); 136 | 137 | 138 | struct baz { 139 | int a; 140 | char b[7]; 141 | short c; 142 | char d[157]; 143 | }; 144 | 145 | VISITABLE_STRUCT(baz, a, b, c, d); 146 | 147 | static_assert(ext::is_fully_visitable(), ""); 148 | 149 | int main () {} 150 | -------------------------------------------------------------------------------- /generate_pp_map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright © 2016 Chris Beck 5 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | # file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | # Description: 9 | # Takes, optionally, a number, the default is 69. Generates a C preprocessor macro 10 | # `VISIT_STRUCT_PP_MAP` appropriate for use in visit_struct. 11 | # 12 | # The output of this program should be captured and copy-pasted into into the 13 | # `visit_struct.hpp` file if you wish to change the built-in limit on the number 14 | # of visited members. 15 | 16 | import sys 17 | import argparse 18 | 19 | argparser = argparse.ArgumentParser(description='Generate a PP_MAP macro for visit_struct.') 20 | argparser.add_argument('--limit', type=int, help='Numerical limit on number of visited members for the resulting macro. Default is 69.') 21 | args = argparser.parse_args() 22 | 23 | if args.limit is None: 24 | args.limit = 69 25 | 26 | def write(s): 27 | sys.stdout.write(str(s)) 28 | 29 | def writeln(s = ""): 30 | sys.stdout.write(str(s)) 31 | sys.stdout.write('\n') 32 | 33 | # This macro takes an arbitrary number of arguments, at least N = args.limit, and 34 | # returns the N'th one. Looks like this: 35 | # 36 | # define VISIT_STRUCT_PP_ARG_N( \ 37 | # _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 38 | # _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ 39 | # _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ 40 | # _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ 41 | # _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ 42 | # _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ 43 | # _61,_62,_63,_64,_65,_66,_67,_68,_69,N,...) N 44 | 45 | def write_pp_arg_n(): 46 | write("#define VISIT_STRUCT_PP_ARG_N( ") 47 | 48 | x = 0 49 | n = 1 50 | while n <= args.limit: 51 | if x == 0: 52 | write("\\\n ") 53 | x = 10 54 | x = x - 1 55 | 56 | write(" _") 57 | write(n) 58 | write(",") 59 | n = n + 1 60 | 61 | writeln(" N, ...) N") 62 | 63 | # This macro returns the number of arguments, at most args.limit - 1, that it was passed. 64 | # Looks like this. 65 | # define VISIT_STRUCT_PP_NARG(...) VISIT_STRUCT_EXPAND(VISIT_STRUCT_PP_ARG_N(__VA_ARGS__, \ 66 | # 69,68,67,66,65,64,63,62,61,60, \ 67 | # 59,58,57,56,55,54,53,52,51,50, \ 68 | # 49,48,47,46,45,44,43,42,41,40, \ 69 | # 39,38,37,36,35,34,33,32,31,30, \ 70 | # 29,28,27,26,25,24,23,22,21,20, \ 71 | # 19,18,17,16,15,14,13,12,11,10, \ 72 | # 9,8,7,6,5,4,3,2,1,0)) 73 | 74 | def write_pp_narg(): 75 | write("#define VISIT_STRUCT_PP_NARG(...) VISIT_STRUCT_EXPAND(VISIT_STRUCT_PP_ARG_N(__VA_ARGS__,") 76 | 77 | x = 0 78 | n = args.limit 79 | while n > 0: 80 | if x == 0: 81 | write(" \\\n ") 82 | x = 10 83 | x = x - 1 84 | 85 | write(" ") 86 | write(n) 87 | write(",") 88 | n = n - 1 89 | 90 | writeln(" 0))") 91 | 92 | # This macro applies the first argument, a macro, to each of the n arguments that follow. 93 | # Looks like this. 94 | # define VISIT_STRUCT_APPLYF3(f,_1,_2,_3) f(_1) f(_2) f(_3) 95 | def write_pp_apply_f(n): 96 | write("#define VISIT_STRUCT_APPLYF") 97 | write(n) 98 | write("(f") 99 | 100 | for x in range(1, n + 1): 101 | write(",_") 102 | write(x) 103 | 104 | write(")") 105 | 106 | for x in range(1, n + 1): 107 | write(" f(_") 108 | write(x) 109 | write(")") 110 | 111 | writeln() 112 | 113 | # 114 | # Main routine 115 | # 116 | 117 | writeln("/*** Generated code ***/") 118 | writeln() 119 | writeln("static VISIT_STRUCT_CONSTEXPR const int max_visitable_members = " + str(args.limit) + ";") 120 | 121 | writeln() 122 | writeln("#define VISIT_STRUCT_EXPAND(x) x") 123 | write_pp_arg_n() 124 | write_pp_narg() 125 | 126 | writeln() 127 | writeln("/* need extra level to force extra eval */") 128 | writeln("#define VISIT_STRUCT_CONCAT_(a,b) a ## b") 129 | writeln("#define VISIT_STRUCT_CONCAT(a,b) VISIT_STRUCT_CONCAT_(a,b)") 130 | writeln() 131 | 132 | for n in range(args.limit + 1): 133 | write_pp_apply_f(n) 134 | 135 | writeln() 136 | writeln("#define VISIT_STRUCT_APPLY_F_(M, ...) VISIT_STRUCT_EXPAND(M(__VA_ARGS__))") 137 | writeln("#define VISIT_STRUCT_PP_MAP(f, ...) VISIT_STRUCT_EXPAND(VISIT_STRUCT_APPLY_F_(VISIT_STRUCT_CONCAT(VISIT_STRUCT_APPLYF, VISIT_STRUCT_PP_NARG(__VA_ARGS__)), f, __VA_ARGS__))") 138 | 139 | writeln() 140 | writeln("/*** End generated code ***/") 141 | -------------------------------------------------------------------------------- /include/visit_struct/visit_struct_boost_hana.hpp: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2015 - 2018 Christopher Beck 2 | 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef VISIT_STRUCT_BOOST_HANA_HPP_INCLUDED 7 | #define VISIT_STRUCT_BOOST_HANA_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace visit_struct { 15 | 16 | namespace traits { 17 | 18 | // Accessor value-type 19 | // An accessor is a function object which encapsulates a pointer to member 20 | // It is generally an overloaded function object: 21 | // If the pointer type is T S::*, then the function object has the following overloads: 22 | // S & -> T & 23 | // const S & -> const T & 24 | // S && -> T && 25 | // 26 | // When using boost::hana, a struct is defined *only* in terms of a sequence of names and 27 | // accessors. There is no built-in functionality to obtain the actual member-type T. 28 | // And the accessor is not required to provide a typedef exposing it. 29 | // 30 | // We may attempt to infer it, however, by a decltype expression where we evaluate 31 | // the accessor. Then we can remove const and reference. 32 | // 33 | // However, what if the struct actually contains const members, or reference members? 34 | // If the member type is actually T & and not T, then the accessor yields: 35 | // 36 | // S & -> T & 37 | // const S & -> T & 38 | // S && -> T & 39 | // 40 | // In other words, if the member type is a reference type, then the const and ref qualifiers of input 41 | // don't matter. 42 | // 43 | // If the member type is actually const T, then the accessor yields: 44 | // 45 | // S & -> const T & 46 | // const S & -> const T & 47 | // S && -> const T && 48 | // 49 | // Therefore a viable way to infer the value type from the accessor is 50 | // 1. Check the output types of accessor(S&) and accessor(S&&) 51 | // 2. If they are the same, the value type is that type 52 | // 3. If they are different, then remove reference from one of them to obtain the value type. (It shouldn't matter which one.) 53 | // 54 | // It's not clear what we should do if, after removing reference from the two test types, we don't get the same type. 55 | // In current versions we don't check if that's actually the case. It would be reasonable to static_assert that it doesn't happen I think. 56 | // 57 | // Also it's worth noting that hana doesn't apparently support structs with reference members, and test code fails compilation 58 | // with "error: cannot create pointer to reference member" in gcc-6. So this is probably overkill. 59 | 60 | template 61 | struct accessor_value_type { 62 | using test1 = decltype(std::declval()(std::declval())); 63 | using test2 = decltype(std::declval()(std::declval())); 64 | 65 | using type = typename std::conditional::value, 66 | test1, 67 | typename std::remove_reference::type>::type; 68 | }; 69 | 70 | template 71 | using accessor_value_t = typename accessor_value_type::type; 72 | 73 | // Hana bindings start here 74 | 75 | namespace hana = boost::hana; 76 | 77 | template 78 | struct visitable::value>::type> 79 | { 80 | static constexpr size_t field_count = decltype(hana::length(hana::accessors()))::value; 81 | 82 | // Note: U should be qualified S (const and references) S, interface should ensure that 83 | template 84 | static constexpr void apply(V && v, U && u) { 85 | hana::for_each(hana::accessors(), [&v, &u](auto pair) { 86 | std::forward(v)(hana::to(hana::first(pair)), hana::second(pair)(std::forward(u))); 87 | }); 88 | } 89 | 90 | template 91 | static constexpr void apply(V && v, U1 && u1, U2 && u2) { 92 | hana::for_each(hana::accessors(), [&v, &u1, &u2](auto pair) { 93 | std::forward(v)(hana::to(hana::first(pair)), 94 | hana::second(pair)(std::forward(u1)), 95 | hana::second(pair)(std::forward(u2))); 96 | }); 97 | } 98 | 99 | template 100 | static constexpr void visit_types(V && v) { 101 | hana::for_each(hana::accessors(), [&v](auto pair) { 102 | using member_type = accessor_value_t; 103 | std::forward(v)(hana::to(hana::first(pair)), 104 | visit_struct::type_c{}); 105 | }); 106 | } 107 | 108 | template 109 | static constexpr void visit_accessors(V && v) { 110 | hana::for_each(hana::accessors(), [&v](auto pair) { 111 | std::forward(v)(hana::to(hana::first(pair)), 112 | hana::second(pair)); 113 | }); 114 | } 115 | 116 | template 117 | static constexpr auto get_value(std::integral_constant, T && t) -> 118 | decltype(hana::second(hana::at(hana::accessors(), hana::size_c)) (std::forward(t))) { 119 | return hana::second(hana::at(hana::accessors(), hana::size_c)) (std::forward(t)); 120 | } 121 | 122 | template 123 | static constexpr auto get_name(std::integral_constant) -> const char * { 124 | return hana::to(hana::first(hana::at(hana::accessors(), hana::size_c))); 125 | } 126 | 127 | template 128 | static constexpr auto get_accessor(std::integral_constant) -> 129 | decltype(hana::second(hana::at(hana::accessors(), hana::size_c))) { 130 | return hana::second(hana::at(hana::accessors(), hana::size_c)); 131 | } 132 | 133 | template 134 | static auto type_at(std::integral_constant) -> 135 | visit_struct::type_c(), hana::size_c))), S>>; 136 | 137 | static constexpr bool value = true; 138 | }; 139 | 140 | } // end namespace traits 141 | 142 | } // end namespace visit_struct 143 | 144 | #endif // VISIT_STRUCT_BOOST_HANA_HPP_INCLUDED 145 | -------------------------------------------------------------------------------- /include/visit_struct/visit_struct_boost_fusion.hpp: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2015 - 2018 Christopher Beck 2 | 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef VISIT_STRUCT_BOOST_FUSION_HPP_INCLUDED 7 | #define VISIT_STRUCT_BOOST_FUSION_HPP_INCLUDED 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace visit_struct { 18 | 19 | namespace traits { 20 | 21 | namespace fusion = boost::fusion; 22 | namespace mpl = boost::mpl; 23 | 24 | template 25 | struct visitable::type, 28 | fusion::fusion_sequence_tag 29 | >::value 30 | >::type> 31 | { 32 | 33 | private: 34 | // Get names for members of fusion structure S 35 | // Cage the unwieldy fusion syntax 36 | template 37 | static VISIT_STRUCT_CONSTEXPR auto field_name() -> decltype(fusion::extension::struct_member_name::call()) { 38 | return fusion::extension::struct_member_name::call(); 39 | } 40 | 41 | // Accessor type for fusion structure S 42 | template 43 | struct accessor { 44 | VISIT_STRUCT_CONSTEXPR auto operator()(S & s) const -> 45 | decltype(fusion::at_c(s)) { 46 | return fusion::at_c(s); 47 | } 48 | 49 | VISIT_STRUCT_CONSTEXPR auto operator()(const S & s) const -> 50 | decltype(fusion::at_c(s)) { 51 | return fusion::at_c(s); 52 | } 53 | 54 | VISIT_STRUCT_CONSTEXPR auto operator()(S && s) const -> 55 | decltype(std::move(fusion::at_c(s))) { 56 | return std::move(fusion::at_c(s)); 57 | } 58 | }; 59 | 60 | // T is a qualified S 61 | // V should be a forwarding reference here, we should not be copying visitors 62 | template 63 | struct fusion_visitor { 64 | V visitor; 65 | T struct_instance; 66 | 67 | explicit fusion_visitor(V v, T t) : visitor(std::forward(v)), struct_instance(std::forward(t)) {} 68 | 69 | template 70 | VISIT_STRUCT_CXX14_CONSTEXPR void operator()(Index) const { 71 | using accessor_t = accessor; 72 | std::forward(visitor)(field_name(), accessor_t()(std::forward(struct_instance))); 73 | } 74 | }; 75 | 76 | template 77 | struct fusion_visitor_pair { 78 | V visitor; 79 | T1 instance_1; 80 | T2 instance_2; 81 | 82 | explicit fusion_visitor_pair(V v, T1 t1, T2 t2) 83 | : visitor(std::forward(v)) 84 | , instance_1(std::forward(t1)) 85 | , instance_2(std::forward(t2)) 86 | {} 87 | 88 | template 89 | VISIT_STRUCT_CXX14_CONSTEXPR void operator()(Index) const { 90 | accessor a; 91 | std::forward(visitor)(field_name(), a(std::forward(instance_1)), a(std::forward(instance_2))); 92 | } 93 | }; 94 | 95 | template 96 | struct fusion_visitor_types { 97 | V visitor; 98 | 99 | explicit fusion_visitor_types(V v) : visitor(std::forward(v)) {} 100 | 101 | template 102 | VISIT_STRUCT_CXX14_CONSTEXPR void operator()(Index) const { 103 | using current_type = typename fusion::result_of::value_at::type; 104 | std::forward(visitor)(field_name(), visit_struct::type_c{}); 105 | } 106 | }; 107 | 108 | template 109 | struct fusion_visitor_accessors { 110 | V visitor; 111 | 112 | explicit fusion_visitor_accessors(V v) : visitor(std::forward(v)) {} 113 | 114 | template 115 | VISIT_STRUCT_CXX14_CONSTEXPR void operator()(Index) const { 116 | using accessor_t = accessor; 117 | std::forward(visitor)(field_name(), accessor_t()); 118 | } 119 | }; 120 | 121 | public: 122 | static VISIT_STRUCT_CONSTEXPR const size_t field_count = fusion::result_of::size::value; 123 | 124 | // T should be a qualified S 125 | template 126 | static void apply(V && v, T && t) { 127 | using Indices = mpl::range_c::value >; 128 | using fv_t = fusion_visitor(v)), decltype(std::forward(t))>; 129 | fv_t fv{std::forward(v), std::forward(t)}; 130 | fusion::for_each(Indices(), std::move(fv)); 131 | } 132 | 133 | template 134 | static void apply(V && v, T1 && t1, T2 && t2) { 135 | using Indices = mpl::range_c::value >; 136 | using fv_t = fusion_visitor_pair(v)), decltype(std::forward(t1)), decltype(std::forward(t2))>; 137 | fv_t fv{std::forward(v), std::forward(t1), std::forward(t2)}; 138 | fusion::for_each(Indices(), fv); 139 | } 140 | 141 | template 142 | static void visit_types(V && v) { 143 | using Indices = mpl::range_c::value >; 144 | using fv_t = fusion_visitor_types(v))>; 145 | fv_t fv{std::forward(v)}; 146 | fusion::for_each(Indices(), fv); 147 | } 148 | 149 | template 150 | static void visit_accessors(V && v) { 151 | using Indices = mpl::range_c::value >; 152 | using fv_t = fusion_visitor_accessors(v))>; 153 | fv_t fv{std::forward(v)}; 154 | fusion::for_each(Indices(), fv); 155 | } 156 | 157 | // T should be qualified S 158 | template 159 | static VISIT_STRUCT_CONSTEXPR auto get_value(std::integral_constant, T && t) 160 | -> decltype(accessor()(std::forward(t))) 161 | { 162 | return accessor()(std::forward(t)); 163 | } 164 | 165 | template 166 | static VISIT_STRUCT_CONSTEXPR auto get_name(std::integral_constant) 167 | -> decltype(field_name()) 168 | { 169 | return field_name(); 170 | } 171 | 172 | template 173 | static VISIT_STRUCT_CONSTEXPR auto get_accessor(std::integral_constant) -> 174 | accessor 175 | { 176 | return {}; 177 | } 178 | 179 | template 180 | static auto type_at(std::integral_constant) -> 181 | visit_struct::type_c::type>; 182 | 183 | static VISIT_STRUCT_CONSTEXPR const bool value = true; 184 | }; 185 | 186 | } // end namespace traits 187 | 188 | } // end namespace visit_struct 189 | 190 | #endif // VISIT_STRUCT_BOOST_FUSION_HPP_INCLUDED 191 | -------------------------------------------------------------------------------- /test_visit_struct_intrusive.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace test { 9 | 10 | struct foo { 11 | BEGIN_VISITABLES(foo); 12 | VISITABLE(bool, b); 13 | VISITABLE(int, i); 14 | VISITABLE(float, f); 15 | END_VISITABLES; 16 | }; 17 | 18 | } // end namespace test 19 | 20 | static_assert(visit_struct::field_count() == 3, ""); 21 | 22 | struct test_visitor_one { 23 | std::vector names; 24 | std::vector values; 25 | 26 | template 27 | void operator()(const char * name, T v) { 28 | names.emplace_back(name); 29 | values.emplace_back(v); 30 | } 31 | }; 32 | 33 | using spair = std::pair; 34 | 35 | struct test_visitor_ptr { 36 | std::vector result; 37 | 38 | template 39 | void operator()(const char* name, int C::*) { 40 | result.emplace_back(spair{std::string{name}, "int"}); 41 | } 42 | 43 | template 44 | void operator()(const char* name, float C::*) { 45 | result.emplace_back(spair{std::string{name}, "float"}); 46 | } 47 | 48 | template 49 | void operator()(const char* name, bool C::*) { 50 | result.emplace_back(spair{std::string{name}, "bool"}); 51 | } 52 | }; 53 | 54 | struct test_visitor_type { 55 | std::vector result; 56 | 57 | void operator()(const char* name, visit_struct::type_c) { 58 | result.emplace_back(spair{std::string{name}, "int"}); 59 | } 60 | 61 | void operator()(const char* name, visit_struct::type_c) { 62 | result.emplace_back(spair{std::string{name}, "float"}); 63 | } 64 | 65 | void operator()(const char* name, visit_struct::type_c) { 66 | result.emplace_back(spair{std::string{name}, "bool"}); 67 | } 68 | }; 69 | 70 | 71 | template 72 | struct test_visitor_acc { 73 | test_visitor_type internal; 74 | 75 | template 76 | void operator()(const char* name, A && a) { 77 | using result_t = visit_struct::traits::clean_t()))>; 78 | internal(name, visit_struct::type_c{}); 79 | } 80 | }; 81 | 82 | 83 | 84 | struct test_visitor_three { 85 | int result = 0; 86 | 87 | template 88 | void operator()(const char *, T &&) {} 89 | 90 | void operator()(const char *, int &) { 91 | result = 1; 92 | } 93 | 94 | void operator()(const char *, const int &) { 95 | result = 2; 96 | } 97 | 98 | void operator()(const char *, int &&) { 99 | result = 3; 100 | } 101 | 102 | // Make it non-copyable and non-moveable, apply visitor should still work. 103 | test_visitor_three() = default; 104 | test_visitor_three(const test_visitor_three &) = delete; 105 | test_visitor_three(test_visitor_three &&) = delete; 106 | }; 107 | 108 | 109 | // Test binary visitation 110 | struct lex_compare_visitor { 111 | int result = 0; 112 | 113 | template 114 | void operator()(const char *, const T & t1, const T & t2) { 115 | if (!result) { 116 | if (t1 < t2) { result = -1; } 117 | else if (t1 > t2) { result = 1; } 118 | } 119 | } 120 | }; 121 | 122 | template 123 | int struct_cmp(const T & t1, const T & t2) { 124 | lex_compare_visitor vis; 125 | visit_struct::apply_visitor(vis, t1, t2); 126 | return vis.result; 127 | } 128 | 129 | // debug_print 130 | 131 | struct debug_printer { 132 | template 133 | void operator()(const char * name, const T & t) const { 134 | std::cout << " " << name << ": " << t << std::endl; 135 | } 136 | }; 137 | 138 | template 139 | void debug_print(const T & t) { 140 | std::cout << "{\n"; 141 | visit_struct::apply_visitor(debug_printer{}, t); 142 | std::cout << "}" << std::endl; 143 | } 144 | 145 | // tests 146 | 147 | static_assert(std::is_same(std::declval())), bool &&>::value, ""); 148 | static_assert(std::is_same(std::declval())), int &&>::value, ""); 149 | static_assert(std::is_same(std::declval())), float &&>::value, ""); 150 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 151 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 152 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 153 | static_assert(visit_struct::get_pointer<0, test::foo>() == &test::foo::b, ""); 154 | static_assert(visit_struct::get_pointer<1, test::foo>() == &test::foo::i, ""); 155 | static_assert(visit_struct::get_pointer<2, test::foo>() == &test::foo::f, ""); 156 | static_assert(std::is_same, bool>::value, ""); 157 | static_assert(std::is_same, int>::value, ""); 158 | static_assert(std::is_same, float>::value, ""); 159 | 160 | 161 | int main() { 162 | std::cout << __FILE__ << std::endl; 163 | 164 | { 165 | test::foo s{ true, 5, 7.5f }; 166 | 167 | debug_print(s); 168 | 169 | assert(visit_struct::get<0>(s) == true); 170 | assert(visit_struct::get<1>(s) == 5); 171 | assert(visit_struct::get<2>(s) == 7.5f); 172 | assert(visit_struct::get_name<0>(s) == std::string{"b"}); 173 | assert(visit_struct::get_name<1>(s) == std::string{"i"}); 174 | assert(visit_struct::get_name<2>(s) == std::string{"f"}); 175 | assert(visit_struct::get_accessor<0>(s)(s) == visit_struct::get<0>(s)); 176 | assert(visit_struct::get_accessor<1>(s)(s) == visit_struct::get<1>(s)); 177 | assert(visit_struct::get_accessor<2>(s)(s) == visit_struct::get<2>(s)); 178 | 179 | test_visitor_one vis; 180 | visit_struct::apply_visitor(vis, s); 181 | 182 | assert(vis.names.size() == 3); 183 | assert(vis.values.size() == 3); 184 | assert(vis.names[0] == "b"); 185 | assert(vis.names[1] == "i"); 186 | assert(vis.names[2] == "f"); 187 | assert(vis.values[0] == 1); 188 | assert(vis.values[1] == 5); 189 | assert(vis.values[2] == 7.5); 190 | 191 | s.b = false; 192 | s.i = 19; 193 | s.f = -1.5f; 194 | 195 | visit_struct::for_each(s, vis); 196 | 197 | assert(vis.names.size() == 6); 198 | assert(vis.values.size() == 6); 199 | assert(vis.names[0] == "b"); 200 | assert(vis.names[1] == "i"); 201 | assert(vis.names[2] == "f"); 202 | assert(vis.names[3] == "b"); 203 | assert(vis.names[4] == "i"); 204 | assert(vis.names[5] == "f"); 205 | assert(vis.values[0] == 1); 206 | assert(vis.values[1] == 5); 207 | assert(vis.values[2] == 7.5); 208 | assert(vis.values[3] == 0); 209 | assert(vis.values[4] == 19); 210 | assert(vis.values[5] == -1.5); 211 | 212 | // test get_name 213 | assert(std::string("foo") == visit_struct::get_name(s)); 214 | assert(std::string("foo") == visit_struct::get_name()); 215 | } 216 | 217 | // Test move semantics 218 | { 219 | test::foo s{true, 0, 0}; 220 | 221 | debug_print(s); 222 | 223 | test_visitor_three vis; 224 | 225 | visit_struct::apply_visitor(vis, s); 226 | assert(vis.result == 1); 227 | 228 | const auto & ref = s; 229 | visit_struct::apply_visitor(vis, ref); 230 | assert(vis.result == 2); 231 | 232 | visit_struct::apply_visitor(vis, std::move(s)); 233 | assert(vis.result == 3); 234 | } 235 | 236 | // Test visitation without an instance 237 | { 238 | test_visitor_ptr vis; 239 | 240 | visit_struct::visit_pointers(vis); 241 | assert(vis.result.size() == 3u); 242 | assert(vis.result[0].first == "b"); 243 | assert(vis.result[0].second == "bool"); 244 | assert(vis.result[1].first == "i"); 245 | assert(vis.result[1].second == "int"); 246 | assert(vis.result[2].first == "f"); 247 | assert(vis.result[2].second == "float"); 248 | } 249 | 250 | { 251 | test_visitor_type vis; 252 | 253 | visit_struct::visit_types(vis); 254 | assert(vis.result.size() == 3u); 255 | assert(vis.result[0].first == "b"); 256 | assert(vis.result[0].second == "bool"); 257 | assert(vis.result[1].first == "i"); 258 | assert(vis.result[1].second == "int"); 259 | assert(vis.result[2].first == "f"); 260 | assert(vis.result[2].second == "float"); 261 | } 262 | 263 | { 264 | test_visitor_acc vis2; 265 | 266 | visit_struct::visit_accessors(vis2); 267 | auto & vis = vis2.internal; 268 | 269 | assert(vis.result.size() == 3u); 270 | assert(vis.result[0].first == "b"); 271 | assert(vis.result[0].second == "bool"); 272 | assert(vis.result[1].first == "i"); 273 | assert(vis.result[1].second == "int"); 274 | assert(vis.result[2].first == "f"); 275 | assert(vis.result[2].second == "float"); 276 | } 277 | 278 | // Test binary visitation 279 | { 280 | test::foo f1{true, 1, 1.5f}; 281 | test::foo f2{true, 2, 10.0f}; 282 | 283 | assert(0 == struct_cmp(f1, f1)); 284 | assert(-1 == struct_cmp(f1, f2)); 285 | assert(0 == struct_cmp(f2, f2)); 286 | assert(1 == struct_cmp(f2, f1)); 287 | 288 | f1.i = 3; 289 | 290 | assert(0 == struct_cmp(f1, f1)); 291 | assert(1 == struct_cmp(f1, f2)); 292 | assert(0 == struct_cmp(f2, f2)); 293 | assert(-1 == struct_cmp(f2, f1)); 294 | 295 | f1.i = 2; 296 | 297 | assert(0 == struct_cmp(f1, f1)); 298 | assert(-1 == struct_cmp(f1, f2)); 299 | assert(0 == struct_cmp(f2, f2)); 300 | assert(1 == struct_cmp(f2, f1)); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /test_visit_struct_boost_hana.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct scope_test {}; 4 | static_assert(std::is_same<::scope_test, scope_test>::value, "WTF"); 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /*** 13 | * Test structures 14 | */ 15 | 16 | namespace dummy { 17 | 18 | struct test_struct_one { 19 | BOOST_HANA_DEFINE_STRUCT(test_struct_one, 20 | (int, a), 21 | (float, b), 22 | (std::string, c) 23 | ); 24 | }; 25 | 26 | } // end namespace dummy 27 | 28 | using dummy::test_struct_one; 29 | 30 | static_assert(visit_struct::traits::is_visitable::value, ""); 31 | static_assert(visit_struct::field_count() == 3, ""); 32 | 33 | struct test_struct_two { 34 | bool b; 35 | int i; 36 | double d; 37 | std::string s; 38 | }; 39 | 40 | BOOST_HANA_ADAPT_STRUCT(test_struct_two, d, i, b); 41 | 42 | static_assert(visit_struct::traits::is_visitable::value, ""); 43 | static_assert(visit_struct::field_count() == 3, ""); 44 | 45 | struct test_struct_three { 46 | int i1; 47 | const int i2; 48 | }; 49 | 50 | BOOST_HANA_ADAPT_STRUCT(test_struct_three, i1, i2); 51 | 52 | static_assert(visit_struct::traits::is_visitable::value, ""); 53 | static_assert(visit_struct::field_count() == 2, ""); 54 | 55 | /*** 56 | * Test visitors 57 | */ 58 | 59 | using spair = std::pair; 60 | 61 | struct test_visitor_one { 62 | std::vector result; 63 | 64 | void operator()(const char * name, const std::string & s) { 65 | result.emplace_back(spair{std::string{name}, s}); 66 | } 67 | 68 | template 69 | void operator()(const char * name, const T & t) { 70 | result.emplace_back(spair{std::string{name}, std::to_string(t)}); 71 | } 72 | }; 73 | 74 | 75 | 76 | using ppair = std::pair; 77 | 78 | struct test_visitor_two { 79 | std::vector result; 80 | 81 | template 82 | void operator()(const char * name, const T & t) { 83 | result.emplace_back(ppair{name, static_cast(&t)}); 84 | } 85 | }; 86 | 87 | 88 | 89 | struct test_visitor_three { 90 | int result = 0; 91 | 92 | template 93 | void operator()(const char *, T &&) {} 94 | 95 | void operator()(const char *, int &) { 96 | result = 1; 97 | } 98 | 99 | void operator()(const char *, const int &) { 100 | result = 2; 101 | } 102 | 103 | void operator()(const char *, int &&) { 104 | result = 3; 105 | } 106 | 107 | // Make it non-copyable and non-moveable, apply visitor should still work. 108 | test_visitor_three() = default; 109 | test_visitor_three(const test_visitor_three &) = delete; 110 | test_visitor_three(test_visitor_three &&) = delete; 111 | }; 112 | 113 | 114 | // Test binary visitation 115 | struct lex_compare_visitor { 116 | int result = 0; 117 | 118 | template 119 | void operator()(const char *, const T & t1, const T & t2) { 120 | if (!result) { 121 | if (t1 < t2) { result = -1; } 122 | else if (t1 > t2) { result = 1; } 123 | } 124 | } 125 | }; 126 | 127 | template 128 | int struct_cmp(const T & t1, const T & t2) { 129 | lex_compare_visitor vis; 130 | visit_struct::apply_visitor(vis, t1, t2); 131 | return vis.result; 132 | } 133 | 134 | // Test types visitation 135 | struct types_visitor { 136 | std::vector result; 137 | 138 | void operator()(const char *, visit_struct::type_c) { 139 | result.push_back("int"); 140 | } 141 | 142 | void operator()(const char *, visit_struct::type_c) { 143 | result.push_back("const int"); 144 | } 145 | 146 | void operator()(const char *, visit_struct::type_c) { 147 | result.push_back("bool"); 148 | } 149 | 150 | void operator()(const char *, visit_struct::type_c) { 151 | result.push_back("float"); 152 | } 153 | 154 | void operator()(const char *, visit_struct::type_c) { 155 | result.push_back("double"); 156 | } 157 | 158 | void operator()(const char *, visit_struct::type_c) { 159 | result.push_back("std::string"); 160 | } 161 | }; 162 | 163 | // debug_print 164 | 165 | struct debug_printer { 166 | template 167 | void operator()(const char * name, const T & t) const { 168 | std::cout << " " << name << ": " << t << std::endl; 169 | } 170 | }; 171 | 172 | template 173 | void debug_print(const T & t) { 174 | std::cout << "{\n"; 175 | visit_struct::apply_visitor(debug_printer{}, t); 176 | std::cout << "}" << std::endl; 177 | } 178 | 179 | /*** 180 | * tests 181 | */ 182 | 183 | static_assert(std::is_same(std::declval())), int &&>::value, ""); 184 | static_assert(std::is_same(std::declval())), float &&>::value, ""); 185 | static_assert(std::is_same(std::declval())), std::string &&>::value, ""); 186 | 187 | static_assert(std::is_same()), const char *>::value, ""); 188 | static_assert(std::is_same()), const char *>::value, ""); 189 | static_assert(std::is_same()), const char *>::value, ""); 190 | // TODO: This should ideally return const char (&)[], is that easy to do within hana? 191 | 192 | static_assert(std::is_same, int>::value, ""); 193 | static_assert(std::is_same, float>::value, ""); 194 | static_assert(std::is_same, std::string>::value, ""); 195 | 196 | static_assert(std::is_same, double>::value, ""); 197 | static_assert(std::is_same, int>::value, ""); 198 | static_assert(std::is_same, bool>::value, ""); 199 | 200 | static_assert(std::is_same, int>::value, ""); 201 | static_assert(std::is_same, const int>::value, ""); 202 | 203 | #include 204 | 205 | int main() { 206 | std::cout << __FILE__ << std::endl; 207 | std::cout << "Boost version: "<< BOOST_LIB_VERSION << std::endl; 208 | 209 | { 210 | test_struct_one s{ 5, 7.5f, "asdf" }; 211 | 212 | debug_print(s); 213 | 214 | assert(visit_struct::get<0>(s) == 5); 215 | assert(visit_struct::get<1>(s) == 7.5f); 216 | assert(visit_struct::get<2>(s) == "asdf"); 217 | assert(visit_struct::get_name<0>(s) == std::string{"a"}); 218 | assert(visit_struct::get_name<1>(s) == std::string{"b"}); 219 | assert(visit_struct::get_name<2>(s) == std::string{"c"}); 220 | assert(visit_struct::get_accessor<0>(s)(s) == visit_struct::get<0>(s)); 221 | assert(visit_struct::get_accessor<1>(s)(s) == visit_struct::get<1>(s)); 222 | assert(visit_struct::get_accessor<2>(s)(s) == visit_struct::get<2>(s)); 223 | 224 | test_visitor_one vis1; 225 | visit_struct::apply_visitor(vis1, s); 226 | 227 | assert(vis1.result.size() == 3); 228 | assert(vis1.result[0].first == "a"); 229 | assert(vis1.result[0].second == "5"); 230 | assert(vis1.result[1].first == "b"); 231 | assert(vis1.result[1].second == "7.500000"); 232 | assert(vis1.result[2].first == "c"); 233 | assert(vis1.result[2].second == "asdf"); 234 | 235 | test_visitor_two vis2; 236 | visit_struct::apply_visitor(vis2, s); 237 | 238 | assert(vis2.result.size() == 3); 239 | assert(vis2.result[0].second == &s.a); 240 | assert(vis2.result[1].second == &s.b); 241 | assert(vis2.result[2].second == &s.c); 242 | 243 | 244 | test_struct_one t{ 0, 0.0f, "jkl" }; 245 | 246 | debug_print(t); 247 | 248 | test_visitor_one vis3; 249 | visit_struct::apply_visitor(vis3, t); 250 | 251 | assert(vis3.result.size() == 3); 252 | assert(vis3.result[0].first == "a"); 253 | assert(vis3.result[0].second == "0"); 254 | assert(vis3.result[1].first == "b"); 255 | assert(vis3.result[1].second == "0.000000"); 256 | assert(vis3.result[2].first == "c"); 257 | assert(vis3.result[2].second == "jkl"); 258 | 259 | test_visitor_two vis4; 260 | visit_struct::apply_visitor(vis4, t); 261 | 262 | assert(vis4.result.size() == 3); 263 | assert(vis4.result[0].first == vis2.result[0].first); 264 | assert(vis4.result[0].second == &t.a); 265 | assert(vis4.result[1].first == vis2.result[1].first); 266 | assert(vis4.result[1].second == &t.b); 267 | assert(vis4.result[2].first == vis2.result[2].first); 268 | assert(vis4.result[2].second == &t.c); 269 | 270 | } 271 | 272 | { 273 | test_struct_two s{ false, 5, -1.0, "foo" }; 274 | 275 | debug_print(s); 276 | 277 | test_visitor_one vis1; 278 | visit_struct::apply_visitor(vis1, s); 279 | 280 | assert(vis1.result.size() == 3); 281 | assert(vis1.result[0].first == std::string{"d"}); 282 | assert(vis1.result[0].second == "-1.000000"); 283 | assert(vis1.result[1].first == std::string{"i"}); 284 | assert(vis1.result[1].second == "5"); 285 | assert(vis1.result[2].first == std::string{"b"}); 286 | assert(vis1.result[2].second == "0"); 287 | 288 | test_visitor_two vis2; 289 | visit_struct::apply_visitor(vis2, s); 290 | 291 | assert(vis2.result.size() == 3); 292 | assert(vis2.result[0].second == &s.d); 293 | assert(vis2.result[1].second == &s.i); 294 | assert(vis2.result[2].second == &s.b); 295 | 296 | 297 | test_struct_two t{ true, -14, .75, "bar" }; 298 | 299 | debug_print(t); 300 | 301 | test_visitor_one vis3; 302 | visit_struct::apply_visitor(vis3, t); 303 | 304 | assert(vis3.result.size() == 3); 305 | assert(vis3.result[0].first == std::string{"d"}); 306 | assert(vis3.result[0].second == "0.750000"); 307 | assert(vis3.result[1].first == std::string{"i"}); 308 | assert(vis3.result[1].second == "-14"); 309 | assert(vis3.result[2].first == std::string{"b"}); 310 | assert(vis3.result[2].second == "1"); 311 | 312 | test_visitor_two vis4; 313 | visit_struct::apply_visitor(vis4, t); 314 | 315 | assert(vis4.result.size() == 3); 316 | assert(vis4.result[0].first == vis2.result[0].first); 317 | assert(vis4.result[0].second == &t.d); 318 | assert(vis4.result[1].first == vis2.result[1].first); 319 | assert(vis4.result[1].second == &t.i); 320 | assert(vis4.result[2].first == vis2.result[2].first); 321 | assert(vis4.result[2].second == &t.b); 322 | } 323 | 324 | // Test move semantics 325 | { 326 | test_struct_one s{0, 0, ""}; 327 | 328 | test_visitor_three vis; 329 | 330 | visit_struct::apply_visitor(vis, s); 331 | assert(vis.result == 1); 332 | 333 | const auto & ref = s; 334 | visit_struct::apply_visitor(vis, ref); 335 | assert(vis.result == 2); 336 | 337 | visit_struct::apply_visitor(vis, std::move(s)); 338 | assert(vis.result == 3); 339 | } 340 | 341 | // Test binary visitation 342 | { 343 | test_struct_one f1{10, 7.5f, "a"}; 344 | test_struct_one f2{11, 7.5f, "b"}; 345 | 346 | assert(0 == struct_cmp(f1, f1)); 347 | assert(-1 == struct_cmp(f1, f2)); 348 | assert(0 == struct_cmp(f2, f2)); 349 | assert(1 == struct_cmp(f2, f1)); 350 | 351 | f1.a = 13; 352 | 353 | assert(0 == struct_cmp(f1, f1)); 354 | assert(1 == struct_cmp(f1, f2)); 355 | assert(0 == struct_cmp(f2, f2)); 356 | assert(-1 == struct_cmp(f2, f1)); 357 | 358 | f1.a = 11; 359 | 360 | assert(0 == struct_cmp(f1, f1)); 361 | assert(-1 == struct_cmp(f1, f2)); 362 | assert(0 == struct_cmp(f2, f2)); 363 | assert(1 == struct_cmp(f2, f1)); 364 | } 365 | 366 | // Test types visitation 367 | { 368 | types_visitor vis; 369 | visit_struct::visit_types(vis); 370 | assert(vis.result.size() == 3); 371 | assert(vis.result[0] == "int"); 372 | assert(vis.result[1] == "float"); 373 | assert(vis.result[2] == "std::string"); 374 | } 375 | 376 | { 377 | types_visitor vis; 378 | visit_struct::visit_types(vis); 379 | assert(vis.result.size() == 3); 380 | assert(vis.result[0] == "double"); 381 | assert(vis.result[1] == "int"); 382 | assert(vis.result[2] == "bool"); 383 | } 384 | 385 | { 386 | types_visitor vis; 387 | visit_struct::visit_types(vis); 388 | assert(vis.result.size() == 2); 389 | assert(vis.result[0] == "int"); 390 | assert(vis.result[1] == "const int"); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /test_visit_struct_boost_fusion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /*** 10 | * Test structures 11 | */ 12 | 13 | 14 | BOOST_FUSION_DEFINE_STRUCT((dummy), test_struct_one, 15 | (int, a) 16 | (float, b) 17 | (std::string, c)) 18 | 19 | using dummy::test_struct_one; 20 | 21 | static_assert(visit_struct::traits::is_visitable::value, "WTF"); 22 | static_assert(visit_struct::field_count() == 3, ""); 23 | 24 | struct test_struct_two { 25 | bool b; 26 | int i; 27 | double d; 28 | std::string s; 29 | }; 30 | 31 | // BOOST_FUSION_ADAPT_STRUCT(test_struct_two, d, i, b) 32 | 33 | BOOST_FUSION_ADAPT_STRUCT(test_struct_two, 34 | (double, d) 35 | (int, i) 36 | (bool, b)) 37 | 38 | 39 | static_assert(visit_struct::traits::is_visitable::value, "WTF"); 40 | static_assert(visit_struct::field_count() == 3, ""); 41 | 42 | /*** 43 | * Test visitors 44 | */ 45 | 46 | using spair = std::pair; 47 | 48 | struct test_visitor_one { 49 | std::vector result; 50 | 51 | void operator()(const char * name, const std::string & s) { 52 | result.emplace_back(spair{std::string{name}, s}); 53 | } 54 | 55 | template 56 | void operator()(const char * name, const T & t) { 57 | result.emplace_back(spair{std::string{name}, std::to_string(t)}); 58 | } 59 | }; 60 | 61 | 62 | 63 | using ppair = std::pair; 64 | 65 | struct test_visitor_two { 66 | std::vector result; 67 | 68 | template 69 | void operator()(const char * name, const T & t) { 70 | result.emplace_back(ppair{name, static_cast(&t)}); 71 | } 72 | }; 73 | 74 | 75 | 76 | struct test_visitor_three { 77 | int result = 0; 78 | 79 | template 80 | void operator()(const char *, T &&) {} 81 | 82 | void operator()(const char *, int &) { 83 | result = 1; 84 | } 85 | 86 | void operator()(const char *, const int &) { 87 | result = 2; 88 | } 89 | 90 | void operator()(const char *, int &&) { 91 | result = 3; 92 | } 93 | 94 | // Make it non-copyable and non-moveable, apply visitor should still work. 95 | test_visitor_three() = default; 96 | test_visitor_three(const test_visitor_three &) = delete; 97 | test_visitor_three(test_visitor_three &&) = delete; 98 | }; 99 | 100 | // visitation without instance 101 | 102 | struct test_visitor_type { 103 | std::vector result; 104 | 105 | void operator()(const char* name, visit_struct::type_c) { 106 | result.emplace_back(spair{std::string{name}, "int"}); 107 | } 108 | 109 | void operator()(const char* name, visit_struct::type_c) { 110 | result.emplace_back(spair{std::string{name}, "float"}); 111 | } 112 | 113 | void operator()(const char* name, visit_struct::type_c) { 114 | result.emplace_back(spair{std::string{name}, "double"}); 115 | } 116 | 117 | void operator()(const char* name, visit_struct::type_c) { 118 | result.emplace_back(spair{std::string{name}, "std::string"}); 119 | } 120 | 121 | void operator()(const char* name, visit_struct::type_c) { 122 | result.emplace_back(spair{std::string{name}, "bool"}); 123 | } 124 | }; 125 | 126 | // Test binary visitation 127 | struct lex_compare_visitor { 128 | int result = 0; 129 | 130 | template 131 | void operator()(const char *, const T & t1, const T & t2) { 132 | if (!result) { 133 | if (t1 < t2) { result = -1; } 134 | else if (t1 > t2) { result = 1; } 135 | } 136 | } 137 | }; 138 | 139 | template 140 | int struct_cmp(const T & t1, const T & t2) { 141 | lex_compare_visitor vis; 142 | visit_struct::apply_visitor(vis, t1, t2); 143 | return vis.result; 144 | } 145 | 146 | 147 | 148 | // debug_print 149 | 150 | struct debug_printer { 151 | template 152 | void operator()(const char * name, const T & t) const { 153 | std::cout << " " << name << ": " << t << std::endl; 154 | } 155 | }; 156 | 157 | template 158 | void debug_print(const T & t) { 159 | std::cout << "{\n"; 160 | visit_struct::apply_visitor(debug_printer{}, t); 161 | std::cout << "}" << std::endl; 162 | } 163 | 164 | /*** 165 | * tests 166 | */ 167 | 168 | static_assert(std::is_same(std::declval())), int &&>::value, ""); 169 | static_assert(std::is_same(std::declval())), float &&>::value, ""); 170 | static_assert(std::is_same(std::declval())), std::string &&>::value, ""); 171 | 172 | static_assert(std::is_same()), const char *>::value, ""); 173 | static_assert(std::is_same()), const char *>::value, ""); 174 | static_assert(std::is_same()), const char *>::value, ""); 175 | 176 | static_assert(std::is_same(std::declval())), double &&>::value, ""); 177 | static_assert(std::is_same(std::declval())), int &&>::value, ""); 178 | static_assert(std::is_same(std::declval())), bool &&>::value, ""); 179 | 180 | static_assert(std::is_same, int>::value, ""); 181 | static_assert(std::is_same, float>::value, ""); 182 | static_assert(std::is_same, std::string>::value, ""); 183 | 184 | static_assert(std::is_same, double>::value, ""); 185 | static_assert(std::is_same, int>::value, ""); 186 | static_assert(std::is_same, bool>::value, ""); 187 | 188 | #include 189 | 190 | int main() { 191 | std::cout << __FILE__ << std::endl; 192 | std::cout << "Boost version: "<< BOOST_LIB_VERSION << std::endl; 193 | 194 | { 195 | test_struct_one s{ 5, 7.5f, "asdf" }; 196 | 197 | debug_print(s); 198 | 199 | assert(visit_struct::get<0>(s) == 5); 200 | assert(visit_struct::get<1>(s) == 7.5f); 201 | assert(visit_struct::get<2>(s) == "asdf"); 202 | assert(visit_struct::get_name<0>(s) == std::string{"a"}); 203 | assert(visit_struct::get_name<1>(s) == std::string{"b"}); 204 | assert(visit_struct::get_name<2>(s) == std::string{"c"}); 205 | assert(visit_struct::get_accessor<0>(s)(s) == visit_struct::get<0>(s)); 206 | assert(visit_struct::get_accessor<1>(s)(s) == visit_struct::get<1>(s)); 207 | assert(visit_struct::get_accessor<2>(s)(s) == visit_struct::get<2>(s)); 208 | 209 | test_visitor_one vis1; 210 | visit_struct::apply_visitor(vis1, s); 211 | 212 | assert(vis1.result.size() == 3); 213 | assert(vis1.result[0].first == "a"); 214 | assert(vis1.result[0].second == "5"); 215 | assert(vis1.result[1].first == "b"); 216 | assert(vis1.result[1].second == "7.500000"); 217 | assert(vis1.result[2].first == "c"); 218 | assert(vis1.result[2].second == "asdf"); 219 | 220 | test_visitor_two vis2; 221 | visit_struct::apply_visitor(vis2, s); 222 | 223 | assert(vis2.result.size() == 3); 224 | assert(vis2.result[0].second == &s.a); 225 | assert(vis2.result[1].second == &s.b); 226 | assert(vis2.result[2].second == &s.c); 227 | 228 | 229 | test_struct_one t{ 0, 0.0f, "jkl" }; 230 | 231 | debug_print(t); 232 | 233 | test_visitor_one vis3; 234 | visit_struct::apply_visitor(vis3, t); 235 | 236 | assert(vis3.result.size() == 3); 237 | assert(vis3.result[0].first == "a"); 238 | assert(vis3.result[0].second == "0"); 239 | assert(vis3.result[1].first == "b"); 240 | assert(vis3.result[1].second == "0.000000"); 241 | assert(vis3.result[2].first == "c"); 242 | assert(vis3.result[2].second == "jkl"); 243 | 244 | test_visitor_two vis4; 245 | visit_struct::apply_visitor(vis4, t); 246 | 247 | assert(vis4.result.size() == 3); 248 | assert(vis4.result[0].first == vis2.result[0].first); 249 | assert(vis4.result[0].second == &t.a); 250 | assert(vis4.result[1].first == vis2.result[1].first); 251 | assert(vis4.result[1].second == &t.b); 252 | assert(vis4.result[2].first == vis2.result[2].first); 253 | assert(vis4.result[2].second == &t.c); 254 | 255 | } 256 | 257 | { 258 | test_struct_two s{ false, 5, -1.0, "foo" }; 259 | 260 | debug_print(s); 261 | 262 | test_visitor_one vis1; 263 | visit_struct::apply_visitor(vis1, s); 264 | 265 | assert(vis1.result.size() == 3); 266 | assert(vis1.result[0].first == std::string{"d"}); 267 | assert(vis1.result[0].second == "-1.000000"); 268 | assert(vis1.result[1].first == std::string{"i"}); 269 | assert(vis1.result[1].second == "5"); 270 | assert(vis1.result[2].first == std::string{"b"}); 271 | assert(vis1.result[2].second == "0"); 272 | 273 | test_visitor_two vis2; 274 | visit_struct::apply_visitor(vis2, s); 275 | 276 | assert(vis2.result.size() == 3); 277 | assert(vis2.result[0].second == &s.d); 278 | assert(vis2.result[1].second == &s.i); 279 | assert(vis2.result[2].second == &s.b); 280 | 281 | 282 | test_struct_two t{ true, -14, .75, "bar" }; 283 | 284 | debug_print(t); 285 | 286 | test_visitor_one vis3; 287 | visit_struct::apply_visitor(vis3, t); 288 | 289 | assert(vis3.result.size() == 3); 290 | assert(vis3.result[0].first == std::string{"d"}); 291 | assert(vis3.result[0].second == "0.750000"); 292 | assert(vis3.result[1].first == std::string{"i"}); 293 | assert(vis3.result[1].second == "-14"); 294 | assert(vis3.result[2].first == std::string{"b"}); 295 | assert(vis3.result[2].second == "1"); 296 | 297 | test_visitor_two vis4; 298 | visit_struct::apply_visitor(vis4, t); 299 | 300 | assert(vis4.result.size() == 3); 301 | assert(vis4.result[0].first == vis2.result[0].first); 302 | assert(vis4.result[0].second == &t.d); 303 | assert(vis4.result[1].first == vis2.result[1].first); 304 | assert(vis4.result[1].second == &t.i); 305 | assert(vis4.result[2].first == vis2.result[2].first); 306 | assert(vis4.result[2].second == &t.b); 307 | } 308 | 309 | // Test move semantics 310 | { 311 | test_struct_one s{0, 0, ""}; 312 | 313 | test_visitor_three vis; 314 | 315 | visit_struct::apply_visitor(vis, s); 316 | assert(vis.result == 1); 317 | 318 | const auto & ref = s; 319 | visit_struct::apply_visitor(vis, ref); 320 | assert(vis.result == 2); 321 | 322 | visit_struct::apply_visitor(vis, std::move(s)); 323 | assert(vis.result == 3); 324 | } 325 | 326 | // Test visitation without instance 327 | { 328 | test_visitor_type vis; 329 | 330 | visit_struct::visit_types(vis); 331 | assert(vis.result.size() == 3u); 332 | assert(vis.result[0].first == "a"); 333 | assert(vis.result[0].second == "int"); 334 | assert(vis.result[1].first == "b"); 335 | assert(vis.result[1].second == "float"); 336 | assert(vis.result[2].first == "c"); 337 | assert(vis.result[2].second == "std::string"); 338 | } 339 | 340 | { 341 | test_visitor_type vis; 342 | 343 | visit_struct::visit_types(vis); 344 | assert(vis.result.size() == 3u); 345 | assert(vis.result[0].first == "d"); 346 | assert(vis.result[0].second == "double"); 347 | assert(vis.result[1].first == "i"); 348 | assert(vis.result[1].second == "int"); 349 | assert(vis.result[2].first == "b"); 350 | assert(vis.result[2].second == "bool"); 351 | } 352 | 353 | // Test binary visitation 354 | { 355 | test_struct_two f1{true, 1, 1.5, "holy"}; 356 | test_struct_two f2{true, 2, 10.0, "moly"}; 357 | 358 | assert(0 == struct_cmp(f1, f1)); 359 | assert(-1 == struct_cmp(f1, f2)); 360 | assert(0 == struct_cmp(f2, f2)); 361 | assert(1 == struct_cmp(f2, f1)); 362 | 363 | f1.d = 10; 364 | 365 | assert(0 == struct_cmp(f1, f1)); 366 | assert(-1 == struct_cmp(f1, f2)); 367 | assert(0 == struct_cmp(f2, f2)); 368 | assert(1 == struct_cmp(f2, f1)); 369 | 370 | f1.i = 3; 371 | 372 | assert(0 == struct_cmp(f1, f1)); 373 | assert(1 == struct_cmp(f1, f2)); 374 | assert(0 == struct_cmp(f2, f2)); 375 | assert(-1 == struct_cmp(f2, f1)); 376 | 377 | f2.i = 3; 378 | 379 | assert(0 == struct_cmp(f1, f1)); 380 | assert(0 == struct_cmp(f1, f2)); 381 | assert(0 == struct_cmp(f2, f2)); 382 | assert(0 == struct_cmp(f2, f1)); 383 | 384 | 385 | f1.d = 20.5; 386 | 387 | assert(0 == struct_cmp(f1, f1)); 388 | assert(1 == struct_cmp(f1, f2)); 389 | assert(0 == struct_cmp(f2, f2)); 390 | assert(-1 == struct_cmp(f2, f1)); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /test_visit_struct.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /*** 11 | * Test structures 12 | */ 13 | 14 | struct test_struct_one { 15 | int a; 16 | float b; 17 | std::string c; 18 | }; 19 | 20 | VISITABLE_STRUCT(test_struct_one, a, b, c); 21 | 22 | static_assert(visit_struct::traits::is_visitable::value, "WTF"); 23 | static_assert(visit_struct::field_count() == 3, "WTF"); 24 | 25 | struct test_struct_two { 26 | bool b; 27 | int i; 28 | double d; 29 | std::string s; 30 | }; 31 | 32 | VISITABLE_STRUCT(test_struct_two, d, i, b); 33 | // Note the order and that 's' is not registered! 34 | 35 | static_assert(visit_struct::traits::is_visitable::value, "WTF"); 36 | static_assert(visit_struct::field_count() == 3, "WTF"); 37 | 38 | // Make test struct two have a special context for visitation 39 | struct MyContext {}; 40 | VISITABLE_STRUCT_IN_CONTEXT(MyContext, test_struct_two, b, i, d, s); 41 | 42 | static_assert(visit_struct::traits::is_visitable::value, "WTF"); 43 | static_assert(visit_struct::context::field_count() == 4, "WTF"); 44 | 45 | /*** 46 | * Test visitors 47 | */ 48 | 49 | using spair = std::pair; 50 | 51 | struct test_visitor_one { 52 | std::vector result; 53 | 54 | void operator()(const char * name, const std::string & s) { 55 | result.emplace_back(spair{std::string{name}, s}); 56 | } 57 | 58 | template 59 | void operator()(const char * name, const T & t) { 60 | result.emplace_back(spair{std::string{name}, std::to_string(t)}); 61 | } 62 | }; 63 | 64 | struct test_visitor_ptr { 65 | std::vector result; 66 | 67 | template 68 | void operator()(const char* name, int C::*) { 69 | result.emplace_back(spair{std::string{name}, "int"}); 70 | } 71 | 72 | template 73 | void operator()(const char* name, float C::*) { 74 | result.emplace_back(spair{std::string{name}, "float"}); 75 | } 76 | 77 | template 78 | void operator()(const char* name, std::string C::*) { 79 | result.emplace_back(spair{std::string{name}, "std::string"}); 80 | } 81 | }; 82 | 83 | struct test_visitor_type { 84 | std::vector result; 85 | 86 | void operator()(const char* name, visit_struct::type_c) { 87 | result.emplace_back(spair{std::string{name}, "int"}); 88 | } 89 | 90 | void operator()(const char* name, visit_struct::type_c) { 91 | result.emplace_back(spair{std::string{name}, "float"}); 92 | } 93 | 94 | void operator()(const char* name, visit_struct::type_c) { 95 | result.emplace_back(spair{std::string{name}, "std::string"}); 96 | } 97 | }; 98 | 99 | 100 | 101 | using ppair = std::pair; 102 | 103 | struct test_visitor_two { 104 | std::vector result; 105 | 106 | template 107 | void operator()(const char * name, const T & t) { 108 | result.emplace_back(ppair{name, static_cast(&t)}); 109 | } 110 | }; 111 | 112 | 113 | 114 | struct test_visitor_three { 115 | int result = 0; 116 | 117 | template 118 | void operator()(const char *, T &&) {} 119 | 120 | void operator()(const char *, int &) { 121 | result = 1; 122 | } 123 | 124 | void operator()(const char *, const int &) { 125 | result = 2; 126 | } 127 | 128 | void operator()(const char *, int &&) { 129 | result = 3; 130 | } 131 | 132 | // Make it non-copyable and non-moveable, apply visitor should still work. 133 | test_visitor_three() = default; 134 | test_visitor_three(const test_visitor_three &) = delete; 135 | test_visitor_three(test_visitor_three &&) = delete; 136 | }; 137 | 138 | 139 | // Some binary visitors for test 140 | 141 | struct test_eq_visitor { 142 | bool result = true; 143 | 144 | template 145 | void operator()(const char *, const T & t1, const T & t2) { 146 | result = result && (t1 == t2); 147 | } 148 | }; 149 | 150 | struct test_pair_visitor { 151 | bool result = false; 152 | 153 | template 154 | void operator()(const char *, const T &, const T &) {} 155 | 156 | void operator()(const char *, const int & x, const int & y) { 157 | result = result || (x > y); 158 | } 159 | }; 160 | 161 | // Interface for binary visitors 162 | template 163 | bool struct_eq(const T & t1, const T & t2) { 164 | test_eq_visitor vis; 165 | visit_struct::apply_visitor(vis, t1, t2); 166 | return vis.result; 167 | } 168 | 169 | template 170 | bool struct_int_cmp(const T & t1, const T & t2) { 171 | test_pair_visitor vis; 172 | visit_struct::apply_visitor(vis, t1, t2); 173 | return vis.result; 174 | } 175 | 176 | // debug_print 177 | 178 | struct debug_printer { 179 | template 180 | void operator()(const char * name, const T & t) const { 181 | std::cout << " " << name << ": " << t << std::endl; 182 | } 183 | }; 184 | 185 | template 186 | void debug_print(const T & t) { 187 | std::cout << "{\n"; 188 | visit_struct::apply_visitor(debug_printer{}, t); 189 | std::cout << "}" << std::endl; 190 | } 191 | 192 | /*** 193 | * tests 194 | */ 195 | 196 | static_assert(std::is_same(std::declval())), int &&>::value, ""); 197 | static_assert(std::is_same(std::declval())), float &&>::value, ""); 198 | static_assert(std::is_same(std::declval())), std::string &&>::value, ""); 199 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 200 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 201 | static_assert(std::is_same()), const char (&)[2]>::value, ""); 202 | static_assert(visit_struct::get_pointer<0, test_struct_one>() == &test_struct_one::a, ""); 203 | static_assert(visit_struct::get_pointer<1, test_struct_one>() == &test_struct_one::b, ""); 204 | static_assert(visit_struct::get_pointer<2, test_struct_one>() == &test_struct_one::c, ""); 205 | static_assert(std::is_same, int>::value, ""); 206 | static_assert(std::is_same, float>::value, ""); 207 | static_assert(std::is_same, std::string>::value, ""); 208 | 209 | int main() { 210 | // Test version string 211 | std::cout << VISIT_STRUCT_VERSION_STRING << std::endl; 212 | 213 | std::cout << __FILE__ << std::endl; 214 | 215 | { 216 | test_struct_one s{ 5, 7.5f, "asdf" }; 217 | 218 | debug_print(s); 219 | 220 | // Test getters 221 | 222 | assert(visit_struct::field_count(s) == 3); 223 | assert(visit_struct::get<0>(s) == 5); 224 | assert(visit_struct::get<1>(s) == 7.5f); 225 | assert(visit_struct::get<2>(s) == "asdf"); 226 | assert(visit_struct::get_name<0>(s) == std::string{"a"}); 227 | assert(visit_struct::get_name<1>(s) == std::string{"b"}); 228 | assert(visit_struct::get_name<2>(s) == std::string{"c"}); 229 | assert(visit_struct::get_accessor<0>(s)(s) == visit_struct::get<0>(s)); 230 | assert(visit_struct::get_accessor<1>(s)(s) == visit_struct::get<1>(s)); 231 | assert(visit_struct::get_accessor<2>(s)(s) == visit_struct::get<2>(s)); 232 | 233 | // Test visitation 234 | 235 | test_visitor_one vis1; 236 | visit_struct::apply_visitor(vis1, s); 237 | 238 | assert(vis1.result.size() == 3); 239 | assert(vis1.result[0].first == "a"); 240 | assert(vis1.result[0].second == "5"); 241 | assert(vis1.result[1].first == "b"); 242 | assert(vis1.result[1].second == "7.500000"); 243 | assert(vis1.result[2].first == "c"); 244 | assert(vis1.result[2].second == "asdf"); 245 | 246 | test_visitor_two vis2; 247 | visit_struct::apply_visitor(vis2, s); 248 | 249 | assert(vis2.result.size() == 3); 250 | assert(vis2.result[0].second == &s.a); 251 | assert(vis2.result[1].second == &s.b); 252 | assert(vis2.result[2].second == &s.c); 253 | 254 | test_struct_one t{ 0, 0.0f, "jkl" }; 255 | 256 | debug_print(t); 257 | 258 | assert(visit_struct::field_count(t) == 3); 259 | assert(visit_struct::get<0>(t) == 0); 260 | assert(visit_struct::get<1>(t) == 0.0f); 261 | assert(visit_struct::get<2>(t) == "jkl"); 262 | assert(visit_struct::get_name<0>(t) == std::string{"a"}); 263 | assert(visit_struct::get_name<1>(t) == std::string{"b"}); 264 | assert(visit_struct::get_name<2>(t) == std::string{"c"}); 265 | 266 | test_visitor_one vis3; 267 | visit_struct::for_each(t, vis3); 268 | 269 | assert(vis3.result.size() == 3); 270 | assert(vis3.result[0].first == "a"); 271 | assert(vis3.result[0].second == "0"); 272 | assert(vis3.result[1].first == "b"); 273 | assert(vis3.result[1].second == "0.000000"); 274 | assert(vis3.result[2].first == "c"); 275 | assert(vis3.result[2].second == "jkl"); 276 | 277 | test_visitor_two vis4; 278 | visit_struct::apply_visitor(vis4, t); 279 | 280 | assert(vis4.result.size() == 3); 281 | assert(vis4.result[0].first == vis2.result[0].first); 282 | assert(vis4.result[0].second == &t.a); 283 | assert(vis4.result[1].first == vis2.result[1].first); 284 | assert(vis4.result[1].second == &t.b); 285 | assert(vis4.result[2].first == vis2.result[2].first); 286 | assert(vis4.result[2].second == &t.c); 287 | 288 | // test get_name 289 | assert(std::string("test_struct_one") == visit_struct::get_name(s)); 290 | assert(std::string("test_struct_one") == visit_struct::get_name(t)); 291 | assert(std::string("test_struct_one") == visit_struct::get_name()); 292 | } 293 | 294 | { 295 | test_struct_two s{ false, 5, -1.0, "foo" }; 296 | 297 | debug_print(s); 298 | 299 | assert(visit_struct::get<0>(s) == -1.0f); 300 | assert(visit_struct::get<1>(s) == 5); 301 | assert(visit_struct::get<2>(s) == false); 302 | assert(visit_struct::get_name<0>(s) == std::string{"d"}); 303 | assert(visit_struct::get_name<1>(s) == std::string{"i"}); 304 | assert(visit_struct::get_name<2>(s) == std::string{"b"}); 305 | 306 | 307 | test_visitor_one vis1; 308 | visit_struct::apply_visitor(vis1, s); 309 | 310 | assert(vis1.result.size() == 3); 311 | assert(vis1.result[0].first == std::string{"d"}); 312 | assert(vis1.result[0].second == "-1.000000"); 313 | assert(vis1.result[1].first == std::string{"i"}); 314 | assert(vis1.result[1].second == "5"); 315 | assert(vis1.result[2].first == std::string{"b"}); 316 | assert(vis1.result[2].second == "0"); 317 | 318 | test_visitor_two vis2; 319 | visit_struct::apply_visitor(vis2, s); 320 | 321 | assert(vis2.result.size() == 3); 322 | assert(vis2.result[0].first == std::string{"d"}); 323 | assert(vis2.result[0].second == &s.d); 324 | assert(vis2.result[1].first == std::string{"i"}); 325 | assert(vis2.result[1].second == &s.i); 326 | assert(vis2.result[2].first == std::string{"b"}); 327 | assert(vis2.result[2].second == &s.b); 328 | 329 | test_visitor_two vis2_context; 330 | visit_struct::context::apply_visitor(vis2_context, s); 331 | 332 | assert(vis2_context.result.size() == 4); 333 | assert(vis2_context.result[0].first == std::string{"b"}); 334 | assert(vis2_context.result[0].second == &s.b); 335 | assert(vis2_context.result[1].first == std::string{"i"}); 336 | assert(vis2_context.result[1].second == &s.i); 337 | assert(vis2_context.result[2].first == std::string{"d"}); 338 | assert(vis2_context.result[2].second == &s.d); 339 | assert(vis2_context.result[3].first == std::string{"s"}); 340 | assert(vis2_context.result[3].second == &s.s); 341 | 342 | test_struct_two t{ true, -14, .75, "bar" }; 343 | 344 | debug_print(t); 345 | 346 | test_visitor_one vis3; 347 | visit_struct::apply_visitor(vis3, t); 348 | 349 | assert(vis3.result.size() == 3); 350 | assert(vis3.result[0].first == std::string{"d"}); 351 | assert(vis3.result[0].second == "0.750000"); 352 | assert(vis3.result[1].first == std::string{"i"}); 353 | assert(vis3.result[1].second == "-14"); 354 | assert(vis3.result[2].first == std::string{"b"}); 355 | assert(vis3.result[2].second == "1"); 356 | 357 | test_visitor_two vis4; 358 | visit_struct::apply_visitor(vis4, t); 359 | 360 | assert(vis4.result.size() == 3); 361 | assert(vis4.result[0].first == vis2.result[0].first); 362 | assert(vis4.result[0].second == &t.d); 363 | assert(vis4.result[1].first == vis2.result[1].first); 364 | assert(vis4.result[1].second == &t.i); 365 | assert(vis4.result[2].first == vis2.result[2].first); 366 | assert(vis4.result[2].second == &t.b); 367 | 368 | // test get_name 369 | assert(std::string("test_struct_two") == visit_struct::get_name(s)); 370 | assert(std::string("test_struct_two") == visit_struct::get_name(t)); 371 | assert(std::string("test_struct_two") == visit_struct::get_name()); 372 | } 373 | 374 | // Test move semantics 375 | { 376 | test_struct_one s{0, 0, ""}; 377 | 378 | test_visitor_three vis; 379 | 380 | visit_struct::apply_visitor(vis, s); 381 | assert(vis.result == 1); 382 | 383 | const auto & ref = s; 384 | visit_struct::apply_visitor(vis, ref); 385 | assert(vis.result == 2); 386 | 387 | visit_struct::apply_visitor(vis, std::move(s)); 388 | assert(vis.result == 3); 389 | } 390 | 391 | // Test visiting with no instance 392 | { 393 | test_visitor_ptr vis; 394 | 395 | visit_struct::visit_pointers(vis); 396 | assert(vis.result.size() == 3u); 397 | assert(vis.result[0].first == "a"); 398 | assert(vis.result[0].second == "int"); 399 | assert(vis.result[1].first == "b"); 400 | assert(vis.result[1].second == "float"); 401 | assert(vis.result[2].first == "c"); 402 | assert(vis.result[2].second == "std::string"); 403 | } 404 | 405 | { 406 | test_visitor_type vis; 407 | 408 | visit_struct::visit_types(vis); 409 | assert(vis.result.size() == 3u); 410 | assert(vis.result[0].first == "a"); 411 | assert(vis.result[0].second == "int"); 412 | assert(vis.result[1].first == "b"); 413 | assert(vis.result[1].second == "float"); 414 | assert(vis.result[2].first == "c"); 415 | assert(vis.result[2].second == "std::string"); 416 | } 417 | 418 | // Test visiting two instances 419 | { 420 | test_struct_one s1{0, 0, ""}; 421 | test_struct_one s2{1, 1, "a"}; 422 | test_struct_one s3{2, 0, ""}; 423 | test_struct_one s4{3, 4, "b"}; 424 | 425 | assert(struct_eq(s1, s1)); 426 | assert(struct_eq(s2, s2)); 427 | assert(struct_eq(s3, s3)); 428 | assert(struct_eq(s4, s4)); 429 | 430 | assert(!struct_eq(s1, s2)); 431 | assert(!struct_eq(s1, s3)); 432 | assert(!struct_eq(s1, s4)); 433 | assert(!struct_eq(s2, s3)); 434 | 435 | assert(struct_int_cmp(s2, s1)); 436 | assert(!struct_int_cmp(s1, s2)); 437 | assert(struct_int_cmp(s3, s1)); 438 | assert(!struct_int_cmp(s1, s3)); 439 | assert(struct_int_cmp(s4, s1)); 440 | assert(!struct_int_cmp(s1, s4)); 441 | } 442 | } 443 | -------------------------------------------------------------------------------- /include/visit_struct/visit_struct_intrusive.hpp: -------------------------------------------------------------------------------- 1 | // (C) Copyright 2015 - 2018 Christopher Beck 2 | 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED 7 | #define VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED 8 | 9 | /*** 10 | * A collection of templates and macros supporting a second form of VISIT_STRUCT 11 | * mechanism. 12 | * 13 | * In this version, the visitable members are declared *within* the body of the 14 | * struct, at the same time that they are actually declared. 15 | * 16 | * This version uses templates for iteration rather than macros, so it's really 17 | * fairly different. It is more DRY and less likely to produce gross error 18 | * messages than the other, at the cost of being invasive to your structure 19 | * definition. 20 | * 21 | * This version adds some typedefs to your class, and it invisibly adds some 22 | * declarations of obscure member functions to your class. These declarations 23 | * do not have corresponding definitions and generate no object code, they are 24 | * merely a device for metaprogramming, exploiting overload resolution rules to 25 | * create "state". In normal code, you won't be able to detect any of this. 26 | * 27 | * This sounds a lot more evil than it really is -- it is morally equivalent to 28 | * `std::declval`, I would say, which is also specified to be a declaration with 29 | * no definition, which you simply aren't permitted to odr-use. 30 | */ 31 | 32 | #include 33 | 34 | namespace visit_struct { 35 | 36 | namespace detail { 37 | 38 | /*** 39 | * Poor man's mpl vector 40 | */ 41 | 42 | template 43 | struct TypeList { 44 | static VISIT_STRUCT_CONSTEXPR const unsigned int size = sizeof...(Ts); 45 | }; 46 | 47 | // Append metafunction 48 | template 49 | struct Append; 50 | 51 | template 52 | struct Append, T> { 53 | typedef TypeList type; 54 | }; 55 | 56 | template 57 | using Append_t = typename Append::type; 58 | 59 | // Cdr metafunction (cdr is a lisp function which returns the tail of a list) 60 | template 61 | struct Cdr; 62 | 63 | template 64 | struct Cdr> { 65 | typedef TypeList type; 66 | }; 67 | 68 | template 69 | using Cdr_t = typename Cdr::type; 70 | 71 | // Find metafunction (get the idx'th element) 72 | template 73 | struct Find : Find, idx - 1> {}; 74 | 75 | template 76 | struct Find, 0> { 77 | typedef T type; 78 | }; 79 | 80 | template 81 | using Find_t = typename Find::type; 82 | 83 | // Alias used when capturing references to string literals 84 | 85 | template 86 | using char_array = const char [N]; 87 | 88 | /*** 89 | * The "rank" template is a trick which can be used for 90 | * certain metaprogramming techniques. It creates 91 | * an inheritance hierarchy of trivial classes. 92 | */ 93 | 94 | template 95 | struct Rank : Rank {}; 96 | 97 | template <> 98 | struct Rank<0> {}; 99 | 100 | static VISIT_STRUCT_CONSTEXPR const int max_visitable_members_intrusive = 100; 101 | 102 | /*** 103 | * To create a "compile-time" TypeList whose members are accumulated one-by-one, 104 | * the basic idea is to define a function, which takes a `Rank` object, and 105 | * whose return type is the type representing the current value of the list. 106 | * 107 | * That function is not a template function -- it is defined as taking a 108 | * particular rank object. Initially, it is defined only for `Rank<0>`. 109 | * 110 | * To add an element to the list, we define an overload of the function, which 111 | * takes the next higher `Rank` as it's argument. It's return value is, 112 | * the new value of the list, formed by using `Append_t` with the old value. 113 | * 114 | * To obtain the current value of the list, we use decltype with the name of the 115 | * function, and `Rank<100>`, or some suitably large integer. The C++ standard 116 | * specifies that overload resolution is in this case unambiguous and must 117 | * select the overload for the "most-derived" type which matches. 118 | * 119 | * The upshot is that `decltype(my_function(Rank<100>{}))` is a single well-formed 120 | * expression, which, because of C++ overload resolution rules, can be a 121 | * "mutable" value from the point of view of metaprogramming. 122 | * 123 | * 124 | * Attribution: 125 | * I first learned this trick from a stackoverflow post by Roman Perepelitsa: 126 | * http://stackoverflow.com/questions/4790721/c-type-registration-at-compile-time-trick 127 | * 128 | * He attributes it to a talk from Matt Calabrese at BoostCon 2011. 129 | * 130 | * 131 | * The expression is inherently dangerous if you are using it inside the body 132 | * of a struct -- obviously, it has different values at different points of the 133 | * structure definition. The "END_VISITABLES" macro is important in that this 134 | * finalizes the list, typedeffing `decltype(my_function(Rank<100>{}))` to some 135 | * fixed name in your struct at a specific point in the definition. That 136 | * typedef can only ultimately have one meaning, no matter where else the name 137 | * may be used (even implicitly) in your structure definition. That typedef is 138 | * what the trait defined in this header ultimately hooks into to find the 139 | * visitable members. 140 | */ 141 | 142 | // A tag inserted into a structure to mark it as visitable 143 | 144 | struct intrusive_tag{}; 145 | 146 | /*** 147 | * Helper structures which perform pack expansion in order to visit a structure. 148 | */ 149 | 150 | // In MSVC 2015, sometimes a pointer to member cannot be constexpr, for instance 151 | // I had trouble with code like this: 152 | // 153 | // struct S { 154 | // int a; 155 | // static constexpr auto a_ptr = &S::a; 156 | // }; 157 | // 158 | // This works fine in gcc and clang. 159 | // MSVC is okay with it if instead it is a template parameter it seems, so we 160 | // use `member_ptr_helper` below as a workaround, a bit like so: 161 | // 162 | // struct S { 163 | // int a; 164 | // using a_helper = member_ptr_helper; 165 | // }; 166 | 167 | template 168 | struct member_ptr_helper { 169 | static VISIT_STRUCT_CONSTEXPR T S::* get_ptr() { return member_ptr; } 170 | using value_type = T; 171 | 172 | using accessor_t = visit_struct::accessor; 173 | }; 174 | 175 | // M should be derived from a member_ptr_helper 176 | template 177 | struct member_helper { 178 | template 179 | VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S && structure_instance) { 180 | std::forward(visitor)(M::member_name(), std::forward(structure_instance).*M::get_ptr()); 181 | } 182 | 183 | template 184 | VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S1 && s1, S2 && s2) { 185 | std::forward(visitor)(M::member_name(), 186 | std::forward(s1).*M::get_ptr(), 187 | std::forward(s2).*M::get_ptr()); 188 | } 189 | 190 | template 191 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_pointers(V && visitor) { 192 | std::forward(visitor)(M::member_name(), M::get_ptr()); 193 | } 194 | 195 | template 196 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_accessors(V && visitor) { 197 | std::forward(visitor)(M::member_name(), typename M::accessor_t()); 198 | } 199 | 200 | template 201 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_types(V && visitor) { 202 | std::forward(visitor)(M::member_name(), visit_struct::type_c{}); 203 | } 204 | 205 | }; 206 | 207 | template 208 | struct structure_helper; 209 | 210 | template 211 | struct structure_helper> { 212 | template 213 | VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S && structure_instance) { 214 | // Use parameter pack expansion to force evaluation of the member helper for each member in the list. 215 | // Inside parens, a comma operator is being used to discard the void value and produce an integer, while 216 | // not being an unevaluated context. The order of evaluation here is enforced by the compiler. 217 | // Extra zero at the end is to avoid UB for having a zero-size array. 218 | int dummy[] = {(member_helper::apply_visitor(std::forward(visitor), std::forward(structure_instance)), 0)..., 0}; 219 | // Suppress unused warnings, even in case of empty parameter pack 220 | static_cast(dummy); 221 | static_cast(visitor); 222 | static_cast(structure_instance); 223 | } 224 | 225 | template 226 | VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S1 && s1, S2 && s2) { 227 | int dummy[] = {(member_helper::apply_visitor(std::forward(visitor), std::forward(s1), std::forward(s2)), 0)..., 0}; 228 | static_cast(dummy); 229 | static_cast(visitor); 230 | static_cast(s1); 231 | static_cast(s2); 232 | } 233 | 234 | template 235 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_pointers(V && visitor) { 236 | int dummy[] = {(member_helper::visit_pointers(std::forward(visitor)), 0)..., 0}; 237 | static_cast(dummy); 238 | static_cast(visitor); 239 | } 240 | 241 | template 242 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_accessors(V && visitor) { 243 | int dummy[] = {(member_helper::visit_accessors(std::forward(visitor)), 0)..., 0}; 244 | static_cast(dummy); 245 | static_cast(visitor); 246 | } 247 | 248 | template 249 | VISIT_STRUCT_CXX14_CONSTEXPR static void visit_types(V && visitor) { 250 | int dummy[] = {(member_helper::visit_types(std::forward(visitor)), 0)..., 0}; 251 | static_cast(dummy); 252 | static_cast(visitor); 253 | } 254 | }; 255 | 256 | 257 | } // end namespace detail 258 | 259 | 260 | /*** 261 | * Implement trait 262 | */ 263 | 264 | namespace traits { 265 | 266 | template 267 | struct visitable ::value 272 | >::type 273 | > 274 | { 275 | static VISIT_STRUCT_CONSTEXPR const std::size_t field_count = T::Visit_Struct_Registered_Members_List__::size; 276 | 277 | // Apply to an instance 278 | // S should be the same type as T modulo const and reference 279 | template 280 | static VISIT_STRUCT_CXX14_CONSTEXPR void apply(V && v, S && s) { 281 | detail::structure_helper::apply_visitor(std::forward(v), std::forward(s)); 282 | } 283 | 284 | // Apply with two instances 285 | template 286 | static VISIT_STRUCT_CXX14_CONSTEXPR void apply(V && v, S1 && s1, S2 && s2) { 287 | detail::structure_helper::apply_visitor(std::forward(v), std::forward(s1), std::forward(s2)); 288 | } 289 | 290 | // Apply with no instance 291 | template 292 | static VISIT_STRUCT_CXX14_CONSTEXPR void visit_pointers(V && v) { 293 | detail::structure_helper::visit_pointers(std::forward(v)); 294 | } 295 | 296 | template 297 | static VISIT_STRUCT_CXX14_CONSTEXPR void visit_types(V && v) { 298 | detail::structure_helper::visit_types(std::forward(v)); 299 | } 300 | 301 | template 302 | static VISIT_STRUCT_CXX14_CONSTEXPR void visit_accessors(V && v) { 303 | detail::structure_helper::visit_accessors(std::forward(v)); 304 | } 305 | 306 | // Get pointer 307 | template 308 | static VISIT_STRUCT_CONSTEXPR auto get_pointer(std::integral_constant) 309 | -> decltype(detail::Find_t::get_ptr()) 310 | { 311 | return detail::Find_t::get_ptr(); 312 | } 313 | 314 | // Get accessor 315 | template 316 | static VISIT_STRUCT_CONSTEXPR auto get_accessor(std::integral_constant) 317 | -> typename detail::Find_t::accessor_t 318 | { 319 | return {}; 320 | } 321 | 322 | // Get value 323 | template 324 | static VISIT_STRUCT_CONSTEXPR auto get_value(std::integral_constant tag, S && s) 325 | -> decltype(std::forward(s).*get_pointer(tag)) 326 | { 327 | return std::forward(s).*get_pointer(tag); 328 | } 329 | 330 | // Get name 331 | template 332 | static VISIT_STRUCT_CONSTEXPR auto get_name(std::integral_constant) 333 | -> decltype(detail::Find_t::member_name()) 334 | { 335 | return detail::Find_t::member_name(); 336 | } 337 | 338 | // Get type 339 | template 340 | static auto type_at(std::integral_constant) 341 | -> visit_struct::type_c::value_type>; 342 | 343 | // Get name of structure 344 | static VISIT_STRUCT_CONSTEXPR decltype(T::Visit_Struct_Get_Name__()) get_name() { 345 | return T::Visit_Struct_Get_Name__(); 346 | } 347 | 348 | static VISIT_STRUCT_CONSTEXPR const bool value = true; 349 | }; 350 | 351 | } // end namespace trait 352 | 353 | } // end namespace visit_struct 354 | 355 | // Macros to be used within a structure definition 356 | 357 | #define VISIT_STRUCT_GET_REGISTERED_MEMBERS decltype(Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank{})) 358 | 359 | #define VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) Visit_Struct_Member_Record__##NAME 360 | 361 | #define BEGIN_VISITABLES(NAME) \ 362 | typedef NAME VISIT_STRUCT_CURRENT_TYPE; \ 363 | static VISIT_STRUCT_CONSTEXPR decltype(#NAME) Visit_Struct_Get_Name__() { \ 364 | return #NAME; \ 365 | } \ 366 | ::visit_struct::detail::TypeList<> static inline Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<0>); \ 367 | static_assert(true, "") 368 | 369 | #define VISITABLE(TYPE, NAME) \ 370 | TYPE NAME; \ 371 | struct VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) : \ 372 | visit_struct::detail::member_ptr_helper \ 375 | { \ 376 | static VISIT_STRUCT_CONSTEXPR const ::visit_struct::detail::char_array & member_name() { \ 377 | return #NAME; \ 378 | } \ 379 | }; \ 380 | static inline ::visit_struct::detail::Append_t \ 382 | Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank); \ 383 | static_assert(true, "") 384 | 385 | #define VISITABLE_INIT(TYPE, NAME, VALUE) \ 386 | TYPE NAME = VALUE; \ 387 | struct VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) : \ 388 | visit_struct::detail::member_ptr_helper \ 391 | { \ 392 | static VISIT_STRUCT_CONSTEXPR const ::visit_struct::detail::char_array & member_name() { \ 393 | return #NAME; \ 394 | } \ 395 | }; \ 396 | static inline ::visit_struct::detail::Append_t \ 398 | Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank); \ 399 | static_assert(true, "") 400 | 401 | #define VISITABLE_DIRECT_INIT(TYPE, NAME, ...) \ 402 | TYPE NAME __VA_ARGS__; \ 403 | struct VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) : \ 404 | visit_struct::detail::member_ptr_helper \ 407 | { \ 408 | static VISIT_STRUCT_CONSTEXPR const ::visit_struct::detail::char_array& member_name() \ 409 | { \ 410 | return #NAME; \ 411 | } \ 412 | }; \ 413 | static inline ::visit_struct::detail::Append_t \ 415 | Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank); \ 416 | static_assert(true, "") 417 | 418 | 419 | 420 | #define END_VISITABLES \ 421 | typedef VISIT_STRUCT_GET_REGISTERED_MEMBERS Visit_Struct_Registered_Members_List__; \ 422 | typedef ::visit_struct::detail::intrusive_tag Visit_Struct_Visitable_Structure_Tag__; \ 423 | static_assert(true, "") 424 | 425 | 426 | #endif // VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED 427 | -------------------------------------------------------------------------------- /IMPLEMENTATION_NOTES.md: -------------------------------------------------------------------------------- 1 | Implementation Notes 2 | ==================== 3 | 4 | There's not much code so this will be brief. 5 | 6 | Goal here is to explain why things are done a certain way when this isn't appropriate 7 | for the main docu, or code comments. 8 | 9 | ## Trait 10 | 11 | The first thing that isn't clear from docu is that there is in fact a type trait, 12 | `visit_struct::traits::visitable`. `apply_visitor` is defined in terms of the trait, 13 | and the macros / registration mechanisms work by specializing the trait. 14 | 15 | Conceivably the trait doesn't need to exist, but if it doesn't then the intrusive 16 | syntax and the regular syntax can't play together nicely, as the intrusive one 17 | sort of needs to have a trait to work. I think in general a trait tends to give 18 | better error messages than just one massive overloaded function. 19 | 20 | It also gives the user a way to statically assert that a structure is visitable. Even though 21 | that's not documented, most users savvy enough to want something like that 22 | probably are going to peek at the source code anyways at some point, it really is not 23 | that much. 24 | 25 | So, I think using a trait is probably worth it. 26 | 27 | ## `constexpr` support 28 | 29 | `visit_struct` assumes that you have `constexpr` -- it targets C++11 and this is a C++11 30 | feature. In C++14 however, `constexpr` became more powerful and void functions like the `apply_visitor` function 31 | itself can be made `constexpr`. 32 | 33 | Currently we indicate `C++14-constexpr` things with a macro, and this is enabled by a preprocessor test: 34 | 35 | ``` 36 | # if (defined _MSC_VER) || (!defined __cplusplus) || (__cplusplus == 201103L) 37 | # define VISIT_STRUCT_CONSTEXPR 38 | # else 39 | # define VISIT_STRUCT_CONSTEXPR constexpr 40 | # endif 41 | ``` 42 | 43 | It would be better if we could actually test for the feature using SFINAE somehow I guess, but I'm not sure 44 | how to do that. There might be a good way to improve this test -- using `__cplusplus` value to test version 45 | is not actually that wise, and this may cause problems for certain compilers. 46 | 47 | I suspect that users who have issues here will be able to fix it pretty easily on their own. 48 | 49 | 50 | ## Intrusive Syntax Details and Limitations 51 | 52 | When using the intrusive syntax, the macro `VISITABLE` declares a member and declares 53 | a secret typedef containing the "metadata", i.e. the name and the member pointer. Besides 54 | this, it uses a trick to add this type to a list associated to the class. The list is updated 55 | basically by using overload resolution, see code comments for details. The `END_VISITABLES` 56 | macro finalizes the list, and provides a final typedef which the `apply_visitor` impl can hook 57 | into to get the list of members. The rest is pretty standard, we use pack expansion to perform 58 | visitation, the hard part is just getting the list. 59 | 60 | One unfortunate part of this is the when we make a list of length `n` this way, it requires 61 | template recursion depth `n` from the compiler. This means that there is a practical limit of 62 | say 100 or 200 in these lists. Currently we code it at 100, because boost `bjam` likes to 63 | impose this limit on template depth by default. 64 | 65 | If you run up against this limit, you can simply increase the number in our header, and / or try to pass appropriate flags 66 | to your compiler to increase the template limits for your implementation. 67 | 68 | If that's not possible, another way would be to change our implementation. The basic issue is the `rank` template, when 69 | we instantiate `rank` it forces depth `n` template recursion. What we really want is, instead of 70 | indexing the list by a single "number" (rank), index it by a pair or a tuple of "numbers". However, 71 | overload resolution won't always be unambiguous if we make the function take multiple parameters. 72 | 73 | I think there might be a solution in which we basically do have a function with multiple parameters, but it is 74 | `curried`, so the main function takes a rank, and returns a type containing a function which also takes 75 | a rank, which returns the list, or something like this. 76 | 77 | I'm not sure, I didn't attempt to implement it. And I'm sure that it will make template error messages much more 78 | complicated in cases when a visitor cannot be applied for whatever reason. 79 | 80 | Hopefully no one desperately needs to support more than a hundred things with this syntax, but if you do, that's 81 | what I would attempt. 82 | 83 | Another thing you can do in such a situation is try to break up your structs with hundreds of things into structs containing structs with 84 | many fewer things, and visit them in a hierarchical fashion. That might be more organized anyways. 85 | 86 | ## Basic Syntax Details 87 | 88 | When using the basic syntax, the macro `VISITABLE_STRUCT` specializes the main trait for a given structure, and generates the 89 | various `apply_visitor` implementations. When we do this, we get from the user the list of members of the structure, but in the C preprocessor 90 | it's not that easy to iterate over an arbitrarily sized list. 91 | 92 | Originally, we used a `map-macro` developed by swansontec, see the README. However, this macro doesn't actually work with the MSVC preprocessor -- 93 | it seems there are some discrepancies with regards to variadic macros between MSVC and clang / gcc. At one point I took a stab at fixing it, 94 | but the issue is rather subtle and I don't have a good test environment, as I don't run windows. 95 | 96 | The swansontec macro is relatively succinct and looks pretty clean, however, even besides the portability issues there are some issues that it 97 | generates rather ugly error messages when it doesn't work. 98 | 99 | For instance, a pretty common mistake is a typo like this: 100 | 101 | ``` 102 | struct my_type { 103 | int a; 104 | int b; 105 | int c; 106 | }; 107 | 108 | VISITABLE_STRUCT(my_type, a, b, d); 109 | ``` 110 | 111 | Since `d` is not a member, we're going to get an error, and because the error is in the code generated by the macro, a compiler like gcc or clang 112 | is going to spit out the whole preprocessor stack associated to it. It turns out that even though the swansontec macro looks quite succinct, the 113 | error messages it makes are rather verbose because it makes the compiler do a lot of work. 114 | 115 | For instance, the stated recursion limit of the "reference" implementation of the map macro is 365 -- that is, you can use it with lists of as many 116 | as 365 parameters. One of the strong points of that implementation is that it is easy to extend it. If you add another line here and define `EVAL5` continuing 117 | the pattern, it gets a new limit of more than a thousand items. 118 | 119 | However, one of the drawbacks is that actually, every time the map macro is used it causes macro expansion to take place 365 times, even when the list is very short, like one or two items. 120 | 121 | The consequence of this is that even that tiny program above, when it is compiled, generates a two-thousand line long error message with `gcc 5.4.1`. 122 | 123 | Most lines look like this: 124 | 125 | ``` 126 | foo.cpp:9:1: note: in expansion of macro ‘VISITABLE_STRUCT’ 127 | VISITABLE_STRUCT(my_type, a, b, d); 128 | ^ 129 | foo.cpp: In static member function ‘static void visit_struct::traits::visitable::apply(V&&)’: 130 | include/visit_struct/visit_struct.hpp:111:43: error: ‘d’ is not a member of ‘self_type {aka my_type}’ 131 | std::forward(visitor)(#MEMBER_NAME, &self_type::MEMBER_NAME); 132 | ^ 133 | include/visit_struct/visit_struct.hpp:81:40: note: in definition of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 134 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 135 | ^ 136 | include/visit_struct/visit_struct.hpp:82:66: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 137 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 138 | ^ 139 | include/visit_struct/visit_struct.hpp:82:92: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 140 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 141 | ^ 142 | include/visit_struct/visit_struct.hpp:83:40: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL1’ 143 | #define VISIT_STRUCT_PP_MAP_EVAL2(...) VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(__VA_ARGS__))) 144 | ^ 145 | include/visit_struct/visit_struct.hpp:82:40: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 146 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 147 | ^ 148 | include/visit_struct/visit_struct.hpp:82:66: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 149 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 150 | ^ 151 | include/visit_struct/visit_struct.hpp:82:92: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 152 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 153 | ^ 154 | include/visit_struct/visit_struct.hpp:83:66: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL1’ 155 | #define VISIT_STRUCT_PP_MAP_EVAL2(...) VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(__VA_ARGS__))) 156 | ^ 157 | include/visit_struct/visit_struct.hpp:82:40: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 158 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 159 | ^ 160 | include/visit_struct/visit_struct.hpp:82:66: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 161 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 162 | ^ 163 | include/visit_struct/visit_struct.hpp:82:92: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 164 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 165 | ^ 166 | include/visit_struct/visit_struct.hpp:83:92: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL1’ 167 | #define VISIT_STRUCT_PP_MAP_EVAL2(...) VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(VISIT_STRUCT_PP_MAP_EVAL1(__VA_ARGS__))) 168 | ^ 169 | include/visit_struct/visit_struct.hpp:84:40: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL2’ 170 | #define VISIT_STRUCT_PP_MAP_EVAL3(...) VISIT_STRUCT_PP_MAP_EVAL2(VISIT_STRUCT_PP_MAP_EVAL2(VISIT_STRUCT_PP_MAP_EVAL2(__VA_ARGS__))) 171 | ^ 172 | include/visit_struct/visit_struct.hpp:82:40: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 173 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 174 | ^ 175 | include/visit_struct/visit_struct.hpp:82:66: note: in expansion of macro ‘VISIT_STRUCT_PP_MAP_EVAL0’ 176 | #define VISIT_STRUCT_PP_MAP_EVAL1(...) VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(VISIT_STRUCT_PP_MAP_EVAL0(__VA_ARGS__))) 177 | ^ 178 | 179 | ... 180 | 181 | ``` 182 | 183 | `clang-3.8.0` gives a somewhat better error message: 184 | 185 | ``` 186 | $ clang++ -std=c++11 -Iinclude/ foo.cpp 187 | foo.cpp:9:33: error: no member named 'd' in 'my_type' 188 | VISITABLE_STRUCT(my_type, a, b, d); 189 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~ 190 | include/visit_struct/visit_struct.hpp:124:53: note: expanded from macro 'VISITABLE_STRUCT' 191 | VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER, __VA_ARGS__) \ 192 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 193 | include/visit_struct/visit_struct.hpp:98:86: note: expanded from macro 'VISIT_STRUCT_PP_MAP' 194 | #define VISIT_STRUCT_PP_MAP(f, ...) VISIT_STRUCT_PP_MAP_EVAL(VISIT_STRUCT_PP_MAP1(f, __VA_ARGS__, (), 0)) 195 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ 196 | include/visit_struct/visit_struct.hpp:97:114: note: expanded from macro 'VISIT_STRUCT_PP_MAP1' 197 | #define VISIT_STRUCT_PP_MAP1(f, x, peek, ...) f(x) VISIT_STRUCT_PP_MAP_NEXT(peek, VISIT_STRUCT_PP_MAP0)(f, peek, __VA_ARGS__) 198 | ^ 199 | note: (skipping 364 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) 200 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 201 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 202 | ^~~~~~~~~~~ 203 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 204 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 205 | ^~~~~~~~~~~ 206 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 207 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 208 | ^~~~~~~~~~~ 209 | foo.cpp:9:33: error: no member named 'd' in 'my_type' 210 | VISITABLE_STRUCT(my_type, a, b, d); 211 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~ 212 | include/visit_struct/visit_struct.hpp:130:53: note: expanded from macro 'VISITABLE_STRUCT' 213 | VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER, __VA_ARGS__) \ 214 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 215 | include/visit_struct/visit_struct.hpp:98:86: note: expanded from macro 'VISIT_STRUCT_PP_MAP' 216 | #define VISIT_STRUCT_PP_MAP(f, ...) VISIT_STRUCT_PP_MAP_EVAL(VISIT_STRUCT_PP_MAP1(f, __VA_ARGS__, (), 0)) 217 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ 218 | include/visit_struct/visit_struct.hpp:97:114: note: expanded from macro 'VISIT_STRUCT_PP_MAP1' 219 | #define VISIT_STRUCT_PP_MAP1(f, x, peek, ...) f(x) VISIT_STRUCT_PP_MAP_NEXT(peek, VISIT_STRUCT_PP_MAP0)(f, peek, __VA_ARGS__) 220 | ^ 221 | note: (skipping 364 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) 222 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 223 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 224 | ^~~~~~~~~~~ 225 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 226 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 227 | ^~~~~~~~~~~ 228 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 229 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 230 | ^~~~~~~~~~~ 231 | foo.cpp:9:33: error: no member named 'd' in 'my_type' 232 | VISITABLE_STRUCT(my_type, a, b, d); 233 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~ 234 | include/visit_struct/visit_struct.hpp:136:58: note: expanded from macro 'VISITABLE_STRUCT' 235 | VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_MOVE, __VA_ARGS__) \ 236 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 237 | include/visit_struct/visit_struct.hpp:98:86: note: expanded from macro 'VISIT_STRUCT_PP_MAP' 238 | #define VISIT_STRUCT_PP_MAP(f, ...) VISIT_STRUCT_PP_MAP_EVAL(VISIT_STRUCT_PP_MAP1(f, __VA_ARGS__, (), 0)) 239 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ 240 | include/visit_struct/visit_struct.hpp:97:114: note: expanded from macro 'VISIT_STRUCT_PP_MAP1' 241 | #define VISIT_STRUCT_PP_MAP1(f, x, peek, ...) f(x) VISIT_STRUCT_PP_MAP_NEXT(peek, VISIT_STRUCT_PP_MAP0)(f, peek, __VA_ARGS__) 242 | ^ 243 | note: (skipping 364 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) 244 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 245 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 246 | ^~~~~~~~~~~ 247 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 248 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 249 | ^~~~~~~~~~~ 250 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 251 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 252 | ^~~~~~~~~~~ 253 | foo.cpp:9:33: error: no member named 'd' in 'my_type' 254 | VISITABLE_STRUCT(my_type, a, b, d); 255 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~ 256 | include/visit_struct/visit_struct.hpp:142:58: note: expanded from macro 'VISITABLE_STRUCT' 257 | VISIT_STRUCT_PP_MAP(VISIT_STRUCT_MEMBER_HELPER_TYPE, __VA_ARGS__) \ 258 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 259 | include/visit_struct/visit_struct.hpp:98:86: note: expanded from macro 'VISIT_STRUCT_PP_MAP' 260 | #define VISIT_STRUCT_PP_MAP(f, ...) VISIT_STRUCT_PP_MAP_EVAL(VISIT_STRUCT_PP_MAP1(f, __VA_ARGS__, (), 0)) 261 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ 262 | include/visit_struct/visit_struct.hpp:97:114: note: expanded from macro 'VISIT_STRUCT_PP_MAP1' 263 | #define VISIT_STRUCT_PP_MAP1(f, x, peek, ...) f(x) VISIT_STRUCT_PP_MAP_NEXT(peek, VISIT_STRUCT_PP_MAP0)(f, peek, __VA_ARGS__) 264 | ^ 265 | note: (skipping 364 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) 266 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 267 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 268 | ^~~~~~~~~~~ 269 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 270 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 271 | ^~~~~~~~~~~ 272 | include/visit_struct/visit_struct.hpp:81:40: note: expanded from macro 'VISIT_STRUCT_PP_MAP_EVAL0' 273 | #define VISIT_STRUCT_PP_MAP_EVAL0(...) __VA_ARGS__ 274 | ^~~~~~~~~~~ 275 | 4 errors generated. 276 | ``` 277 | 278 | If this is the only issue, I think I could live with error messages like this from clang, at least it even gets the right problem 279 | in the first line of the message. But the upshot is that the swansontec macro might be a little too clever and make the preprocessor do a little more work than is ideal 280 | for `visit_struct` -- clearly it relies on some standard assumptions that MSVC unfortunately breaks. Since I don't actually use MSVC though, this issue wasn't really a priority 281 | for me and I let it sit for a while. I did report the bug on github to swansontec, and mentioned that I tried to fix it at one point. 282 | 283 | Eventually, Jarod42 made a PR in which he proposed to use a different strategy. Instead of the recursion trick, we do something more brute-force: 284 | first, restrict attention to lists of size at most 69. Then, make a (standard) macro that figures out how many arguments, at most 69, were passed to a given macro. 285 | Then, define 69 macros which each apply a given macro to their remaining arguments. 286 | 287 | This kind of thing is actually pretty standard macro trickery, and it's known to work on gcc, clang, msvc, since a long time, I've seen it in several other projects. 288 | It's unfortunately considerably more verbose than the swansontec idea, though not complicated. 289 | 290 | However, and this is the other main benefit, the error messages are not more verbose. For instance, with Jarod42's patch, the gcc error message now looks more like that clang error 291 | message, and there are not many levels of macro expansions. 292 | 293 | To me, getting error messages that are ten to a hundred times shorter is far more important than supporting more than 69 entries, most people will never need close to so many. 294 | 295 | As a compromise though, I do place some value on it being extensible, that is, a programmer who needs more than 69 should ideally be able to get that without a lot of trouble, like with the swansontec macro. 296 | 297 | What I decided to do instead is, go with the "dumbest" and most transparent map macro that could work, and which will give the best error messages possible, somewhat similar to Jarod42's patch. But, I decided to throw together a quick python program that generates this code, so that if someone needs it, they can just rerun the script with a different number. That python script lives at `generate_pp_map.py` now in the repository. 298 | 299 | Additionally, we now define a constant that states the current argument limit of `visit_struct` so that people can make static assertions about it if they have problems with this. 300 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # visit_struct 2 | 3 | [![Build Status](https://github.com/cbeck88/visit_struct/actions/workflows/ci.yml/badge.svg)](http://travis-ci.org/cbeck88/visit_struct) 4 | 5 | [![Boost licensed](https://img.shields.io/badge/license-Boost-blue.svg)](./LICENSE) 6 | 7 | A header-only library providing **structure visitors** for C++11 and C++14. 8 | 9 | ## Motivation 10 | 11 | In C++ there is no built-in way to iterate over the members of a `struct` type. 12 | 13 | Oftentimes, an application may contain several small "POD" datatypes, and one 14 | would like to be able to easily serialize and deserialize, print them in debugging 15 | info, and so on. Usually, the programmer has to write a bunch of boilerplate 16 | for each one of these, listing the struct members over and over again. 17 | 18 | (This is only the most obvious use of structure visitors.) 19 | 20 | Naively one would like to be able to write something like: 21 | 22 | ```c++ 23 | for (const auto & member : my_struct) { 24 | std::cerr << member.name << ": " << member.value << std::endl; 25 | } 26 | ``` 27 | 28 | However, this syntax can never be legal in C++, because when we iterate using a 29 | for loop, the iterator has a fixed static type, and `member.value` similarly has 30 | a fixed static type. But the struct member types must be allowed to vary. 31 | 32 | ## Visitors 33 | 34 | The usual way to overcome issues like that (without taking a performance hit) 35 | is to use the *visitor pattern*. For our purposes, a *visitor* is a generic callable 36 | object. Suppose our struct looks like this: 37 | 38 | ```c++ 39 | struct my_type { 40 | int a; 41 | float b; 42 | std::string c; 43 | }; 44 | ``` 45 | 46 | and suppose we had a function like this, which calls the visitor `v` once for 47 | each member of the struct: 48 | 49 | ```c++ 50 | template 51 | void visit(const my_type & my_struct, V && v) { 52 | v("a", my_struct.a); 53 | v("b", my_struct.b); 54 | v("c", my_struct.c); 55 | } 56 | ``` 57 | 58 | (For comparison, see also the function `boost::apply_visitor` from the `boost::variant` library, 59 | which similarly applies a visitor to the value stored within a variant.) 60 | 61 | Then we can "simulate" the for-loop that we wanted to write in a variety of ways. For instance, we can 62 | make a template function out of the body 63 | of the for-loop and use that as a visitor. 64 | 65 | ```c++ 66 | template 67 | void log_func(const char * name, const T & value) { 68 | std::cerr << name << ": " << value << std::endl; 69 | } 70 | 71 | visit(my_struct, log_func); 72 | ``` 73 | 74 | Using a template function here means that even though a struct may contain several different types, the compiler 75 | figures out which function to call at compile-time, and we don't do any run-time polymorphism -- the whole call can 76 | often be inlined. 77 | 78 | Basically we are solving the original problem in a very exact way -- there is no longer an explicit 79 | iterator, and each time the "loop body" can be instantiated with different types as needed. 80 | 81 | If the loop has internal state or "output", we can use a function object (an object which overloads `operator()`) as the visitor, 82 | and collect the state in its members. Also in C++14 we have generic lambdas, which sometimes makes all this very terse. 83 | 84 | Additionally, while making a visitor is sometimes more verbose than you'd like, it has an added benefit that generic visitors can 85 | be used and reused many times. Often, when doing things like logging or serialization, you don't want each struct to get a different 86 | implementation or policy, you want to reuse the same code for all of them. 87 | 88 | ## Reflection 89 | 90 | So, if we have a template function `visit` for our struct, it may let us simplify code and promote code reuse. 91 | 92 | However, that means we still have to actually define `visit` for every struct we want to use it 93 | with, and possibly several versions of it, taking `const my_type &`, `my_type &`, `my_type &&`, and so on. 94 | That's also quite a bit of repetitive code, and the whole point of this is to reduce repetition. 95 | 96 | Again, ideally we would be able to do something totally generic, like, 97 | 98 | ```c++ 99 | template 100 | void for_each(S && s, V && v) { 101 | // Insert magic here... 102 | for (auto && member : s) { 103 | v(member.name, member.value); 104 | } 105 | } 106 | ``` 107 | 108 | where both the visitor and struct are template parameters, and use this to visit the members of any struct. 109 | 110 | Unfortunately, current versions of C++ *lack reflection*. It's not possible 111 | to *programmatically inspect* the list of members of a generic class type `S`, using templates or 112 | anything else standard, even if `S` is a complete type (in which case, the compiler obviously 113 | knows its members). If we're lucky we might get something like this in C++20, but right 114 | now there's no way to actually implement the fully generic `for_each`. 115 | 116 | This means that any implementation of `for_each` requires some help, usually in the form of *registration macros* 117 | or similar. 118 | 119 | ## Overview 120 | 121 | This library permits the following syntax in a C++11 program: 122 | 123 | ```c++ 124 | struct my_type { 125 | int a; 126 | float b; 127 | std::string c; 128 | }; 129 | 130 | VISITABLE_STRUCT(my_type, a, b, c); 131 | 132 | 133 | 134 | struct debug_printer { 135 | template 136 | void operator()(const char * name, const T & value) { 137 | std::cerr << name << ": " << value << std::endl; 138 | } 139 | }; 140 | 141 | void debug_print(const my_type & my_struct) { 142 | visit_struct::for_each(my_struct, debug_printer{}); 143 | } 144 | ``` 145 | 146 | Intuitively, you can think that the macro `VISITABLE_STRUCT` is defining overloads of `visit_struct::for_each` 147 | for your structure. 148 | 149 | In C++14 this can be made more succinct using a lambda: 150 | 151 | ```c++ 152 | void debug_print(const my_type & my_struct) { 153 | visit_struct::for_each(my_struct, 154 | [](const char * name, const auto & value) { 155 | std::cerr << name << ": " << value << std::endl; 156 | }); 157 | } 158 | ``` 159 | 160 | These two things, the macro `VISITABLE_STRUCT` and the function `visit_struct::for_each`, 161 | represent the most important functionality of the library. 162 | 163 | A nice feature of `visit_struct` is that `for_each` always respects the 164 | C++11 value category of it's arguments. 165 | That is, if `my_struct` is a const l-value reference, non-const l-value reference, or r-value 166 | reference, then `for_each` will pass each of the fields to the visitor correspondingly, 167 | and the visitor is also forwarded properly. 168 | 169 | It should be noted that there are already libraries that permit iterating over a structure like 170 | this, such as `boost::fusion`, which does this and much more. Or `boost::hana`, which is like 171 | a modern successor to `boost::fusion` which takes advantage of C++14. 172 | 173 | However, our library can be used as a single-header, header-only library with no external dependencies. 174 | The core `visit_struct.hpp` is in total about four hundred lines of code, depending on how you count, 175 | and is fully functional on its own. For some applications, `visit_struct` is all that you need. 176 | 177 | Additionally, the syntax for doing these kind of visitations is (IMO) a little nicer than in `fusion` 178 | or `hana`. And `visit_struct` has much better compiler support right now than `hana`. `hana` requires 179 | a high level of conformance to C++14. It only supports `gcc-6` and up for instance, and doesn't work with 180 | any versions of MSVC. (Its support on `clang` is quite good.) `visit_struct` can be used with 181 | many "first generation C++11 compilers" that are now quite old, like `gcc-4.8` and MSVC 2013. 182 | 183 | **Note:** The macro `VISITABLE_STRUCT` must be used at filescope, an error will occur if it is 184 | used within a namespace. You can simply include the namespaces as part of the type, e.g. 185 | 186 | ```c++ 187 | VISITABLE_STRUCT(foo::bar::baz, a, b, c); 188 | ``` 189 | 190 | ## Compatibility with `boost::fusion` 191 | 192 | **visit_struct** also has support code so that it can be used with "fusion-adapted structures". 193 | That is, any structure that `boost::fusion` knows about, can also be used with `visit_struct::for_each`, 194 | if you include the extra header. 195 | 196 | `#include ` 197 | 198 | This compatability header means that you don't have to register a struct once with `fusion` and once with `visit_struct`. 199 | It may help if you are migrating from one library to the other. 200 | 201 | ## Compatiblity with `boost::hana` 202 | 203 | **visit_struct** also has a similar compatibility header for `boost::hana`. 204 | 205 | `#include ` 206 | 207 | ## "Intrusive" Syntax 208 | 209 | A drawback of the basic syntax is that you have to repeat the field member names. 210 | 211 | This introduces a maintenance burden: What if someone adds a field member and doesn't update 212 | the list? 213 | 214 | 1. It is possible to write a static assertion that all of the members are registered, by comparing 215 | sizeof the struct with what it should be given the known registered members. (See [test_fully_visitable.cpp](./test_fully_visitable.cpp) ) 216 | 2. It may be *useful* to register only a subset of the field members for serialization. 217 | 3. It may be a requirement for you that you cannot change the header where the struct is defined, and you still want to visit it, so the 218 | first syntax may be pretty much the only option for you. 219 | 220 | However, none of these changes the fact that with the first syntax, you have to write the names twice. 221 | 222 | If visit_struct were e.g. a clang plugin instead of a header-only library, then perhaps we could make the syntax look like this: 223 | 224 | ```c++ 225 | struct my_type { 226 | __attribute__("visitable") int a; 227 | __attribute__("visitable") float b; 228 | __attribute__("visitable") std::string c; 229 | }; 230 | 231 | void debug_print(const my_type & my_struct) { 232 | __builtin_visit_struct(my_struct, 233 | [](const char * name, const auto & member) { 234 | std::cout << name << ": " << member << std::endl; 235 | }); 236 | } 237 | ``` 238 | 239 | We don't offer a clang plugin like this, but we do offer an additional header, 240 | `visit_struct_intrusive.hpp` which uses macros to get pretty close to this syntax, and which is portable: 241 | 242 | ```c++ 243 | struct my_type { 244 | BEGIN_VISITABLES(my_type); 245 | VISITABLE(int, a); 246 | VISITABLE(float, b); 247 | VISITABLE(std::string, c); 248 | END_VISITABLES; 249 | }; 250 | ``` 251 | 252 | This declares a structure which is essentially the same as 253 | 254 | ```c++ 255 | struct my_type { 256 | int a; 257 | float b; 258 | std::string c; 259 | }; 260 | ``` 261 | 262 | There are no additional data members defined within the type, although there are 263 | some "secret" static declarations which are occurring. (Basically, a bunch of typedef's.) 264 | That's why it's "intrusive". There is still no run-time overhead. 265 | 266 | Each line above expands to a separate series of declarations within the body of `my_type`, and arbitrary other C++ 267 | declarations may appear between them. 268 | 269 | ```c++ 270 | struct my_type { 271 | 272 | int not_visitable; 273 | double not_visitable_either; 274 | 275 | BEGIN_VISITABLES(my_type); 276 | VISITABLE(int, a); 277 | VISITABLE(float, b); 278 | 279 | typedef std::pair spair; 280 | 281 | VISITABLE(spair, p); 282 | 283 | void do_nothing() const { } 284 | 285 | VISITABLE(std::string, c); 286 | 287 | END_VISITABLES; 288 | }; 289 | ``` 290 | 291 | When `visit_struct::for_each` is used, each member declared with `VISITABLE` 292 | will be visited, in the order that they are declared. 293 | 294 | The benefits of this version are that, you don't need to type all the member 295 | names twice, and you don't need to jump out of your namespaces back to filescope 296 | in order to register a struct. The main drawbacks are that this is still somewhat 297 | verbose, the implementation is a bit more complicated, and this one may not be 298 | useful in some cases, like if the struct you want to visit belongs to some other 299 | project and you can't change its definition. 300 | 301 | 302 | ## Binary Visitation 303 | 304 | **visit_struct** also supports visiting two instances of the same struct type at once. 305 | 306 | For instance, the function call 307 | 308 | ```c++ 309 | visit_struct::for_each(s1, s2, v); 310 | ``` 311 | 312 | is similar to 313 | 314 | ```c++ 315 | v("a", s1.a, s2.a); 316 | v("b", s1.b, s2.b); 317 | v("c", s1.c, s2.c); 318 | ``` 319 | 320 | This is useful for implementing generic equality and comparison operators for visitable 321 | structures, for instance. Here's an example of a generic function `struct_eq` which 322 | compares any two visitable structures for equality using `operator ==` on each field, 323 | and which short-circuits properly. 324 | 325 | ```c++ 326 | struct eq_visitor { 327 | bool result = true; 328 | 329 | template 330 | void operator()(const char *, const T & t1, const T & t2) { 331 | result = result && (t1 == t2); 332 | } 333 | }; 334 | 335 | template 336 | bool struct_eq(const T & t1, const T & t2) { 337 | eq_visitor vis; 338 | visit_struct::for_each(t1, t2, vis); 339 | return vis.result; 340 | } 341 | ``` 342 | 343 | On clang 3.5 with a simple example, this compiles the same assembly as a hand-rolled equality operator. See it on [godbolt compiler explorer](https://gcc.godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSBnVAV2OUxAHIB6bgaj4QAwgEo%2BQ1AAcAnsTzAEBPgCYADLQCsfALQr1AdnEI5DIpISZifAEKZkAawCkqgILOXvAQBE8puQCNmAkx0PmYAOywrAgsbVFRTPgBlVAAzAgB3AENiTD4AGTx2cIZMUj4ANUsGPFRwvloAOlVGwSTMPKzkNABbSSzw6Txw4HdPPlS8ABs8/IBJIWwAOSTsPhI%2BNBk%2BLKVFAkkQXgyTxv9400aSYG55xZXsAH1aR5aCAA8CEXcf5QBmPCpSKYVKVOZJOYAFUeSUhACUAKpCaEACQACmjHnMlkJ8givNgvL8/lhJuE8hVwVCYfCkaiMVicXiCUTXGMAFSc9x8dl8NHEVAANzwWAYOwmXWmhGkfCIfCwyCmOU6fD8zGQBFYnTFjmUymFNQIWX8M11yh2kR2kkkUxlWT4BsIJG5vLlhFakNiU18SjSfB6mB6/mqfF84t6kmmmG0BDwAflu3tao1WvKA1CMUseTD4VQLr4xAiMbjeSFlgsWXQjRd3B%2BbP%2Bw0VzCwfEcfyEQSlBGkbewxMbU2beTbQm7kkwjwIxCyhAYvbrHn4ADENgBZJIVIR6Wh/VXMa0kAjlDJ5SRBTZ1UyYd6SKzBhDDUL20nDTDVtn13eA4GgykQ6GwoiyKPEIADyKyQtgAAaaJwsSAihqCEAvuSoSPOuQiPBU2BwmIuoAGwEYI6FJJh2Fwq27Ztl4DQAByqKo3yfghAgoRSVIAbSwFgRB0GwfBAiYFMpQCQhbFgv%2BNJAdCPGwnxFFoCUwQ3sQomYJEgLwep%2BCpAunguOkljiLq1gmbQAAssqxFk1q2o8jpEFYqQRBqtT1MgAx8MGF5Kdet7voufAAOp5EaxDAJgShCCZZn0F554MOqCATC5saXjsuSylkwARaEf7UoBdIgVBUEWSB4FyTBcIBfB36khJBVcTJJVlbJkFVaJgKCMhIKvmhGFYTheHKIRI3EQN5GUdFfw0bQACcDEiHh%2BjRStghgGAbFoY8yA2swDB7Qwy2rVuECPDth2HZRRIzdutCqH8%2BRMW4/wsfKvXkg1nHScVpXmeVvEdcxgnCZgomsR97GSYV3Etf9bXyT5V4qWpGm6cx2maR%2BLjhFkAYHV0eQOY8yZKI4%2BimdjuP4/07CytOs6thTen8PycY5DKwR9EqwR8BkD7IMlYYHXYeBZN6ABeISyqgBaYMAPpGfaY5g64XM2rsw7tir1N5JC5Q63jeTLC41j5Gs1EOqgIrzq4pMOr4hDGqafyU24rieCiQnjtEgYa7z/NFMlgt2PYYpdcr0jjqGYq5ArV65Og7jqzzWujlH6lG3w%2Buyhnut8CbZsW7dgrW0nfx9nbU7qkovj2Y7RomnkICqgQ6AgCAqTi6Uk4Z0zpn6DdbvJ37qdTYb8aQrbLj23XDnO2D7b69yb2r2vufjvnpjtyAmdN48mnawzBAMB389NyOU8V2f4vMIv2Adyr098C32%2BP4WE4q/35NDyzfBe1MH2spR6az5gLZKuQehljFMgQUFpQi5FSFmcIdNUgCh6OKL%2BEAGBZEFJgMUTB4xjmGMAZ6KdQEjgnnrae9tFSYE8uTN2AgVb1Soa3HekCyw7UFJQvOWc34gE4Xgx4iDkHsEvr2R%2BGdJEgC/k/V2PxB5tmHmrEBvNeGbyzlfSuLh9okM2DMAYk5rob0zvGOhAwJHX1kdIhR2NPCrmGHgbQeDiA1DqOsUEAjehQPCL3aOEATzrHCLaOWABHZgeBMrRWUKZWJtBaBkLUWnNhOc2EIhodXDUF4ei%2BP8cOZmrhmEZ3qgqKYKsIDVy1kudhHcymCnFlY7AEAxCvzbnUuwUwGlTBHBkiuLSxDyOHkoux7s3CDz4J4bSfBdYEzplOGcJ8/6QmyhMDYRpgDyl8P0AggsR7cwodrPhk9MmFmySrHa/cwg1BGKYkxV9rBM1/vY/gLhuj4KYNEDOLdnIoLSvUVA/gqB2CUOpDykhEo830faSQ1twjBGIDGVA2gAxBksPs/2KTjl5FXIGNEU5yi4p6PiqwkgpynJrjsd5DBPnfyKcAg56ijmaJORXFe%2BVvpFQRlVHYQRZZSEsLsEgLSICQlbCNIiXwkZKG0L2d6ioKkCNSCQbIxBy7Il7JUkQjR2RkuIMtJhCFciamIPUJVKqcjqu0VqnVerlErx/oo55YzeBjH4AiUoiLhgIq7uIj2tYXl8DhJFVg9RwjMDRVYP0kwhLoHDvUe058ZitzOQQDFY8NFmLyEkaeHKpJcoqu1WCtSQA1ClsYmNUx0A7RYPClpijDXGtDfTRZp8QBJsXqOY%2BbaLF%2BLTe2HNbLrGVurWgCI/bhmsjGeQpl6cWXZtzRxfN3FC2IwEWWz%2BEw8CxpreOiASRxWjXwgauWJr6gjt3fCkcg7mnfFdk8hcbqrQ2mkPXQ0GwIB1DyLPJSAx2BJMZVi%2BdyQDbYsqIu6GTVfqtVXTyrIfLn12QcsKioh6iKCnKAegiREjo6GfqYre7Td6433ofaK9L14IQWbODuc8G4L0od2juvbJzXvnNY7pd8V6UZkU/NkhSXDMKY%2B2%2BjF8j6tuY4YvtbGh0PxADZF92CiPKuIKq9VFRNWChEOUc1qnLUyeaUdO9k6/4KaQw3D9mRZY/tMH%2B/BAHMXjzA0kOKbCkjKFA8BjTbLXB5phs1P6ANKrFvg3KMzr7kPEAgKh7DY0MPJFoGhsaDA4ruaS/hVUygxCyp84Jgj/CiN7xmAfXSVEKOUZbTRkAdHDQMbK3lirVHhMsf7XOrNlWT7MdQLkuo%2BTr2peULx2xOjGsCBkZx1WDX15DfHA2leejbk%2BN61h26bDqOdZQN1vJ8ihAucw4N6xQyV7rbbR2xjEmUBSdY%2B2Jbfib0yPC0pneKm1Mjm880rTpBuOjZ%2B790bunXsDtoJqlL2nvt/Yh5DgQAP9MDoO4ZrLxmnV/xU48ehgtBAuHKZYXGvMGDSHhVkd4ayrDhbfU6MgctXGlDFCQKIniMrAAjepE%2BDmM3MvaxUTz7X7u%2BaXf56D8NYMhYQ6j9HCB93pdVOUGLEq4vZfw2wgRRWJxkfB%2BvE7tGGDk8bi7LtF2WsGfG7fSbjWZuTcYcd4TZ3xNVcNwO9jcnHsw7VW9zT2mS0vdh7tkHS17X8anU%2BsXXRkoZEIMlKzKaa5alDL%2BlB%2BD02HLa/nPb%2BX4zue5/nd77g/NQaEHDILRaKKhdlsHwW%2B7EuxYyyl/bUuGAecqFLrTeHcvFOA8rkjxW1flco5r6r2ubfkam41vv9vk9Z1H1t3rO3U/ufN/hs3HGTfq7evP/jhqFubNuzCExa3mtT77bY3bA35%2BjKExdwfo%2Brute3/d6xzvlMWtd%2B2d7EBPsr6h5/irLv1UuZB4kr7HvL/YA9eH/a9eHbBRHf3cZQPf1SoBuKyUKDOMUN/BuEmLJAgKRccHaEcRoPA3sMQP0TMOWeOBFaWVFYMNxRPWdNzTPLObPPnSDH6fPQLblEXOUYmFWBgaLJvBXVvNPb9QrTvVXUrIfH7PvGrJ2MTfXO3a/I3JfQcU3CrNfcZQ1PvS/ZrOQh3WTM%2BNArgp7DuL3Z/IQV/LTJHAPP%2BCoBA4gigoyWFb1EMCAAiJIDuLIQg0EYguORWROf0QMSgucVRQDJzYDJIOg%2BMBglwXPZggvNg4vBDYmew%2BFaoHgqvB0PgkbAQktFXErEcD/N6CQgfUTPXK/ehaTbQvsBQrjIA1fQ7aRdfK3C/IoztEooxVrO/OTBIuFBFbgsAl/d3cwmAyw6w6yalT5FA35VyDxQFYFDUXDIg2ILwhOcgvw6oagoDHnMIikCDRqaI1g4XOIjgtArodgGlEgbg2XI9NIlvDIpXIQheHI%2BrcQ4TSQ3XZozQ0o67H3HQ9tZfaoliFQy3eldQpo87WQj4tox3XQw0R4Y4j5M4gwzuJ/dTfo6Ax1bGJ9FwWyGUSLQQMPGIGZWWYYWzePI6J9T0MMDyKYYSB2aExI7o8oFTLyLoewNTGBbrHZOWLIJgEoNY4IjYzIiIqIrlGI/Y3lMLLEnXFDXg6447MDDve47vYfDXZ4wo2raQlosor4iojo34pUmouTPjVQleYmUmKEwgR4Ok1Y8ohEow5E/pMw1EpRdEuAgAcUigdBNy8hlEfGvEEG9HsEEJ3giiUEZM1BtHs15JHG9VDHQHeE2OSG2M5RXUBnYNlmDMl1SNwxyxuLlLuNI1ELyJYgKJ1zqxkI2zH3aJvkUMAL1NXjKQqWBLVOKPeNaPkLk2DPshNwRIcOAGnCmBrTj1a29XKBFHeHnGZkHh00fz02MJvWwSWmekXwNLqKNPpSbVNQ61OxBNt3LK0K1JkQ7Im27KSN7PFgHOJKHPhRHNjPHIHi8CnOeyRIM3nIGLRJdX4DdKUHzmjW3SrXKH8G9OBDHMCMcyjPhRjLjMyN50iP5zz1FJLz4A7N1haRlKBNzJ3myMVKeIuxeNLI1M%2BMrJ%2BOrMLLEk6QbOt23LLJ7T3MIqQqNmPOCFPP7MUgvLAqPAgtvJ/j9z%2BIQgBIEwEHXPqEbKkObINxoshJADooDAYvlj7PPKNCvXbGHI4rZQnK8FfKdOnWSSmmjNHPjOgqFOTOCwOLTMikeGQqwzl2PWuPlXKQzmgDMt1ijNjMw01SWjpTy0EsQscqNmcsgrnI0tgMCk/N8MjT4EtMpwAtjywGApcBnTTl0pcqgsTOXRkngoQw7IipQuzNlPbzzK7wLJ4qLJVJLPVJbM1MIomxrJ%2B3rPsuEteNBN3PBLbI7kyq6MsBkqYvkoGEvPYtHM4sHm4trN4tqNm3qLXJDQ3PqrwvKoIokraocKiwER7LkpYoUt6uvLHNUrvMCoXHip0vAr0uSty0MrSpTJMu8oIAtPaqi0sqPXSNsoqQWqSNUiUqSoCoNRXi8ueoRT8tcv6V2sDRCtsNJ1GJIH/MApisjKUsOqSrcxSoF1iLFNMquthNOKi3SNyva3lPzNyKKqaxwtVJEreLEuavKON2IvxohgVTqooqbJJrBNbPJusQ7LRs%2BU6tWsvHWrYs2oGvUsXOUNGot34tPWbWmrKtJqZv3JZrMrZuFWWpPM5sHJ5pUr7DUsBq0qCLYogv0oRrgvOuRsuphLBtuvSwetqvHActRpNr%2BoTIBs%2BomrPSNrlteqECOoCsdKCs8BCpVghuiuvGhrdthsgvhty3tguV2BJg8oEE31MUuUtgtswEqTpuJsauorJuluXOwN2A5rPLWp6pVv6u2q4oGJjpuU2S/ktjWwzh2iwMXhM1GUDsSpDuc2nljojrJlWzAw7pJltpvTrtRNdPdO/K8QwK1EDtDp0VOsL0RgQqkqTsxrQryowuEIeLENG2LI0MloqokqqpIuprsstvFtEsZp3u%2BPnoGRIr4sbUmqEpToap3PTqltop8uko1ontbpOtguYPSrlAvruoInNrIvsvnufPcsBM8tvqNqcutPfo9k5HZHzEhEJO5kDBZ0QK%2BlNnNlSv9C6AFGPDyHJGlntB6BshRTwdQGPHAU2E8iNADPzGIJHvFFIeQAFHgVVG61LEzFJ3CmZ3hQYDTEtHCysl2FwdYdljlHF3WG4cZz4YIACh5CQYsEymVA4aITsAQHCDwAiXwT4GkBYBofqH2jyDxIfHqC3FvEwFvFQBONpTC26HZO9AYAjwfAIXVGQEbAIFtGqt5CitKAIAIFuX3HFWUFyGQFYBqDwTNDEYFAYAUcQaKV5GsHPBiApM8m7llg8mMdDCUD/rwDwT5jOLyEsAFCsHxhwQijFH5nUjUcijMc2WAFQF0YyAFBGHifzBCgykIdQAyHlDhU2UIFj2YZIGHD1HQAjX8DNH/OYCpMimsb9GyBlEDgxwQC5IwcrAaUJwihEdrlrgCME15CgUylhWIFePYYVjwTFGDACaMhKY2HKeynwXib4BXCsHwAYHCZpTclVA6D4DmFXDRHNlXGWEhBcEhDmHAkeCWFAkgiSEaB6CTkSaUbyDQBbGDCmB6ZjiZNKFCA8RhV2AxzQW6z4AACkcgMBzIG90wsXcxemIpyRpxghQgoqYVpAYgPEPm5AyVnnPRTxWBYVSgGdiD6XBUmWLwWwww5RxJp6GRVwXA0QdgxRRRWG8Bgwqwaw9IEG%2BA3SGXNZQhUW8gEGA0xlbNYxkAvpUqZ6eU1rY8lBSH3hSrisQadRbp8I5pB6XoSRIYLWBc%2BIXAlgvAIB3gxBYrdQvWyQoYdiioGQXA4QXSoWIBWwNB17V5nhyhHgG9Hg/h03zJ02NB038J039B03aJ025p037ovtk2P9ng4pnhM2dwK3c2%2BBnh82W3aBC323i323S323y2W21Aq2U23oM2629R03/gJ3m2M222M3O2M3u2M3e2M3%2B2s3VAh2a2/g62/hM2/hs2W2/hp2/hZ2/h52/hF2/hl2/hV3zJ13HBq2/jHhzI63KWc392n3p3zJZ3zJ53zJF3zJl3zJV2NA72H3azHgNA62NBM2NB32NBp2NBZ2NB52NBF2NBl2NBV38JQPh2WJHh8I638JM38J338Jp38JZ38J538JF38Jl23Xyglhyg8DGgxAlhiRpXv7o3MQlhY2XSIAWOxBp6/WA3osuPgIY242E3zoKgXBHg%2BOkhzpygk3cOEIGO%2BA6PygaOtPO2KOtPm2SOtOG8COtP12xswP15MPyh0PrPu3kPrO234PrP93oPrO4oQPlP73VOBAgPygAO/Pu2f2/O23zJm3zJ93X2%2BBn2/OzOVOP9r3yhL3Evu3T3Eu23D3Ev92d3Eu4o/hYuvOP9lB%2B3lBe3lBu2RpyhlA23lBm3J2VAG9lA4pB3zPvOGh%2B3aBe3aBu2O3yhNBevm3G2GgG8Elev8uLO15%2B3e3u3O2QvEvKvevyhGJnoOQZkOhQhrwFk%2BAZg8EpgZYSc6YNvpw%2BBMBukeRjWw3OOmCC0cQwXHgIAshSB/AxB7Qw2w2vIOPvXp6eIhAwX7vHuhPxOzqfvoQ/unvH1/hLuo3gJ5XAWABNJcVQCAVIZbiHz7wH%2BTjEfIeH2gJH0gZ4MQVIM6RJD7iNn1qDGHrHpcZQXHtNjNgnon%2Bnunkn18Mnn6Cn%2BHv4Gn%2BgDNvHv4RnxJCYM6LLQXrNlH8Nln6e9npccyLnvHjzLNvH8yfnxn4Xwn0XkXpX5nz6SXzH%2BHjQWXnnhXp9vHjQZXkX1Xs6PnjXxn03rXyNpM6EKX/CA3%2BX7N43iDvH6ytXgXtXi39XtXpXkX03kX49O31noqKX/QF33nxXk3z3vH/QM333xnq3gPm3xnr3s6RPsPnXuHpcWiaPo33Nj3/DhPvH2iJPoXlPxnwPtX4PtXzPx4RPkXivnP9HqXuaQvt34v/N0vpv8vvHuaSvunkX1Ps6Wvs6evs6Rv5vtXivkXoftvq76H3Xpce6Lv2Pkvwt/vx4Utx4ct54RiEXn3qv0fmv9PkPxn2fs6eftXof4/xiJfqHx31fhJDf933v7f4t3fwfvHyt2t4fn7zH5PsL%2BDfK/oz1v5nR7%2B3vI/t72J6fhIeDvDHnn1oDU9Ugf/OXjHw/7x8d%2Be/A/v/xG71tAB1fa3kHwz7gCW%2BjPaAUT1gFE8T%2BRAp/kgKl47h3%2BPfHAd/zwF/912tbP/vLx3DECz%2BpAuvuQJF7X9d%2BVA/nrQIAHH8/e/AxgZa2YEy90BtPV3pv0/5l8f%2B%2B/LgX/256oC/%2BbvCyAILV7ACJ%2BEHEQWrzEGQD9%2Bkg/nvQNQH89gBhg%2BQQLmYH69lB3PVQdgL74cDf%2Bh/HQbwP0F/9i%2BmgIwZb3P5kDL%2BogiARIIf62D%2BesgxwaYJCHODyer/Z3u4MwFF84%2B3ggfloL8E8D62gQ54MEN74dtQh/vcfqAOn4UC5%2BMQmAXEJkEOD%2BeSQqfs8FD4IC0ey/F/igKj4ZDDe3fbIV/1yH4DuBhAvQc8AMElC/%2B2/LruUJMFVD8ONQm/nUJoENDveCQ5ofz1aFlDj%2B2fDoaT1z6U8OurAwYRoM4H5CxhfAyYX/1KEzDv%2BHXOYeEOEGRCLB0QhfjYOP52CNhx/Fofz0b6zDj%2BrffYRL3b6v9O%2BfQzwWwJyGaCRh/gwoRMKCE3Dphf/e4Xv3miPChBk/cwVnzeF38PhcA%2BIU0J%2BFbC/h/PMQQ8OP6L9gR2vUEXnzUAnCt%2BZw3wQQN0FXDERrbZEc8FRF/8D%2BagDEWnwiFgCohlA94bEM%2BGEjj%2BiQkkcf3%2BHkirB6I83o/2pH28FBq/RrgyPUG4DmRow1kUUIshIi2hKIv/miN5HcDGu/IyoYKOqHCjahoo%2BoeKMaGSjNhx/bYbKP57yjqBGbKQeaJSFs81RaAjAf0LUHsDhh2ggoeMMbbFCDRPXLkcaJ5Fy8zRHg2QSrxIECjnhQo14SKLxFiiCRjo73lKJdGkjdh7o/np6L5Hm96BeoMXogNVF0jOeEIrAVCKGEwiwxlwvUVMMNGxjngJohMXLyTFy9HBKYwQWmKxEvCcRWYqAfiLoESj8xzo73q6LJEljKRKvb0ZWL97/BqxnQ5/sgMp41cNRIYlsRcN1EIioxHIzsV1zjHPBTRfYuXvL0nYZskhQ44wU8NHEZjxxto7MfaNzHrCiR3vX4TKMXGAjSxK4lXmuJV7ACaum4g4bSN3FuDAxkI04VqLyEsiAhJ4/UWeJjEXjux8Yr0TeIzZ3i3eNXOXtsKfFhDMRZgscU31xGTicx04vMUTwLHziix3vOUcBMVGgSVe64iCaYKq5QSQRXQncfDxGj7joR5wlCfCMjHoS%2Bu547kVeN7GjtbxA4uXsXyq5y9/hpEioSAKtGLCbRywu0asIdE/inRxIwsQBOLFATlx7EisZxPAkq8eJrQkaHxJpECSpeZXESc2LEk6jUJkkjsZhNknzR5JjXRSRm0IkqTe%2BFXBdhaK0npjrRmYj8TRK/F0SjJs4kyUxLMksSlx3vMsauJsnm9uJKvByY3zK5OSVRLgtUQXwbFZDGRSE2EeGLZGnjpJfky8QFNwkKT8JSkh8cRLUly9v%2BJXKKaYNaEz9qJ1g2idIOSkMS5xRPBceZO94eiQJ1k83lxLskFSVeRUsQX1N9ER81R4I%2BCY2MQk%2BDkJXkiSdcIwl3Dmp14tqeOxCnKSupc7HqXLz35Fd%2BpCwoaROJGmJSxpRPb4X%2BOlHe83RFkrKfNN95gS8py083oVJV7rSrBT0zaSvzz55d3JTIw6XCIjEnTGpZ07CXJNalBT2p10zqTO26kLsHpcvA/nl2enaTXp8U96QZO/FfTfxRPf8X9MAmzS2JvvHKYtNsnm97Jq0yGSr2hmeiyZsM7oZTy3aIyaprY48T5OjEYyOuOEwdpdIIk3SCZd0omUuxJm89uBW7cmTFJ0lxS9Jn4mmUlLpnGSfppkpmTNKJ5zSrJwM3Kb73yngyeZ5vKGSrwFlSCtZQswSUuB3ZiyDptUtsWhN8kyyex2M/sXjKInKyIpZXYmSuw1m89ueO7bWa%2BNinvj9ZCUw2Z9KIEmyGZv0onv9JZmWS2ZHEjmaDK5krTzea0vmS7JT5uz6BCcj2VLz3Y%2BzQxR47yWjNuFGjMZLU%2BWTjKun3jw5qklWVHLVkxy12cc3nq70HHm9UxlonWZTNTnUzD%2Baw42SlNNlpTzZGUgGUT2ylFzfeS00uQ7PLm8zze/M6uSn1rlAC%2Be9c1foeybmHjxJqM9kejI7myysZ3c0OX3LCmEyh5JXdWaPKzbxyJ5vPR8VPOHEzyk5uslOeIP0mLzDJy8iaalKmnMSierEguUL3Zm7zOZvvbmYfKdmVzzerss%2BSnwvkp9Neyo8PnDJFlwSVBe06qb7IlmtzH57crsS/K7mJjgpH826ZHN6mPTSZmsgBbzzd4Zcs2JEkBc%2BPImDSlhUCg2TAtpmZyV52cs2bnOZmWzWZaCneULz3lYKy5vvCucfKrmj8a5RClPsAMPYp9beZCw4Rz3SG7SqpmouhS3OOmMLORWElhRdJ7mKz8ZA8rhdHKK6xz/548gRUAt5699T2iciiW%2BKolvTPR90JeXIvgWrzEF6U5BZlK3lAyheIMu2WDN94QzcFei/BafNH7nzjFJCsxSn3aGesaxZU%2BGb0JsUDDaFzc%2B%2BfVKklMKXFwct%2BewtCmcLt%2B38nhX4q3YBKs2gi4vse157b9z2YSiRbpKkVpyZFRsuJRMMmmtskFXIlJVeLSWjtbZQve2dksdm%2B9nZ%2BSgxYQtH7ELR%2Bpg49mUpT57CKlW4pgdfIqm1Lgxok7USjKaWBzn5bSthbjI4URzul3C3%2BXlzHlZtAFWbYZSErGXf9L2Ey7EZEqpnRKpBCSGcfEoUVrylFFs7saoq9HqL8JmCoXtgp0VHzfeJ8w5YUqMWj8TFZy1oaEtH5iDIVV8%2BGTtOoW2KDxnkl5e2OlnvK5Zny3uZ0p%2BX3Th5viv%2Bf0qBWBKQVwS0ZbzwhV79r2UKyiZYJWGzKM59grOcUJzltDlF6K1BZioWkYKS5Wig%2BfityWEr9FxgwxccuKWnLSlo/RvuMtH5WDpVdKynre1vksq6pbK06RytflcqPF/c8Kb8p8W8LAV2XQZUEuEXiqs2kq3ngf1vYyqIlcq6BTEtgXzL%2BBCCpZUkpWWby1l1s9JZspClZKheOSvZXgt94EKSVZqslSUtH6UrrVNKu1Z6OjUOr4ez7Z1c8tdUBz2VzCj5XhO5VKyvFfq/lQGv8XCrg1oq0NVm3BW88pVUa7gc%2BxjXJyYV88uFbEqVXyKVViitVWiopGAys1Gy4uZkv3k7KcFhavJcWoKXGCil5ai1ZWouXUqU%2BtamvlIJnUNrpeAYxlXUrsUNKjpD8hqS0v8luL35PK3tXyp/kjyAVg6oNXuxDUjKx1EqidZGsV7TruelLWdRAvnXTKF5Ca2RcuqRWrqUV66jefnK3WFztVGinFQ%2BO0VC9dFRqg5SaqOXGCTlxgildeuME1qU%2Bdah9fQKQ1Prwuza5Ga2qlnuqO1nKrtd6s/mDy/lIGvhQMog0jqoNqXcNbBqzZTrFeiG%2BXuF2Q1zy0Ni6xNVhoWUpqQhaagEQRtSXbrzRu6rZXmpna7Khe%2Byk9cSrPWkrjB5Ky1cYOrW3rWN96mvhxr95qauNSgh5V4I8ktr/Z/Gp%2BYJs9XCaOpPqr%2BeJoFWgahV4GoZWKug3yas2k6%2BDcpsV6qbu%2BwC5PqAuingKNNVgrTZhu%2BnIrEl685JRmoVFEabZZm3NfuvzVWbIpx6oXiWvs1lrHNFa4wVWsuVubR%2BbGzzTX280mDSF1y6CS5NX5fseNfsyWW3OcW/rAp/6ntb6qA29LBV/C4dUItk3jrUtcGp9ghsy2K9stivERblrEUjjwlc6uNdIow1zKdNyahJamoq3pqjNmamrdmrq0biGtlmw9dZqLWtbT1lvc9Z1svXdamNlvFjf1o80a8vNNfEbTX3MVjb%2BJ24qXj%2B2m30LHF36%2BbedMW0dLlt0W/1X0o23SattYKmDbtsU3pan2Kmo7Yr2L5ftFe6k0RWRIu2TK9ZmmqcYqtK04bytqK/DSos1XljatOqvdXqoPUGqj1VG2zTRtLV0bzVDG5zZb1c22r3NGvdjUNrh019TBX7GvuUou43Laxjqmpa%2BseWBbeNwWubTJJx0hy8dnilbarOA2xbJNQ6knaCrDXnsFNCXfbRlqfZZa6dJ2xnYr3JEaT5hFMyRUVs50Ir6Jumx7fpue2GaBdhGtRcRuxW6rcV5GudgSqF5EqZd7WuXReoV1XqrVvWlXVDrV2DaNew2rXQjt1018rl%2Bu8bSjsm33KTdAWpGTNoYVY6rdncv9bbqi1ibCd62qTYltHVyaPdFOr3bex92vsn2x2p9r3yC5Ptv%2BAHdTeHvlW3aud9MnnU9r52VbXt1W5PcLpI1p6yN%2BqijVnqXbGrLepq/PSDsL1g7i9N60vcYIG0w6NdGveHRr1aE/s69NfIEUjuclN68%2BQHdHQ4q/XNLsdPe3HV8oA326el/y53Qlsg1k6UtSXSnd7up2HbZ9/u%2BfYHqX2K89%2BQHVfVMoj2jSo940mPWVp314a99ie4ze9p3Ui7zN323ib9ua1S6AddmoHQ5st5Oai9LmkvcYLvXl639lezXRr213f7G%2Bf7P/TXypEAHSpqQvPiB1AONK3VoW1pUJoVmRbRN3i/tUTuH3IH3dEa9A1PswO%2B7adH7APU%2B235/sCDivA/iB2IPs7SDH08g3AsoPb649u%2Bl7XQbe2H6PtTB%2BrWLsa1sHipLWldoDv/k8HBlXWy3j1qf1CHVdAfdXWIY/3V6v9tejXmIJX0a9PRThp9ZB1UOfrXl7azQ%2BFu0NhzdDfax3QOvi3ArSdxhz3UpvMMz7wuOBhnTYaD32Gn2jh7gZB2cOQLXD6c9w0msYm86aDvhjVUnq1VH7U9ou9PWfsz2Grs9V%2BtdrRst70bLejGx/cxr60v7odAfWHekYkM16Ne0hnI1YKINB8pBAxwoy%2Bo8E0L31d8ko%2BoZ/XW72lMB/HQPv0ND6XdI%2B7beTrQOT6DtFh7A1YdwNdH8DT7Qg30ZN7c9oOgx1DcMYVWjH7t4x6gzsP53TH6DARxg8foWOn7xd5%2BlY5fuo3X6NjQK%2BXdscV1jrBDlvYQykYr0B8q9pxzI%2Bcd/0a8rj%2BR24/QMROFH6x/mpse3ox3gG3lYW1hRFqqNdLVtCBwNQ0bd3Jbx9wJlo9FzBPtGITnRxfXYZhMOGTe/RhE/L1g5InrtMyjfWie52GC11WJ2gzif8OzHAjBJ5gyEZ%2B0S6/tERp6VweiMdbeDcR4ReDvDX7HLer%2Bo4%2B/oD6f6A%2BUhzkwH25M29eTNvP3sacKN%2BbW9wp8WWAdKMCbyjkpyo98sA0O61tcW4nQCZQPKm0tGBtU20bn1anbDy%2B2E/qfhMm8jTbveDiaeGnFa7tlpxmT4YT12mD9Dp/E/MedOLHiTyxyXasfJPrHZdmx6kyCtpNUq9jz%2B4M4cfH7HHwzGRyM1kYD6XG5DcZm3nycTPADWzhRqhY8aZVPLzds2pxd3tcXQHu1dugnb8aLOGGZNpZkwyCen1%2B7NTC%2B2sz0d84QcDTTZk3i2Z76nbT%2B52sBZdpQ2mn0N8Kr4VvqtO4abTUxzdbiYHOmagjX2l06wbdPsGJz0uik9OapMF6aT/BpXfSdS3JHx%2BqRlk%2BIYD6SGtz4/Hc3kb3NB8DzNvI8wNMR0N7kdty5Q9YtTP7SP1rKttVmYW026vjD5n47UYMP/GjDSp986qZp3gnQu1h7U3Wb1MAXGzEHZsyb1Asm8mdZ2lnZBbZ1DH19cFxFZ4cQsTHkLvZ1C/aaF2OmhzwRkc6EdwvhGODkRr01rJ9OxHQd8RgMzaqSNl6mToh2iycfotnGA%2BFx2QyxZuP7mEzHFm3lxZt567Uejevi5T1Q7FGRLIW941AYkv3n%2B9ehmS38aQOvmmjE%2BpS1gY1OqXIT6lv83CYg6GngLelk3r33s4Qdg9zOzSQNOhUwWOzm%2B5VdZcxN5y/D/Zxy4OarEn7IJSxxyaSb6mTmyZRFhOSRbnNkW6TiRhk1Rf23Mnx%2BrJyK%2ByeivRnx%2BsZ%2BK2xcStB9OLNvVochxt7170rvFw3Xrxb1nm31zKoLVea71NSCrnxoq9UdlMSb5TIqxowpeaNU7KzX5uqzWe6O6nejDZ5q0BYg4gX2rBlk3t/3Q5tmolke%2BC8Ne7OTG7LVshgxhadMuWiTblkk%2BObJMEWpzeemc2tdMUbWFzEOoM4ppXPU6wz4/CM%2BPyjPZHYrAfHkwlaD6HnkrN11K/dZt7/6eLgBzK3rwZVvXTdIpjM28cgO3nCrImmUwWblNgaFTSWsfYpYhvKXar9On87Df87w2tLiNnS61Yg76WIO2/VDibz36YcsbsKnG5ZYe1UHvDBNlBTMcmsk3nLWF1y66cpvunPLnp3Pdwd8t7s/T5y3YyzaXNs2RDoZtI%2BubZObmOTfNrk7ufOt192LV10W0H1uuN9UOktm3vIeluKG/RefbDjlb42W6fratv6xrd5Va2gbOtkG4qf1vg2KzRt6s6behPm3/zHnK2652RttWIOHVh2xjedsH9sOrthde7ej2e2vD007E/ZYmvoLSbQd8myHbHNh38LnByO96dv2%2Bn/L/p%2BO4GcTvSrk7q5zm7Po3M83GLS%2B/m%2BP0FsXXhbSVoPilaD4l2xBmNoPp6PntPqCOddi3decbudrczsBx86VefNyWKrYNqq4bZqv928DOpoe01cg5I3YOE9xDmja6tO2Tec97gQRwXsc6yDuNldSNe9u2XfbaF/2xkuHO72cLodvC9TaPuEW6bxFu/aRYf0CGtrlFkK9Rb2u%2B66L4/Bi5ne3Ov3ejrFvO5dbr7XWi74toPv/asEu2Q%2BUgshyA4eOZD3rF5jvZjogM3moH7inQ5rfgPt36jndvWztpVMoP1TaDqExg/rOW3sH1t8e7bdRv230bRDiDiQ897c8iO5DlE%2BaaofYaaHa920xve3kp7prhJ2a6OfmtU3FrNN5a9w9Wu8P1r/D8i4I8hXCPdrYV/a%2BI4/ZRXx%2BMV7O3Fbr7xnP7Bd7%2B2Ld/sS2g%2B6joB1o/oHBOQHgpwS/UpeO5WG7QcrQ2Y%2BlOt3LHTu4G5tq7t2PyzZhyG5YehsD2XHmlke%2B47Hu4OvHk9gh47Yg6z3PepDoJ/LxI4hPzLS6rs6qroerLN7WK%2BJ8w8ScU3977D1J5w9ptR3T7fl%2B/QFcvtBXtrBT6NUU7EcRWJHZTmwydZhM53qnQtuviLfqfKPGnqjsu4A4z5tOM%2BfvQ5yA5TMK2296ZtQ6JY0PiXm75jkZzFrqPFn5L3d5B73dQcdGFnGli28s5aueOnOGznx4Q%2B2fEPdngTz3gc7d5kcjn8aiy8vYxO0OxrfZ2J3Meudk3bne95JwfY4deXj7Pl15zHfPtx2BHi54Kwcdvsc3U7XNx%2B/PufsyHKnAtuR5P3zuKPC7dfYu007r4tPEXGfdpyi%2BAE8uQHp5vR4raxevGcX%2BVpu16oJf5nRnxLl86DbJf2OKXjjql%2Bg5pfD3ALHjtZ4y/wfMutnNnfx%2By/w77PPe3L4vhR15c3b%2BXFBle5E%2BWWE2MVjDnNTvclesP7nHlw%2B3K64cvP6bWTxmzk82tqvvnGr0KynfCtp3DrGd461nZjNgvJ%2BNTyF1/br4/26%2Bf9%2BF3X1ad2vkXGfR16YKzcgOBLGLtM/Yuxd5XVbpjpbVJZKuFnEDut0fVM720zO%2B74b5x5G6wf0vY3dtzq4m52f4c9nnL9N570zef9DL4F4y/lqguFbjn2m059aeFcxP1lAd8V2W7xVsOq3sriO7W5Pv1uz77zi%2B6q4Tvqvlzmrmddq4fvp2n7UjpizI%2BuPguP7w7up6O4afjurXk/G15o5nch8HXGfBd4NLSvi8Zbz1pcDRzAdfXjHkDwZ9u%2BKs1G934z13bY6BPTPQTVZs9w1bhtRvtLqzlG0y9vcz22XD7jl/hy5cvvPeb7z3t1aMu9WXpa%2Bvlyc4Qv43znVW0V05dA/bK7n0rh5znpg8Ku4Pbzvhx86Q9X2UPSd9t3fYw9qasPernDy/cNdv3jXzVhR5PyUcWuVHdfNR%2BXdtch97Xc7ujxnwY8Z8HrTHqu1tJruvXXXmLtdx643cmPuPfegG23bGcd2JnQn1AyJ8/NzOTbEbxqwjZWe6X1n8b%2BT346s5KfU3T7/Dhm/U%2Be9t%2B2nfDvKJD0vjv3ennNwZ7xtnPAPRNvEyB80XB2K3VnyD485rfPPYPPD%2BD458Q%2B5OW3QjttyI7%2BdIaAXpTo6%2BU5Be5GjXudk18F%2BRvmvJ%2BlruF806i9UeYvs7kPvO4S8Z9G%2BNHDPlLcevMeqllPN1ux872ceBnFRoZ3mbgNEvZL5VoN0e9MOieob1X897V7cdXvZPTX6ey14CfteVPz7/Dq%2B56%2BafPee/N1tm7NO5uPD%2Bboz5N%2BLdb3A7FnqV5RurfQeVvdntbw5%2BydOetvyH1t6h/c9avO3Or7zzrt88Gv%2B3VTwdxC8n5QuSPMLsj/d%2BtePeG%2BSLmj3F5D70ePvSX77xnwrsWKYJsPR4EuDu6rhmOeBAHgJJE6BtVwZ0LCHJwU7nQhqld8hdCFlbytcefAQTs78eCW%2BxOE2uHob7u5fdwIwPX30Acp4y50eDIXjnGxt%2Byd5OcbRTo8CWgMl02tv%2BPy6UT%2BO/4GnIAuJaBFaMtpYBrHkJyHO7Z%2BEmBzbOBYEFYsNYmXTa5NLFMZe/XfaIDVuiV%2B%2BpfgIS4OYNgHyBeByoCIJYJCAgDAtVw1gHCFCxcDAsxAIBGf9NnG4mQGg4PFL175H9j%2B4QjwFED37RA4Rh/2AUf%2BP945T/Z/x/3iuN16ImFNMFmfVE4X%2BCr%2BD/k/7AA%2BUMJPlrSpMA%2BHHn/SNA7/6/w/9gFfJ6%2BBJb/w38t/cfzRB4QXf338f/B/2n8T/L/AK56Uc/1MIr/EQBv8/gIAN/9ygAiFSZtcFWA7h0A6AI9YnfaeiADN/fIG391/SEFh5t/CALX8J/I/1gDP8eALyxEAy/3fRr/MNnwDgWcoBNIMCOuhwJ2wROkqRXGfJDwC9/WgN/870NWh2pCA9vxX8xA8f1IDyA%2BTiEAhAGgPv96AhgKhwmA6HGnJAcC/3tJkA1AM4DH/GknNJTSeTBtoBA4BktosA4QNwCQAYwI9xbAuuHsDjAvmn/8xkIgPR4SAkAPX80QFwDmA4QNQKgCNAzQL%2BxtAz3Bf99Aj7EMCOA%2BQJCCTA8/z/x%2BkUHC/94gugMSDdA73DnwUgrLDSDIAjII8CvAwAJcAAAaSeAXSbAEhBIIOECSBggwoLCCZ/CIP2pM0FPDlRGgjoNGwIg01iKAvfJGgQpOgzQIiCBAQ8i7IFaRiiVpWKGGnYoR0bXHUgI0UQIKDf/XsEww68B6kYDxuA%2BgqQbSKILnIjofIPEDoAk9EGCIcYYNFoNyJIN9wDg9QIqJHkE4OAIIgn%2BHuDngpoM2CXg94I2D16HoPNYg/A2gGCPgh4LeCL6cYNko86LmgLppghkl/I40NHHDQegRYMODgWAghsoGAs4K2D7KOIKWDoAq5ABDYAs4K8pMQxENuDcQzoMeCJkEkIpC5/VrkpCaQ7oKNAzWPoN/pZYGkNOCgQsyiyoQQrqnzpFKIOhmDoQuYLhCEQm4ORDsyE/zRDHqDEJGhsAkQIcD0giQJxCWQnjDeCCQqULsDvkWUKxCkQ%2B9EVDwg%2Bf3JCdQykPFCDQkkLpDdgXoN%2BDjKQ2mNCzcNkOtpbGeWiIwVqMEOVpIQrdFjQBQhYI1CiQkUPaDXg1Th4DU0VwksChAQQOcCcA9UMcDMA1UJcCIwuUIf9KIC2ApgqQ77AJC1KZRGtDSQvUJogMw3EKNCcwl4NNCGQ/4PzDUQt4I7pc6ZinBCeQ5SlmDYQz0LcCK4dYNLD/QtAnMCLkZABHBBA6UNcC4wqf3YwVEd8kr8wwGv2sxxwDxnFg8AKWDFBiCdbHKBrGYUFFAQmcLGiYAwdllCBlmZKHQAmmacNiAMgEgHsAAoTwFsAMWDIANhtKfoGnBVwoyAPRnGFgCrQdgKYEWYrmUKFiAcEeMDWYBcX/39AMAGZkyYuadhlERcgePEPD%2BAXlltZLAX1DyAtoHYHOACmdSEShcgHcNEZPw%2BMIpJxKCuGxYigcWFtAQIp9CWBUAYIBbgwIovzlBSGAMgwYfSd4GlhgybokKZiAewCxYjmFFjqBS4QcDFYYgTyDmAwATgFCB/SN8CfRV4Zvz6Z8EcIG4ilAC5jyB9GZgAoigKBnCkYQaVoBxgEEXYGUYRGeoFWYHAHJg4YdmLSLDwqSASLegtwnZioZRGIJCMiiSYIErAY4EAAMiWIWgBbgvATpFUZPIeYIwRHAbDgvRXI9yNUAhmYgg%2BYBUBnA7QqGIOFwYIUGZCNhpwwkg0gTiECIqxySMUAkixQFyK5IlmLIBlBQyWIBHC9uYMhkiYqCYHQQMGfOCIAAycIFijV4ZQBbg5geFEiRYwHbiWZMAbiN4i8AciLlBsmfagvDYwcWFVAxwsWElgzQjxFIisgciMfAigXRlIZJAGWFsjV4CbAEZ2GVZgKZ7QckF6YRYccL6j/kEnGO4Q8UKkoJWgZJnYoVo3qMnD%2BokoEMZgkW0Cmi3obyFIYWwURlmRaYb9C2A3wZIAkZZYAMisYrIEhGnCRQO0EtBFII0D6g%2BYcPGGAPo6cOPgaycYHXggkNqNWR3mHZAxxG/TkMmDuaaYLlQLI%2BhHVY2/Zfw5QsGJ4H8x90KDAwCPfM3xLC4uVwDujCYUwKup7YRhBJitAsDnJj5kbtAVDaYuALeCWY%2B4KYCWgqiAwj2YvELA57YQfBQiuAq2BtgMImmN5i8w3mLCCIg9ujVDo4S2CFjiQqWP%2BI2Y5WMaDCw80PR5%2BgjKlfoF6a0LODRQ0MP%2BBFY44LViWuFMKgYw2RWPTCzYpUPXong22KGDVYx2LFCz/ekM1iBJJGhtZ10ScM3QL0MdHApLYHyMzDh2GVkxA5WNEFD8kBLvx78%2B/MCAH8c4GTjt8E/B3xtjBgyWJdiQCZoO0pWg%2Bgl1oeYqWIiCvuEUgNpvgkWKfAsSFIispKY8GmSA68DAnf9iSf9F1D7YpMMzjXY0OMj9w4t32ID0gxQJwhU/OP3t8k/E4LJDsw9uNn8M4ieLpj16LmPHxwifSlcxnMeHANCi4rWJLjLQsuNLgRQRDGkAq4y4kixMMSvGria8ZIHNBMyYXlZDW4u4Onis4t4LDj0IHuO8C%2B43wMeB/AwIMHjk4jPwd9pYrMNvi/Q/en/ifsbOK1oOcLPF9Db4teM9iN4ovFbgzQ81m3jQgTokWpziJvGQDmwh1DbigEz4O%2BwH4iOKjjLWHwLIDQA%2BEE/j0/TP3Vi/47BJwSqaahOUJxuOeLYR3sbBKgTtxFghgxS492IQSy4SmPyRUE1IkiwYAjBPpRxYuhKvjcEruMfjI43uIKD%2B4igKoCTApOPISf4p2Nbjx4sRN%2Bwp4jROTCgSHOLAT6CCBOnjWEpAXYShcThPgTy43hJdp%2BE6uMES%2BY6%2BO0SW4iRIElm/AhIFwiEpQJcAVAshOHihE%2BxMwT1ExxPoTqQoJLXg6Q1NDdCq0D0LcisE/%2BLODXI/2mJwaYpROHiH0bUNtix4tONCSdE4amyTV8cbjwSn4koPKDHgSoOqCcIUIhbY0/XxILDnYvJNXgNYn4K1jRSG1nOBUAXbgmxd8D%2BCyT3gpgMdR0k%2BpLCS6kwZNP8h8B2JGShksZP1CJk/JKHxvgmEhpRLAAgGTo74KMMGwqxR9AmQpkS0AZiiYVsIwIl/TGFBBPAaenRBMQbEFxB8QQkAXB7YZVFlgIGAQGjIsgaAgEBUgDFlEZJmM/D6ZmAJuE2BPaD1mxii4VKiR54gNMH/JygZAA8D7YTADCRJSKwAeSvIeIF25EImZk7oaIKpA9ZmEPRPnjqEfgkQT1gH2CFQMaCABtZBYHIB5AIU/8LFRJUOKBtZqUsaAIBheBFIEp8EVFJMQUU8pClxKkSvBmgq6LLBeSH0CwkboQKdnBxTs4aeDaTduN/xhSSUqlMPRZQWlLlTJUJlJFoYUuFJpIBUgMJrhn/YgDRwQ8blINgG8A0FLpzg%2BoANBGgDlInRkcbGElTgEUwBx4bWO5PlTUgRVKUg1ke5LGhUgFVJvonaaVLCQkeOKC9S4GFwFtTggUwGp5HU%2BIGdTXUxICdSiIINOjpTUiYCaAXuXlNugvUxoBe5q4l1LOBroS2AzT/AKXBzTzWaiHzTlARoA7DG6TSncBoyUhmGAUKBFKdROAbTCmAuADQE4BSAcIC4BVADtNQAuAGJDiRHkJgFYA6YMNloAO0zAk4Ae05P3sAQADzlbTOAcyA7Su0qdN7SuADtNPh12SdOnTSAOAFgAkACMCjAyACgFlS%2BgI9MuwBgYAGPZ12SYGxw3ESgHOhXgSgG0x/AbtI7SfEFnFAgQkaQDfTSAfADCZao/BF/SqAHRmIAf0zgD%2BBGgctPHTSAGYBGAYgOgFIBLGQUFAggUEFAfTzoVQGfTYM9SGAAEM%2BgGQzUMmYkwIbfJ9OoAAARTvgwMndnug/2CyBA4iOQ9jmgRoeiH0Bn2SgFhilQcDObSW03DPwyFyUgCkB/kU%2BE4BtAUCHNBtAbeGogOw2JFigdAIKGwiN0lgDYAOAAAgXT20ztN/S%2B0zgHeBaIfCG0AyOAxEvS%2BASDK0AIAXAApxxUfpXEB2SI9KszEkCdLfSZ0udPXYF0pdM0zV0wTPXTGAEAC3SnMltK4BlAZdK0zvM7dJ4zSAanDcgQAcyCAA%3D%3D%3D). 344 | 345 | ## Visitation without an instance 346 | 347 | Besides iteration over an *instance* of a registered struct, **visit_struct** also 348 | supports visiting the *definition* of the struct. In this case, instead of passing 349 | you the field name and the field value within some instance, it passes you the 350 | field name and the *pointer to member* corresponding to that field. 351 | 352 | Suppose that you are serializing many structs in your program as json. You might also want to be able to emit the json schema associated 353 | to each struct that your program is expecting, especially to produce good diagnostics if loading the data fails. When you visit without 354 | an instance, you can get all the type information for the struct, but you don't have to actually instantiate it, which might be complicated or expensive. 355 | 356 | 357 | ### `visit_pointers` 358 | 359 | The function call 360 | 361 | ```c++ 362 | visit_struct::visit_pointers(v); 363 | ``` 364 | 365 | is similar to 366 | 367 | ```c++ 368 | v("a", &my_type::a); 369 | v("b", &my_type::b); 370 | v("c", &my_type::c); 371 | ``` 372 | 373 | These may be especially useful when you have a C++14 compiler which has proper `constexpr` support. 374 | In that case, these visitations are `constexpr` also, so you can use this 375 | for some nifty metaprogramming purposes. (For an example, check out [test_fully_visitable.cpp](./test_fully_visitable.cpp).) 376 | 377 | There are two alternate versions of this visitation. 378 | 379 | ### `visit_types` 380 | 381 | This function call 382 | 383 | ```c++ 384 | visit_struct::visit_types(v); 385 | ``` 386 | 387 | is similar to 388 | 389 | ```c++ 390 | v("a", visit_struct::type_c()); 391 | v("b", visit_struct::type_c()); 392 | v("c", visit_struct::type_c()); 393 | ``` 394 | 395 | Here, `type_c` is just a tag, so that your visitor can take appropriate action using tag dispatch. 396 | This syntax is a little simpler than the pointer to member syntax. 397 | 398 | ### `visit_accessors` 399 | 400 | In the third version, you get passed an "accessor", that is, a function object that implements the function computed by 401 | the pointer-to-member. 402 | 403 | This call 404 | 405 | ```c++ 406 | visit_struct::visit_accessors(v); 407 | ``` 408 | 409 | is roughly similar to 410 | 411 | ```c++ 412 | v("a", [](auto s) { return s.a; }); 413 | v("b", [](auto s) { return s.b; }); 414 | v("c", [](auto s) { return s.c; }); 415 | ``` 416 | 417 | Accessors are convenient because they can be used easily with other standard algorithms that require function objects, 418 | they avoid the syntax of member pointers, and because they are well-supported by hana and fusion. 419 | 420 | Much thanks to Jarod42 for this patch and subsequent suggestions. 421 | 422 | 423 | 424 | 425 | **Note:** The compatibility headers for `boost::fusion` and `boost::hana` don't 426 | currently support `visit_pointers`. They only support `visit_types`, and `visit_accessors`. 427 | 428 | To my knowledge, there is no way to get the pointers-to-members from `boost::fusion` or `boost::hana`. 429 | That is, there is no publicly exposed interface to get them. 430 | 431 | 432 | If you really want or need to be able to get the pointers to members, that's a pretty good reason to use `visit_struct` honestly. 433 | If you think you need the fusion or hana compatibility, then you should probably avoid anything to do with member pointers here, and stick to accessors instead. 434 | 435 | ## Tuple Methods, Indexed Access 436 | 437 | `for_each` is quite powerful, and by crafting special visitors, there is a lot that you can do with it. 438 | 439 | However, one thing that you cannot easily do is implement `std::tuple` 440 | methods, like `std::get` to get the `i`'th member of the struct. 441 | Most if not all libraries that support struct-field reflection support this in some way. 442 | So, we decided that we should support this also. 443 | 444 | We didn't change our implementation of `for_each`, which works well on all targets. 445 | But we have added new functions which allow indexed access to structures, and to the metadata. 446 | 447 | ### `get` 448 | 449 | ```c++ 450 | visit_struct::get(s); 451 | ``` 452 | 453 | Gets (a reference to) the `i`'th visitable member of the struct `s`. Index is 0-based. Analogous to `std::get`. 454 | 455 | ### `get_name` 456 | 457 | ```c++ 458 | visit_struct::get_name(); 459 | visit_struct::get_name(s); 460 | ``` 461 | 462 | Gets a string constant representing the name of the `i`'th member of the struct type `S`. The struct type may be passed as a second template parameter. 463 | If an instance is available, it may be passed as an argument, and the struct type will be deduced (the argument will not be accessed). 464 | 465 | ### `get_pointer` 466 | 467 | ```c++ 468 | visit_struct::get_pointer(); 469 | visit_struct::get_pointer(s); 470 | ``` 471 | 472 | Gets the pointer-to-member for the `i`'th visitable element of the struct type `S`. 473 | 474 | ### `get_accessor` 475 | 476 | ```c++ 477 | visit_struct::get_accessor(); 478 | visit_struct::get_accessor(s); 479 | ``` 480 | 481 | Gets the accessor corresponding to the `i`'th visitable element of the struct type `S`. 482 | 483 | ### `type_at` 484 | 485 | ```c++ 486 | visit_struct::type_at 487 | ``` 488 | 489 | This alias template gives the declared type of the `i`'th member of `S`. 490 | 491 | ### `field_count` 492 | 493 | ```c++ 494 | visit_struct::field_count(); 495 | visit_struct::field_count(s); 496 | ``` 497 | 498 | Gets a `size_t` which tells how many visitable fields there are. 499 | 500 | ## Other functions 501 | 502 | ### `get_name` (no index) 503 | 504 | ```c++ 505 | visit_struct::get_name(); 506 | visit_struct::get_name(s); 507 | ``` 508 | 509 | Gets a string constant representing the name of the structure. The string here is the token that you passed to the `visit_struct` macro in order to register the structure. 510 | 511 | This could be useful for error messages. E.g. "Failed to match json input with struct of type 'foo', layout: ..." 512 | 513 | There are other ways to get a name for the type, such as `typeid`, but it has implementation-defined behavior and sometimes gives a mangled name. However, the `visit_struct` name might not 514 | always be acceptable either -- it might contain namespaces, or not, depending on if you use standard or intrusive syntax, for instance. 515 | 516 | Since the programmer is already taking the trouble of passing this name into a macro to register the struct, we think we might as well give programmatic access to that string if they want it. 517 | 518 | Note that there is no equivalent feature in `fusion` or `hana` to the best of my knowledge, so there's no support for this in the compatibility headers. 519 | 520 | ### `apply_visitor` 521 | 522 | ```c++ 523 | visit_struct::apply_visitor(v, s); 524 | visit_struct::apply_visitor(v, s1, s2); 525 | ``` 526 | 527 | This is an alternate syntax for `for_each`. The only difference is that the visitor comes first rather than last. 528 | 529 | Historically, `apply_visitor` is a much older part of `visit_struct` than `for_each`. Its syntax is similar to `boost::apply_visitor` from the `boost::variant` library. 530 | For a long time, `apply_visitor` was the only function in the library. 531 | 532 | However, experience has shown that `for_each` is little nicer syntax than `apply_visitor`. It reads more like a for loop -- the bounds of the loop come first, which are the structure, then the body of the loop, which is repeated. 533 | 534 | Additionally, in C++14 one may often use generic lambdas. Then the code is a little more readable if the lambda comes last, since it may span several lines of code. 535 | 536 | (I won't say I wasn't influenced by ldionne's opinion. He makes this same point in the `boost::hana` docs [here](http://www.boost.org/doc/libs/1_63_0/libs/hana/doc/html/index.html#tutorial-rationales-parameters).) 537 | 538 | So, nowadays I prefer and recommend `for_each`. The original `apply_visitor` syntax isn't going to be deprecated or broken though. 539 | 540 | ### `traits::is_visitable` 541 | 542 | ```c++ 543 | visit_struct::traits::is_visitable::value 544 | ``` 545 | 546 | This type trait can be used to check if a structure is visitable. The above expression should resolve to boolean true or false. I consider it part of the public API. You can use it in SFINAE to easily select types that `visit_struct` knows how to use. 547 | 548 | ## Advanced Features 549 | 550 | ### Visitation contexts 551 | 552 | In a larger project, sometimes you want to visit a different set of members in each of your structures for different purposes. 553 | For example, for serialization you might want to visit everything. For logging you might want to omit some of the elements. For scripting access you might want a different subset still. 554 | 555 | It's possible to use inheritance to create proxy objects which can be declared visitable in different ways from the base object. But it has ergonomic problems and may force unnecessary copies, which is particularly harmful when walking large nested structures. 556 | 557 | As an alternative, visit struct supports creating custom visitation "contexts". A context can be represented by any type, but usually a tag type in your program. Structs can be registered as visitable against each of these tags. 558 | 559 | ``` 560 | struct Logging {}; 561 | struct Scripting {}; 562 | 563 | struct Foo { 564 | int i; 565 | double d; 566 | bool b; 567 | string s; 568 | }; 569 | 570 | VISITABLE_STRUCT(Foo, i, d, b, s); 571 | VISITABLE_STRUCT_IN_CONTEXT(Logging, Foo, i, s); 572 | VISITABLE_STRUCT_IN_CONTEXT(Scripting, Foo, d, b, s); 573 | ``` 574 | 575 | Then, you can visit it in a special context by using calls such as 576 | 577 | ``` 578 | visit_struct::context::for_each(foo, visitor); 579 | ``` 580 | 581 | The whole API of free functions is reproduced under `context` to invoke those functions within the given context. 582 | 583 | If you need to use the same context repeatedly, you can shorten this like so 584 | 585 | ``` 586 | using C = visit_struct::Context; 587 | C::for_each(foo, visitor); 588 | C::for_each(bar, visitor); 589 | ``` 590 | 591 | This also allows you to write functions that are generic over the context parameter if you like. 592 | The "default" context corresponds to the type `void`. 593 | 594 | ## Limits 595 | 596 | When using `VISITABLE_STRUCT`, the maximum number of members which can be registered 597 | is `visit_struct::max_visitable_members`, which is by default 69. 598 | 599 | When using the intrusive syntax, the maximum number of members is `visit_struct::max_visitable_members_intrusive`, 600 | which is by default 100. 601 | 602 | These limits can both be increased, see the source comments and also [IMPLEMENTATION_NOTES.md](/IMPLEMENTATION_NOTES.md). 603 | 604 | ## Compiler Support 605 | 606 | **visit_struct** targets C++11 -- you need to have r-value references at least, and for the intrusive syntax, you need 607 | variadic templates also. 608 | 609 | **visit_struct** is known to work with versions of gcc `>= 4.8.2` and versions of clang `>= 3.5`. 610 | 611 | The appveyor build tests against MSVC 2013, 2015, 2017. 612 | 613 | MSVC 2015 is believed to be fully supported. 614 | 615 | For MSVC 2013, the basic syntax is supported, the intrusive syntax doesn't work there and now isn't tested. 616 | Again, patches welcome. 617 | 618 | Much thanks again to Jarod42 for significant patches related to MSVC support. 619 | 620 | ## Constexpr Correctness 621 | 622 | `visit_struct` attempts to target three different levels of `constexpr` support. 623 | 624 | - No support 625 | - C++11 support 626 | - C++14 extended support 627 | 628 | This is controlled by two macros `VISIT_STRUCT_CONSTEXPR` and `VISIT_STRUCT_CXX14_CONSTEXPR`. We use these tokens where we would use the `constexpr` keyword. 629 | 630 | In the `visit_struct.hpp` header, these macros are defined to either `constexpr` or nothing. 631 | 632 | We attempt to guess the appropriate setting by inspecting the preprocessor symbols `__cplusplus` and `_MSC_VER`. 633 | 634 | If it doesn't work on your compiler, please open a github issue, especially if you know how to fix it :) 635 | 636 | In the meantime, if you don't want to tweak the headers for your project, you can override the behavior by defining these macros yourself, before including `visit_struct.hpp`. 637 | If the header sees that you have defined them it won't touch them and will defer to your settings. In most cases this should not be necessary. 638 | 639 | On gcc and clang, we assume at least C++11 constexpr support. If you enabled a later standard using `-std=...`, we turn on the full `constexpr`. 640 | 641 | On MSVC currently the settings are: 642 | 643 | - VS2013: no support 644 | - VS2015: C++11 support 645 | - VS2017: C++14 extended support 646 | 647 | ## Licensing and Distribution 648 | 649 | **visit_struct** is available under the boost software license. 650 | 651 | ## See also 652 | 653 | * [map-macro](https://github.com/swansontec/map-macro) from swansontec 654 | * [boost-hana](http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/index.html) from ldionne 655 | * [pod flat reflection](https://github.com/apolukhin/magic_get) from apolukhin 656 | * [self-aware structs](http://duriansoftware.com/joe/Self-aware-struct-like-types-in-C++11.html) from jckarter 657 | --------------------------------------------------------------------------------