├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake_uninstall.cmake.in ├── include ├── better-enums │ └── enum.h └── procmap │ ├── Logger.hpp │ ├── MemoryMap.hpp │ └── MemorySegment.hpp ├── src └── procmap │ ├── Logger.cpp │ ├── MemoryMap.cpp │ └── MemorySegment.cpp └── test └── simple.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # CMake # 3 | ####################################### 4 | CMakeCache.txt 5 | CMakeFiles 6 | CMakeScripts 7 | Testing 8 | Makefile 9 | cmake_install.cmake 10 | install_manifest.txt 11 | compile_commands.json 12 | CTestTestfile.cmake 13 | 14 | ####################################### 15 | # C # 16 | ####################################### 17 | # Prerequisites 18 | *.d 19 | 20 | # Object files 21 | *.o 22 | *.ko 23 | *.obj 24 | *.elf 25 | 26 | # Linker output 27 | *.ilk 28 | *.map 29 | *.exp 30 | 31 | # Precompiled Headers 32 | *.gch 33 | *.pch 34 | 35 | # Libraries 36 | *.lib 37 | *.a 38 | *.la 39 | *.lo 40 | 41 | # Shared objects (inc. Windows DLLs) 42 | *.dll 43 | *.so 44 | *.so.* 45 | *.dylib 46 | 47 | # Executables 48 | *.exe 49 | *.out 50 | *.app 51 | *.i*86 52 | *.x86_64 53 | *.hex 54 | 55 | # Debug files 56 | *.dSYM/ 57 | *.su 58 | *.idb 59 | *.pdb 60 | 61 | # Kernel Module Compile Results 62 | *.mod* 63 | *.cmd 64 | .tmp_versions/ 65 | modules.order 66 | Module.symvers 67 | Mkfile.old 68 | dkms.conf 69 | 70 | ####################################### 71 | # C++ # 72 | ####################################### 73 | # Prerequisites 74 | *.d 75 | 76 | # Compiled Object files 77 | *.slo 78 | *.lo 79 | *.o 80 | *.obj 81 | 82 | # Precompiled Headers 83 | *.gch 84 | *.pch 85 | 86 | # Compiled Dynamic libraries 87 | *.so 88 | *.dylib 89 | *.dll 90 | 91 | # Fortran module files 92 | *.mod 93 | *.smod 94 | 95 | # Compiled Static libraries 96 | *.lai 97 | *.la 98 | *.a 99 | *.lib 100 | 101 | # Executables 102 | *.exe 103 | *.out 104 | *.app 105 | 106 | ####################################### 107 | # procmap specific # 108 | ####################################### 109 | 110 | # user-generated CMake scripts 111 | DartConfiguration.tcl 112 | cmake_uninstall.cmake 113 | 114 | # generated libraries 115 | /libprocmap.so 116 | 117 | # generated executables 118 | /simple 119 | 120 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: true 3 | 4 | matrix: 5 | include: 6 | - name: "gcc 8" 7 | env: MATRIX_ENV="CC=gcc-8 CXX=g++-8" 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-8 14 | - name: "gcc 7" 15 | env: MATRIX_ENV="CC=gcc-7 CXX=g++-7" 16 | addons: 17 | apt: 18 | sources: 19 | - ubuntu-toolchain-r-test 20 | packages: 21 | - g++-7 22 | - name: "gcc 6" 23 | env: MATRIX_ENV="CC=gcc-6 CXX=g++-6" 24 | addons: 25 | apt: 26 | sources: 27 | - ubuntu-toolchain-r-test 28 | packages: 29 | - g++-6 30 | - name: "clang 6" 31 | env: MATRIX_ENV="CC=clang-6.0 CXX=clang++-6.0" 32 | addons: 33 | apt: 34 | sources: 35 | - ubuntu-toolchain-r-test 36 | - llvm-toolchain-xenial-6.0 37 | packages: 38 | - libstdc++-8-dev 39 | - clang-6.0 40 | 41 | env: 42 | global: 43 | - MAKEFLAGS="-j 2" # parallelize compilation process 44 | 45 | before_install: eval "${MATRIX_ENV}" 46 | 47 | script: 48 | - cmake . -DCMAKE_BUILD_TYPE=Debug 49 | - make all 50 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(unstickymem VERSION 1.0 LANGUAGES CXX C) 3 | 4 | include(GNUInstallDirs) 5 | include(CTest) 6 | 7 | set(CMAKE_VERBOSE_MAKEFILE OFF) 8 | 9 | # get list of source files 10 | file(GLOB_RECURSE procmap_src relative ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp" "src/*c") 11 | add_library(procmap SHARED ${procmap_src}) 12 | 13 | set_property(TARGET procmap PROPERTY POSITION_INDEPENDENT_CODE on) 14 | 15 | target_compile_features(procmap PRIVATE cxx_std_14) 16 | 17 | target_compile_options(procmap PRIVATE -g -Wall -pedantic -Wshadow -Wfatal-errors) 18 | 19 | target_include_directories(procmap 20 | PUBLIC $ 21 | PUBLIC $ 22 | PRIVATE src) 23 | 24 | # 'make install' to the correct locations (provided by GNUInstallDirs) 25 | install(TARGETS procmap 26 | EXPORT procmap_config 27 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 28 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 29 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 30 | 31 | # This makes the project importable from the install directory 32 | # Put config file in per-project dir (name MUST match), can also 33 | # just go into 'cmake' 34 | install(EXPORT procmap_config DESTINATION share/procmap/cmake) 35 | 36 | # uninstall target 37 | if(NOT TARGET uninstall) 38 | configure_file( 39 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 40 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 41 | IMMEDIATE @ONLY) 42 | add_custom_target(uninstall 43 | COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 44 | endif() 45 | 46 | 47 | # tests/examples 48 | add_executable(simple test/simple.cpp) 49 | target_link_libraries(simple procmap) 50 | add_test(simple simple) 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 João Neto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/joaomlneto/procmap.svg?branch=master)](https://travis-ci.com/joaomlneto/procmap) 2 | 3 | # procmap 4 | A simple C++ wrapper around `/proc/self/maps` 5 | 6 | This tool makes it simpler to help visualize the memory map of your process. 7 | 8 | It's super simplistic and was created to suit my own needs. Hopefully it provides a good start for you and you'll enhance it with a few more options (and submit a PR!) :-) 9 | 10 | ## Examples 11 | 12 | ### Reading `/proc/self/maps` 13 | 14 | Couldn't be easier. 15 | ```cpp 16 | MemoryMap m; 17 | ``` 18 | 19 | ### Visualizing `/proc/self/maps` 20 | 21 | ```cpp 22 | MemoryMap m; 23 | m.print(); 24 | ``` 25 | 26 | ![screenshot 2018-11-05 at 19 04 40](https://user-images.githubusercontent.com/1539767/48020367-a9011b00-e12d-11e8-8f2c-03d29bf786ad.png) 27 | 28 | ### Iterating through the entries and selecting only those that are writeable 29 | 30 | ```cpp 31 | MemoryMap map; 32 | for (auto &segment : map) { 33 | if (segment.isWriteable()) { 34 | segment.print(); 35 | } 36 | } 37 | ``` 38 | 39 | ![image](https://user-images.githubusercontent.com/1539767/48020839-d1d5e000-e12e-11e8-87be-5e7ced7e1e88.png) 40 | 41 | ## Reference 42 | 43 | MemoryMap is basically a collection of MemorySegments. Use it as you would a `std::vector`. 44 | 45 | MemorySegment represents a single entry/line in `/proc/*/maps`. It provides a plethora of utility functions to make life more pleasant. Check the [header file](https://github.com/joaomlneto/process-memory-map/blob/master/include/procmap/MemorySegment.hpp) for a complete list. 46 | 47 | ## Suggestions / Complaints 48 | Open an issue! :-) 49 | 50 | ## Contributing 51 | PRs are more than welcome. 52 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 3 | endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif(NOT "${rm_retval}" STREQUAL 0) 18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 20 | endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | endforeach(file) 22 | -------------------------------------------------------------------------------- /include/better-enums/enum.h: -------------------------------------------------------------------------------- 1 | // This file is part of Better Enums, released under the BSD 2-clause license. 2 | // See doc/LICENSE for details, or visit http://github.com/aantron/better-enums. 3 | 4 | #pragma once 5 | 6 | #ifndef BETTER_ENUMS_ENUM_H 7 | #define BETTER_ENUMS_ENUM_H 8 | 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | 18 | // Feature detection. 19 | 20 | #ifdef __GNUC__ 21 | # ifdef __clang__ 22 | # if __has_feature(cxx_constexpr) 23 | # define BETTER_ENUMS_HAVE_CONSTEXPR 24 | # endif 25 | # if !defined(__EXCEPTIONS) || !__has_feature(cxx_exceptions) 26 | # define BETTER_ENUMS_NO_EXCEPTIONS 27 | # endif 28 | # else 29 | # if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L 30 | # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) 31 | # define BETTER_ENUMS_HAVE_CONSTEXPR 32 | # endif 33 | # endif 34 | # ifndef __EXCEPTIONS 35 | # define BETTER_ENUMS_NO_EXCEPTIONS 36 | # endif 37 | # endif 38 | #endif 39 | 40 | #ifdef _MSC_VER 41 | # if _MSC_VER >= 1911 42 | # define BETTER_ENUMS_HAVE_CONSTEXPR 43 | # endif 44 | # ifdef __clang__ 45 | # if __has_feature(cxx_constexpr) 46 | # define BETTER_ENUMS_HAVE_CONSTEXPR 47 | # endif 48 | # endif 49 | # ifndef _CPPUNWIND 50 | # define BETTER_ENUMS_NO_EXCEPTIONS 51 | # endif 52 | # if _MSC_VER < 1600 53 | # define BETTER_ENUMS_VC2008_WORKAROUNDS 54 | # endif 55 | #endif 56 | 57 | #ifdef BETTER_ENUMS_CONSTEXPR 58 | # define BETTER_ENUMS_HAVE_CONSTEXPR 59 | #endif 60 | 61 | #ifdef BETTER_ENUMS_NO_CONSTEXPR 62 | # ifdef BETTER_ENUMS_HAVE_CONSTEXPR 63 | # undef BETTER_ENUMS_HAVE_CONSTEXPR 64 | # endif 65 | #endif 66 | 67 | // GCC (and maybe clang) can be made to warn about using 0 or NULL when nullptr 68 | // is available, so Better Enums tries to use nullptr. This passage uses 69 | // availability of constexpr as a proxy for availability of nullptr, i.e. it 70 | // assumes that nullptr is available when compiling on the right versions of gcc 71 | // and clang with the right -std flag. This is actually slightly wrong, because 72 | // nullptr is also available in Visual C++, but constexpr isn't. This 73 | // imprecision doesn't matter, however, because VC++ doesn't have the warnings 74 | // that make using nullptr necessary. 75 | #ifdef BETTER_ENUMS_HAVE_CONSTEXPR 76 | # define BETTER_ENUMS_CONSTEXPR_ constexpr 77 | # define BETTER_ENUMS_NULLPTR nullptr 78 | #else 79 | # define BETTER_ENUMS_CONSTEXPR_ 80 | # define BETTER_ENUMS_NULLPTR NULL 81 | #endif 82 | 83 | #ifndef BETTER_ENUMS_NO_EXCEPTIONS 84 | # define BETTER_ENUMS_IF_EXCEPTIONS(x) x 85 | #else 86 | # define BETTER_ENUMS_IF_EXCEPTIONS(x) 87 | #endif 88 | 89 | #ifdef __GNUC__ 90 | # define BETTER_ENUMS_UNUSED __attribute__((__unused__)) 91 | #else 92 | # define BETTER_ENUMS_UNUSED 93 | #endif 94 | 95 | 96 | 97 | // Higher-order preprocessor macros. 98 | 99 | #ifdef BETTER_ENUMS_MACRO_FILE 100 | # include BETTER_ENUMS_MACRO_FILE 101 | #else 102 | 103 | #define BETTER_ENUMS_PP_MAP(macro, data, ...) \ 104 | BETTER_ENUMS_ID( \ 105 | BETTER_ENUMS_APPLY( \ 106 | BETTER_ENUMS_PP_MAP_VAR_COUNT, \ 107 | BETTER_ENUMS_PP_COUNT(__VA_ARGS__)) \ 108 | (macro, data, __VA_ARGS__)) 109 | 110 | #define BETTER_ENUMS_PP_MAP_VAR_COUNT(count) BETTER_ENUMS_M ## count 111 | 112 | #define BETTER_ENUMS_APPLY(macro, ...) BETTER_ENUMS_ID(macro(__VA_ARGS__)) 113 | 114 | #define BETTER_ENUMS_ID(x) x 115 | 116 | #define BETTER_ENUMS_M1(m, d, x) m(d,0,x) 117 | #define BETTER_ENUMS_M2(m,d,x,...) m(d,1,x) \ 118 | BETTER_ENUMS_ID(BETTER_ENUMS_M1(m,d,__VA_ARGS__)) 119 | #define BETTER_ENUMS_M3(m,d,x,...) m(d,2,x) \ 120 | BETTER_ENUMS_ID(BETTER_ENUMS_M2(m,d,__VA_ARGS__)) 121 | #define BETTER_ENUMS_M4(m,d,x,...) m(d,3,x) \ 122 | BETTER_ENUMS_ID(BETTER_ENUMS_M3(m,d,__VA_ARGS__)) 123 | #define BETTER_ENUMS_M5(m,d,x,...) m(d,4,x) \ 124 | BETTER_ENUMS_ID(BETTER_ENUMS_M4(m,d,__VA_ARGS__)) 125 | #define BETTER_ENUMS_M6(m,d,x,...) m(d,5,x) \ 126 | BETTER_ENUMS_ID(BETTER_ENUMS_M5(m,d,__VA_ARGS__)) 127 | #define BETTER_ENUMS_M7(m,d,x,...) m(d,6,x) \ 128 | BETTER_ENUMS_ID(BETTER_ENUMS_M6(m,d,__VA_ARGS__)) 129 | #define BETTER_ENUMS_M8(m,d,x,...) m(d,7,x) \ 130 | BETTER_ENUMS_ID(BETTER_ENUMS_M7(m,d,__VA_ARGS__)) 131 | #define BETTER_ENUMS_M9(m,d,x,...) m(d,8,x) \ 132 | BETTER_ENUMS_ID(BETTER_ENUMS_M8(m,d,__VA_ARGS__)) 133 | #define BETTER_ENUMS_M10(m,d,x,...) m(d,9,x) \ 134 | BETTER_ENUMS_ID(BETTER_ENUMS_M9(m,d,__VA_ARGS__)) 135 | #define BETTER_ENUMS_M11(m,d,x,...) m(d,10,x) \ 136 | BETTER_ENUMS_ID(BETTER_ENUMS_M10(m,d,__VA_ARGS__)) 137 | #define BETTER_ENUMS_M12(m,d,x,...) m(d,11,x) \ 138 | BETTER_ENUMS_ID(BETTER_ENUMS_M11(m,d,__VA_ARGS__)) 139 | #define BETTER_ENUMS_M13(m,d,x,...) m(d,12,x) \ 140 | BETTER_ENUMS_ID(BETTER_ENUMS_M12(m,d,__VA_ARGS__)) 141 | #define BETTER_ENUMS_M14(m,d,x,...) m(d,13,x) \ 142 | BETTER_ENUMS_ID(BETTER_ENUMS_M13(m,d,__VA_ARGS__)) 143 | #define BETTER_ENUMS_M15(m,d,x,...) m(d,14,x) \ 144 | BETTER_ENUMS_ID(BETTER_ENUMS_M14(m,d,__VA_ARGS__)) 145 | #define BETTER_ENUMS_M16(m,d,x,...) m(d,15,x) \ 146 | BETTER_ENUMS_ID(BETTER_ENUMS_M15(m,d,__VA_ARGS__)) 147 | #define BETTER_ENUMS_M17(m,d,x,...) m(d,16,x) \ 148 | BETTER_ENUMS_ID(BETTER_ENUMS_M16(m,d,__VA_ARGS__)) 149 | #define BETTER_ENUMS_M18(m,d,x,...) m(d,17,x) \ 150 | BETTER_ENUMS_ID(BETTER_ENUMS_M17(m,d,__VA_ARGS__)) 151 | #define BETTER_ENUMS_M19(m,d,x,...) m(d,18,x) \ 152 | BETTER_ENUMS_ID(BETTER_ENUMS_M18(m,d,__VA_ARGS__)) 153 | #define BETTER_ENUMS_M20(m,d,x,...) m(d,19,x) \ 154 | BETTER_ENUMS_ID(BETTER_ENUMS_M19(m,d,__VA_ARGS__)) 155 | #define BETTER_ENUMS_M21(m,d,x,...) m(d,20,x) \ 156 | BETTER_ENUMS_ID(BETTER_ENUMS_M20(m,d,__VA_ARGS__)) 157 | #define BETTER_ENUMS_M22(m,d,x,...) m(d,21,x) \ 158 | BETTER_ENUMS_ID(BETTER_ENUMS_M21(m,d,__VA_ARGS__)) 159 | #define BETTER_ENUMS_M23(m,d,x,...) m(d,22,x) \ 160 | BETTER_ENUMS_ID(BETTER_ENUMS_M22(m,d,__VA_ARGS__)) 161 | #define BETTER_ENUMS_M24(m,d,x,...) m(d,23,x) \ 162 | BETTER_ENUMS_ID(BETTER_ENUMS_M23(m,d,__VA_ARGS__)) 163 | #define BETTER_ENUMS_M25(m,d,x,...) m(d,24,x) \ 164 | BETTER_ENUMS_ID(BETTER_ENUMS_M24(m,d,__VA_ARGS__)) 165 | #define BETTER_ENUMS_M26(m,d,x,...) m(d,25,x) \ 166 | BETTER_ENUMS_ID(BETTER_ENUMS_M25(m,d,__VA_ARGS__)) 167 | #define BETTER_ENUMS_M27(m,d,x,...) m(d,26,x) \ 168 | BETTER_ENUMS_ID(BETTER_ENUMS_M26(m,d,__VA_ARGS__)) 169 | #define BETTER_ENUMS_M28(m,d,x,...) m(d,27,x) \ 170 | BETTER_ENUMS_ID(BETTER_ENUMS_M27(m,d,__VA_ARGS__)) 171 | #define BETTER_ENUMS_M29(m,d,x,...) m(d,28,x) \ 172 | BETTER_ENUMS_ID(BETTER_ENUMS_M28(m,d,__VA_ARGS__)) 173 | #define BETTER_ENUMS_M30(m,d,x,...) m(d,29,x) \ 174 | BETTER_ENUMS_ID(BETTER_ENUMS_M29(m,d,__VA_ARGS__)) 175 | #define BETTER_ENUMS_M31(m,d,x,...) m(d,30,x) \ 176 | BETTER_ENUMS_ID(BETTER_ENUMS_M30(m,d,__VA_ARGS__)) 177 | #define BETTER_ENUMS_M32(m,d,x,...) m(d,31,x) \ 178 | BETTER_ENUMS_ID(BETTER_ENUMS_M31(m,d,__VA_ARGS__)) 179 | #define BETTER_ENUMS_M33(m,d,x,...) m(d,32,x) \ 180 | BETTER_ENUMS_ID(BETTER_ENUMS_M32(m,d,__VA_ARGS__)) 181 | #define BETTER_ENUMS_M34(m,d,x,...) m(d,33,x) \ 182 | BETTER_ENUMS_ID(BETTER_ENUMS_M33(m,d,__VA_ARGS__)) 183 | #define BETTER_ENUMS_M35(m,d,x,...) m(d,34,x) \ 184 | BETTER_ENUMS_ID(BETTER_ENUMS_M34(m,d,__VA_ARGS__)) 185 | #define BETTER_ENUMS_M36(m,d,x,...) m(d,35,x) \ 186 | BETTER_ENUMS_ID(BETTER_ENUMS_M35(m,d,__VA_ARGS__)) 187 | #define BETTER_ENUMS_M37(m,d,x,...) m(d,36,x) \ 188 | BETTER_ENUMS_ID(BETTER_ENUMS_M36(m,d,__VA_ARGS__)) 189 | #define BETTER_ENUMS_M38(m,d,x,...) m(d,37,x) \ 190 | BETTER_ENUMS_ID(BETTER_ENUMS_M37(m,d,__VA_ARGS__)) 191 | #define BETTER_ENUMS_M39(m,d,x,...) m(d,38,x) \ 192 | BETTER_ENUMS_ID(BETTER_ENUMS_M38(m,d,__VA_ARGS__)) 193 | #define BETTER_ENUMS_M40(m,d,x,...) m(d,39,x) \ 194 | BETTER_ENUMS_ID(BETTER_ENUMS_M39(m,d,__VA_ARGS__)) 195 | #define BETTER_ENUMS_M41(m,d,x,...) m(d,40,x) \ 196 | BETTER_ENUMS_ID(BETTER_ENUMS_M40(m,d,__VA_ARGS__)) 197 | #define BETTER_ENUMS_M42(m,d,x,...) m(d,41,x) \ 198 | BETTER_ENUMS_ID(BETTER_ENUMS_M41(m,d,__VA_ARGS__)) 199 | #define BETTER_ENUMS_M43(m,d,x,...) m(d,42,x) \ 200 | BETTER_ENUMS_ID(BETTER_ENUMS_M42(m,d,__VA_ARGS__)) 201 | #define BETTER_ENUMS_M44(m,d,x,...) m(d,43,x) \ 202 | BETTER_ENUMS_ID(BETTER_ENUMS_M43(m,d,__VA_ARGS__)) 203 | #define BETTER_ENUMS_M45(m,d,x,...) m(d,44,x) \ 204 | BETTER_ENUMS_ID(BETTER_ENUMS_M44(m,d,__VA_ARGS__)) 205 | #define BETTER_ENUMS_M46(m,d,x,...) m(d,45,x) \ 206 | BETTER_ENUMS_ID(BETTER_ENUMS_M45(m,d,__VA_ARGS__)) 207 | #define BETTER_ENUMS_M47(m,d,x,...) m(d,46,x) \ 208 | BETTER_ENUMS_ID(BETTER_ENUMS_M46(m,d,__VA_ARGS__)) 209 | #define BETTER_ENUMS_M48(m,d,x,...) m(d,47,x) \ 210 | BETTER_ENUMS_ID(BETTER_ENUMS_M47(m,d,__VA_ARGS__)) 211 | #define BETTER_ENUMS_M49(m,d,x,...) m(d,48,x) \ 212 | BETTER_ENUMS_ID(BETTER_ENUMS_M48(m,d,__VA_ARGS__)) 213 | #define BETTER_ENUMS_M50(m,d,x,...) m(d,49,x) \ 214 | BETTER_ENUMS_ID(BETTER_ENUMS_M49(m,d,__VA_ARGS__)) 215 | #define BETTER_ENUMS_M51(m,d,x,...) m(d,50,x) \ 216 | BETTER_ENUMS_ID(BETTER_ENUMS_M50(m,d,__VA_ARGS__)) 217 | #define BETTER_ENUMS_M52(m,d,x,...) m(d,51,x) \ 218 | BETTER_ENUMS_ID(BETTER_ENUMS_M51(m,d,__VA_ARGS__)) 219 | #define BETTER_ENUMS_M53(m,d,x,...) m(d,52,x) \ 220 | BETTER_ENUMS_ID(BETTER_ENUMS_M52(m,d,__VA_ARGS__)) 221 | #define BETTER_ENUMS_M54(m,d,x,...) m(d,53,x) \ 222 | BETTER_ENUMS_ID(BETTER_ENUMS_M53(m,d,__VA_ARGS__)) 223 | #define BETTER_ENUMS_M55(m,d,x,...) m(d,54,x) \ 224 | BETTER_ENUMS_ID(BETTER_ENUMS_M54(m,d,__VA_ARGS__)) 225 | #define BETTER_ENUMS_M56(m,d,x,...) m(d,55,x) \ 226 | BETTER_ENUMS_ID(BETTER_ENUMS_M55(m,d,__VA_ARGS__)) 227 | #define BETTER_ENUMS_M57(m,d,x,...) m(d,56,x) \ 228 | BETTER_ENUMS_ID(BETTER_ENUMS_M56(m,d,__VA_ARGS__)) 229 | #define BETTER_ENUMS_M58(m,d,x,...) m(d,57,x) \ 230 | BETTER_ENUMS_ID(BETTER_ENUMS_M57(m,d,__VA_ARGS__)) 231 | #define BETTER_ENUMS_M59(m,d,x,...) m(d,58,x) \ 232 | BETTER_ENUMS_ID(BETTER_ENUMS_M58(m,d,__VA_ARGS__)) 233 | #define BETTER_ENUMS_M60(m,d,x,...) m(d,59,x) \ 234 | BETTER_ENUMS_ID(BETTER_ENUMS_M59(m,d,__VA_ARGS__)) 235 | #define BETTER_ENUMS_M61(m,d,x,...) m(d,60,x) \ 236 | BETTER_ENUMS_ID(BETTER_ENUMS_M60(m,d,__VA_ARGS__)) 237 | #define BETTER_ENUMS_M62(m,d,x,...) m(d,61,x) \ 238 | BETTER_ENUMS_ID(BETTER_ENUMS_M61(m,d,__VA_ARGS__)) 239 | #define BETTER_ENUMS_M63(m,d,x,...) m(d,62,x) \ 240 | BETTER_ENUMS_ID(BETTER_ENUMS_M62(m,d,__VA_ARGS__)) 241 | #define BETTER_ENUMS_M64(m,d,x,...) m(d,63,x) \ 242 | BETTER_ENUMS_ID(BETTER_ENUMS_M63(m,d,__VA_ARGS__)) 243 | 244 | #define BETTER_ENUMS_PP_COUNT_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ 245 | _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, \ 246 | _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ 247 | _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, \ 248 | _56, _57, _58, _59, _60, _61, _62, _63, _64, count, ...) count 249 | 250 | #define BETTER_ENUMS_PP_COUNT(...) \ 251 | BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(__VA_ARGS__, 64, 63, 62, 61, 60,\ 252 | 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42,\ 253 | 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24,\ 254 | 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, \ 255 | 4, 3, 2, 1)) 256 | 257 | #define BETTER_ENUMS_ITERATE(X, f, l) X(f, l, 0) X(f, l, 1) X(f, l, 2) \ 258 | X(f, l, 3) X(f, l, 4) X(f, l, 5) X(f, l, 6) X(f, l, 7) X(f, l, 8) \ 259 | X(f, l, 9) X(f, l, 10) X(f, l, 11) X(f, l, 12) X(f, l, 13) X(f, l, 14) \ 260 | X(f, l, 15) X(f, l, 16) X(f, l, 17) X(f, l, 18) X(f, l, 19) X(f, l, 20) \ 261 | X(f, l, 21) X(f, l, 22) X(f, l, 23) 262 | 263 | #endif // #ifdef BETTER_ENUMS_MACRO_FILE else case 264 | 265 | 266 | 267 | namespace better_enums { 268 | 269 | 270 | // Optional type. 271 | 272 | template 273 | BETTER_ENUMS_CONSTEXPR_ inline T _default() 274 | { 275 | return static_cast(0); 276 | } 277 | 278 | template <> 279 | BETTER_ENUMS_CONSTEXPR_ inline const char* _default() 280 | { 281 | return BETTER_ENUMS_NULLPTR; 282 | } 283 | 284 | template <> 285 | BETTER_ENUMS_CONSTEXPR_ inline std::size_t _default() 286 | { 287 | return 0; 288 | } 289 | 290 | template 291 | struct optional { 292 | BETTER_ENUMS_CONSTEXPR_ optional() : 293 | _valid(false), _value(_default()) { } 294 | 295 | BETTER_ENUMS_CONSTEXPR_ optional(T v) : _valid(true), _value(v) { } 296 | 297 | BETTER_ENUMS_CONSTEXPR_ const T& operator *() const { return _value; } 298 | BETTER_ENUMS_CONSTEXPR_ const T* operator ->() const { return &_value; } 299 | 300 | BETTER_ENUMS_CONSTEXPR_ operator bool() const { return _valid; } 301 | 302 | BETTER_ENUMS_CONSTEXPR_ const T& value() const { return _value; } 303 | 304 | private: 305 | bool _valid; 306 | T _value; 307 | }; 308 | 309 | template 310 | BETTER_ENUMS_CONSTEXPR_ static optional 311 | _map_index(const Element *array, optional index) 312 | { 313 | return index ? static_cast(array[*index]) : optional(); 314 | } 315 | 316 | #ifdef BETTER_ENUMS_VC2008_WORKAROUNDS 317 | 318 | #define BETTER_ENUMS_OR_THROW \ 319 | if (!maybe) \ 320 | throw std::runtime_error(message); \ 321 | \ 322 | return *maybe; 323 | 324 | #else 325 | 326 | #define BETTER_ENUMS_OR_THROW \ 327 | return maybe ? *maybe : throw std::runtime_error(message); 328 | 329 | #endif 330 | 331 | BETTER_ENUMS_IF_EXCEPTIONS( 332 | template 333 | BETTER_ENUMS_CONSTEXPR_ static T _or_throw(optional maybe, 334 | const char *message) 335 | { 336 | BETTER_ENUMS_OR_THROW 337 | } 338 | ) 339 | 340 | template 341 | BETTER_ENUMS_CONSTEXPR_ static T* _or_null(optional maybe) 342 | { 343 | return maybe ? *maybe : BETTER_ENUMS_NULLPTR; 344 | } 345 | 346 | 347 | 348 | // Functional sequencing. This is essentially a comma operator wrapped in a 349 | // constexpr function. g++ 4.7 doesn't "accept" integral constants in the second 350 | // position for the comma operator, and emits an external symbol, which then 351 | // causes a linking error. 352 | 353 | template 354 | BETTER_ENUMS_CONSTEXPR_ U 355 | continue_with(T, U value) { return value; } 356 | 357 | 358 | 359 | // Values array declaration helper. 360 | 361 | template 362 | struct _eat_assign { 363 | explicit BETTER_ENUMS_CONSTEXPR_ _eat_assign(EnumType value) : _value(value) 364 | { } 365 | 366 | template 367 | BETTER_ENUMS_CONSTEXPR_ const _eat_assign& 368 | operator =(Any) const { return *this; } 369 | 370 | BETTER_ENUMS_CONSTEXPR_ operator EnumType () const { return _value; } 371 | 372 | private: 373 | EnumType _value; 374 | }; 375 | 376 | 377 | 378 | // Iterables. 379 | 380 | template 381 | struct _Iterable { 382 | typedef const Element* iterator; 383 | 384 | BETTER_ENUMS_CONSTEXPR_ iterator begin() const { return iterator(_array); } 385 | BETTER_ENUMS_CONSTEXPR_ iterator end() const 386 | { return iterator(_array + _size); } 387 | BETTER_ENUMS_CONSTEXPR_ std::size_t size() const { return _size; } 388 | BETTER_ENUMS_CONSTEXPR_ const Element& operator [](std::size_t index) const 389 | { return _array[index]; } 390 | 391 | BETTER_ENUMS_CONSTEXPR_ _Iterable(const Element *array, std::size_t s) : 392 | _array(array), _size(s) { } 393 | 394 | private: 395 | const Element * const _array; 396 | const std::size_t _size; 397 | }; 398 | 399 | 400 | 401 | // String routines. 402 | 403 | BETTER_ENUMS_CONSTEXPR_ static const char *_name_enders = "= \t\n"; 404 | 405 | BETTER_ENUMS_CONSTEXPR_ inline bool _ends_name(char c, std::size_t index = 0) 406 | { 407 | return 408 | c == _name_enders[index] ? true : 409 | _name_enders[index] == '\0' ? false : 410 | _ends_name(c, index + 1); 411 | } 412 | 413 | BETTER_ENUMS_CONSTEXPR_ inline bool _has_initializer(const char *s, 414 | std::size_t index = 0) 415 | { 416 | return 417 | s[index] == '\0' ? false : 418 | s[index] == '=' ? true : 419 | _has_initializer(s, index + 1); 420 | } 421 | 422 | BETTER_ENUMS_CONSTEXPR_ inline std::size_t 423 | _constant_length(const char *s, std::size_t index = 0) 424 | { 425 | return _ends_name(s[index]) ? index : _constant_length(s, index + 1); 426 | } 427 | 428 | BETTER_ENUMS_CONSTEXPR_ inline char 429 | _select(const char *from, std::size_t from_length, std::size_t index) 430 | { 431 | return index >= from_length ? '\0' : from[index]; 432 | } 433 | 434 | BETTER_ENUMS_CONSTEXPR_ inline char _to_lower_ascii(char c) 435 | { 436 | return c >= 0x41 && c <= 0x5A ? static_cast(c + 0x20) : c; 437 | } 438 | 439 | BETTER_ENUMS_CONSTEXPR_ inline bool _names_match(const char *stringizedName, 440 | const char *referenceName, 441 | std::size_t index = 0) 442 | { 443 | return 444 | _ends_name(stringizedName[index]) ? referenceName[index] == '\0' : 445 | referenceName[index] == '\0' ? false : 446 | stringizedName[index] != referenceName[index] ? false : 447 | _names_match(stringizedName, referenceName, index + 1); 448 | } 449 | 450 | BETTER_ENUMS_CONSTEXPR_ inline bool 451 | _names_match_nocase(const char *stringizedName, const char *referenceName, 452 | std::size_t index = 0) 453 | { 454 | return 455 | _ends_name(stringizedName[index]) ? referenceName[index] == '\0' : 456 | referenceName[index] == '\0' ? false : 457 | _to_lower_ascii(stringizedName[index]) != 458 | _to_lower_ascii(referenceName[index]) ? false : 459 | _names_match_nocase(stringizedName, referenceName, index + 1); 460 | } 461 | 462 | inline void _trim_names(const char * const *raw_names, 463 | const char **trimmed_names, 464 | char *storage, std::size_t count) 465 | { 466 | std::size_t offset = 0; 467 | 468 | for (std::size_t index = 0; index < count; ++index) { 469 | trimmed_names[index] = storage + offset; 470 | 471 | std::size_t trimmed_length = 472 | std::strcspn(raw_names[index], _name_enders); 473 | storage[offset + trimmed_length] = '\0'; 474 | 475 | std::size_t raw_length = std::strlen(raw_names[index]); 476 | offset += raw_length + 1; 477 | } 478 | } 479 | 480 | 481 | 482 | // Eager initialization. 483 | template 484 | struct _initialize_at_program_start { 485 | _initialize_at_program_start() { Enum::initialize(); } 486 | }; 487 | 488 | } // namespace better_enums 489 | 490 | 491 | 492 | // Array generation macros. 493 | 494 | #define BETTER_ENUMS_EAT_ASSIGN_SINGLE(EnumType, index, expression) \ 495 | ((::better_enums::_eat_assign)EnumType::expression), 496 | 497 | #define BETTER_ENUMS_EAT_ASSIGN(EnumType, ...) \ 498 | BETTER_ENUMS_ID( \ 499 | BETTER_ENUMS_PP_MAP( \ 500 | BETTER_ENUMS_EAT_ASSIGN_SINGLE, EnumType, __VA_ARGS__)) 501 | 502 | 503 | 504 | #ifdef BETTER_ENUMS_HAVE_CONSTEXPR 505 | 506 | 507 | 508 | #define BETTER_ENUMS_SELECT_SINGLE_CHARACTER(from, from_length, index) \ 509 | ::better_enums::_select(from, from_length, index), 510 | 511 | #define BETTER_ENUMS_SELECT_CHARACTERS(from, from_length) \ 512 | BETTER_ENUMS_ITERATE( \ 513 | BETTER_ENUMS_SELECT_SINGLE_CHARACTER, from, from_length) 514 | 515 | 516 | 517 | #define BETTER_ENUMS_TRIM_SINGLE_STRING(ignored, index, expression) \ 518 | constexpr std::size_t _length_ ## index = \ 519 | ::better_enums::_constant_length(#expression); \ 520 | constexpr const char _trimmed_ ## index [] = \ 521 | { BETTER_ENUMS_SELECT_CHARACTERS(#expression, _length_ ## index) }; \ 522 | constexpr const char *_final_ ## index = \ 523 | ::better_enums::_has_initializer(#expression) ? \ 524 | _trimmed_ ## index : #expression; 525 | 526 | #define BETTER_ENUMS_TRIM_STRINGS(...) \ 527 | BETTER_ENUMS_ID( \ 528 | BETTER_ENUMS_PP_MAP( \ 529 | BETTER_ENUMS_TRIM_SINGLE_STRING, ignored, __VA_ARGS__)) 530 | 531 | 532 | 533 | #define BETTER_ENUMS_REFER_TO_SINGLE_STRING(ignored, index, expression) \ 534 | _final_ ## index, 535 | 536 | #define BETTER_ENUMS_REFER_TO_STRINGS(...) \ 537 | BETTER_ENUMS_ID( \ 538 | BETTER_ENUMS_PP_MAP( \ 539 | BETTER_ENUMS_REFER_TO_SINGLE_STRING, ignored, __VA_ARGS__)) 540 | 541 | 542 | 543 | #endif // #ifdef BETTER_ENUMS_HAVE_CONSTEXPR 544 | 545 | 546 | 547 | #define BETTER_ENUMS_STRINGIZE_SINGLE(ignored, index, expression) #expression, 548 | 549 | #define BETTER_ENUMS_STRINGIZE(...) \ 550 | BETTER_ENUMS_ID( \ 551 | BETTER_ENUMS_PP_MAP( \ 552 | BETTER_ENUMS_STRINGIZE_SINGLE, ignored, __VA_ARGS__)) 553 | 554 | #define BETTER_ENUMS_RESERVE_STORAGE_SINGLE(ignored, index, expression) \ 555 | #expression "," 556 | 557 | #define BETTER_ENUMS_RESERVE_STORAGE(...) \ 558 | BETTER_ENUMS_ID( \ 559 | BETTER_ENUMS_PP_MAP( \ 560 | BETTER_ENUMS_RESERVE_STORAGE_SINGLE, ignored, __VA_ARGS__)) 561 | 562 | 563 | 564 | // The enums proper. 565 | 566 | #define BETTER_ENUMS_NS(EnumType) better_enums_data_ ## EnumType 567 | 568 | #ifdef BETTER_ENUMS_VC2008_WORKAROUNDS 569 | 570 | #define BETTER_ENUMS_COPY_CONSTRUCTOR(Enum) \ 571 | BETTER_ENUMS_CONSTEXPR_ Enum(const Enum &other) : \ 572 | _value(other._value) { } 573 | 574 | #else 575 | 576 | #define BETTER_ENUMS_COPY_CONSTRUCTOR(Enum) 577 | 578 | #endif 579 | 580 | #define BETTER_ENUMS_TYPE(SetUnderlyingType, SwitchType, GenerateSwitchType, \ 581 | GenerateStrings, ToStringConstexpr, \ 582 | DeclareInitialize, DefineInitialize, CallInitialize, \ 583 | Enum, Underlying, ...) \ 584 | \ 585 | namespace better_enums_data_ ## Enum { \ 586 | \ 587 | BETTER_ENUMS_ID(GenerateSwitchType(Underlying, __VA_ARGS__)) \ 588 | \ 589 | } \ 590 | \ 591 | class Enum { \ 592 | private: \ 593 | typedef ::better_enums::optional _optional; \ 594 | typedef ::better_enums::optional _optional_index; \ 595 | \ 596 | public: \ 597 | typedef Underlying _integral; \ 598 | \ 599 | enum _enumerated SetUnderlyingType(Underlying) { __VA_ARGS__ }; \ 600 | \ 601 | BETTER_ENUMS_CONSTEXPR_ Enum(_enumerated value) : _value(value) { } \ 602 | \ 603 | BETTER_ENUMS_COPY_CONSTRUCTOR(Enum) \ 604 | \ 605 | BETTER_ENUMS_CONSTEXPR_ operator SwitchType(Enum)() const \ 606 | { \ 607 | return SwitchType(Enum)(_value); \ 608 | } \ 609 | \ 610 | BETTER_ENUMS_CONSTEXPR_ _integral _to_integral() const; \ 611 | BETTER_ENUMS_IF_EXCEPTIONS( \ 612 | BETTER_ENUMS_CONSTEXPR_ static Enum _from_integral(_integral value); \ 613 | ) \ 614 | BETTER_ENUMS_CONSTEXPR_ static Enum \ 615 | _from_integral_unchecked(_integral value); \ 616 | BETTER_ENUMS_CONSTEXPR_ static _optional \ 617 | _from_integral_nothrow(_integral value); \ 618 | \ 619 | ToStringConstexpr const char* _to_string() const; \ 620 | BETTER_ENUMS_IF_EXCEPTIONS( \ 621 | BETTER_ENUMS_CONSTEXPR_ static Enum _from_string(const char *name); \ 622 | ) \ 623 | BETTER_ENUMS_CONSTEXPR_ static _optional \ 624 | _from_string_nothrow(const char *name); \ 625 | \ 626 | BETTER_ENUMS_IF_EXCEPTIONS( \ 627 | BETTER_ENUMS_CONSTEXPR_ static Enum _from_string_nocase(const char *name); \ 628 | ) \ 629 | BETTER_ENUMS_CONSTEXPR_ static _optional \ 630 | _from_string_nocase_nothrow(const char *name); \ 631 | \ 632 | BETTER_ENUMS_CONSTEXPR_ static bool _is_valid(_integral value); \ 633 | BETTER_ENUMS_CONSTEXPR_ static bool _is_valid(const char *name); \ 634 | BETTER_ENUMS_CONSTEXPR_ static bool _is_valid_nocase(const char *name); \ 635 | \ 636 | typedef ::better_enums::_Iterable _value_iterable; \ 637 | typedef ::better_enums::_Iterable _name_iterable; \ 638 | \ 639 | typedef _value_iterable::iterator _value_iterator; \ 640 | typedef _name_iterable::iterator _name_iterator; \ 641 | \ 642 | BETTER_ENUMS_CONSTEXPR_ static const std::size_t _size_constant = \ 643 | BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT(__VA_ARGS__)); \ 644 | BETTER_ENUMS_CONSTEXPR_ static std::size_t _size() \ 645 | { return _size_constant; } \ 646 | \ 647 | BETTER_ENUMS_CONSTEXPR_ static const char* _name(); \ 648 | BETTER_ENUMS_CONSTEXPR_ static _value_iterable _values(); \ 649 | ToStringConstexpr static _name_iterable _names(); \ 650 | \ 651 | _integral _value; \ 652 | \ 653 | BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \ 654 | \ 655 | private: \ 656 | explicit BETTER_ENUMS_CONSTEXPR_ Enum(const _integral &value) : \ 657 | _value(value) { } \ 658 | \ 659 | DeclareInitialize \ 660 | \ 661 | BETTER_ENUMS_CONSTEXPR_ static _optional_index \ 662 | _from_value_loop(_integral value, std::size_t index = 0); \ 663 | BETTER_ENUMS_CONSTEXPR_ static _optional_index \ 664 | _from_string_loop(const char *name, std::size_t index = 0); \ 665 | BETTER_ENUMS_CONSTEXPR_ static _optional_index \ 666 | _from_string_nocase_loop(const char *name, std::size_t index = 0); \ 667 | \ 668 | friend struct ::better_enums::_initialize_at_program_start; \ 669 | }; \ 670 | \ 671 | namespace better_enums_data_ ## Enum { \ 672 | \ 673 | static ::better_enums::_initialize_at_program_start \ 674 | _force_initialization; \ 675 | \ 676 | enum _PutNamesInThisScopeAlso { __VA_ARGS__ }; \ 677 | \ 678 | BETTER_ENUMS_CONSTEXPR_ const Enum _value_array[] = \ 679 | { BETTER_ENUMS_ID(BETTER_ENUMS_EAT_ASSIGN(Enum, __VA_ARGS__)) }; \ 680 | \ 681 | BETTER_ENUMS_ID(GenerateStrings(Enum, __VA_ARGS__)) \ 682 | \ 683 | } \ 684 | \ 685 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 686 | inline const Enum \ 687 | operator +(Enum::_enumerated enumerated) \ 688 | { \ 689 | return static_cast(enumerated); \ 690 | } \ 691 | \ 692 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index \ 693 | Enum::_from_value_loop(Enum::_integral value, std::size_t index) \ 694 | { \ 695 | return \ 696 | index == _size() ? \ 697 | _optional_index() : \ 698 | BETTER_ENUMS_NS(Enum)::_value_array[index]._value == value ? \ 699 | _optional_index(index) : \ 700 | _from_value_loop(value, index + 1); \ 701 | } \ 702 | \ 703 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index \ 704 | Enum::_from_string_loop(const char *name, std::size_t index) \ 705 | { \ 706 | return \ 707 | index == _size() ? _optional_index() : \ 708 | ::better_enums::_names_match( \ 709 | BETTER_ENUMS_NS(Enum)::_raw_names()[index], name) ? \ 710 | _optional_index(index) : \ 711 | _from_string_loop(name, index + 1); \ 712 | } \ 713 | \ 714 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index \ 715 | Enum::_from_string_nocase_loop(const char *name, std::size_t index) \ 716 | { \ 717 | return \ 718 | index == _size() ? _optional_index() : \ 719 | ::better_enums::_names_match_nocase( \ 720 | BETTER_ENUMS_NS(Enum)::_raw_names()[index], name) ? \ 721 | _optional_index(index) : \ 722 | _from_string_nocase_loop(name, index + 1); \ 723 | } \ 724 | \ 725 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_integral Enum::_to_integral() const \ 726 | { \ 727 | return _integral(_value); \ 728 | } \ 729 | \ 730 | BETTER_ENUMS_CONSTEXPR_ inline Enum \ 731 | Enum::_from_integral_unchecked(_integral value) \ 732 | { \ 733 | return static_cast<_enumerated>(value); \ 734 | } \ 735 | \ 736 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ 737 | Enum::_from_integral_nothrow(_integral value) \ 738 | { \ 739 | return \ 740 | ::better_enums::_map_index(BETTER_ENUMS_NS(Enum)::_value_array, \ 741 | _from_value_loop(value)); \ 742 | } \ 743 | \ 744 | BETTER_ENUMS_IF_EXCEPTIONS( \ 745 | BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_integral(_integral value) \ 746 | { \ 747 | return \ 748 | ::better_enums::_or_throw(_from_integral_nothrow(value), \ 749 | #Enum "::_from_integral: invalid argument"); \ 750 | } \ 751 | ) \ 752 | \ 753 | ToStringConstexpr inline const char* Enum::_to_string() const \ 754 | { \ 755 | return \ 756 | ::better_enums::_or_null( \ 757 | ::better_enums::_map_index( \ 758 | BETTER_ENUMS_NS(Enum)::_name_array(), \ 759 | _from_value_loop(CallInitialize(_value)))); \ 760 | } \ 761 | \ 762 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ 763 | Enum::_from_string_nothrow(const char *name) \ 764 | { \ 765 | return \ 766 | ::better_enums::_map_index( \ 767 | BETTER_ENUMS_NS(Enum)::_value_array, _from_string_loop(name)); \ 768 | } \ 769 | \ 770 | BETTER_ENUMS_IF_EXCEPTIONS( \ 771 | BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_string(const char *name) \ 772 | { \ 773 | return \ 774 | ::better_enums::_or_throw(_from_string_nothrow(name), \ 775 | #Enum "::_from_string: invalid argument"); \ 776 | } \ 777 | ) \ 778 | \ 779 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ 780 | Enum::_from_string_nocase_nothrow(const char *name) \ 781 | { \ 782 | return \ 783 | ::better_enums::_map_index(BETTER_ENUMS_NS(Enum)::_value_array, \ 784 | _from_string_nocase_loop(name)); \ 785 | } \ 786 | \ 787 | BETTER_ENUMS_IF_EXCEPTIONS( \ 788 | BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_string_nocase(const char *name)\ 789 | { \ 790 | return \ 791 | ::better_enums::_or_throw( \ 792 | _from_string_nocase_nothrow(name), \ 793 | #Enum "::_from_string_nocase: invalid argument"); \ 794 | } \ 795 | ) \ 796 | \ 797 | BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid(_integral value) \ 798 | { \ 799 | return _from_value_loop(value); \ 800 | } \ 801 | \ 802 | BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid(const char *name) \ 803 | { \ 804 | return _from_string_loop(name); \ 805 | } \ 806 | \ 807 | BETTER_ENUMS_CONSTEXPR_ inline bool Enum::_is_valid_nocase(const char *name) \ 808 | { \ 809 | return _from_string_nocase_loop(name); \ 810 | } \ 811 | \ 812 | BETTER_ENUMS_CONSTEXPR_ inline const char* Enum::_name() \ 813 | { \ 814 | return #Enum; \ 815 | } \ 816 | \ 817 | BETTER_ENUMS_CONSTEXPR_ inline Enum::_value_iterable Enum::_values() \ 818 | { \ 819 | return _value_iterable(BETTER_ENUMS_NS(Enum)::_value_array, _size()); \ 820 | } \ 821 | \ 822 | ToStringConstexpr inline Enum::_name_iterable Enum::_names() \ 823 | { \ 824 | return \ 825 | _name_iterable(BETTER_ENUMS_NS(Enum)::_name_array(), \ 826 | CallInitialize(_size())); \ 827 | } \ 828 | \ 829 | DefineInitialize(Enum) \ 830 | \ 831 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 832 | inline bool operator ==(const Enum &a, const Enum &b) \ 833 | { return a._to_integral() == b._to_integral(); } \ 834 | \ 835 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 836 | inline bool operator !=(const Enum &a, const Enum &b) \ 837 | { return a._to_integral() != b._to_integral(); } \ 838 | \ 839 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 840 | inline bool operator <(const Enum &a, const Enum &b) \ 841 | { return a._to_integral() < b._to_integral(); } \ 842 | \ 843 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 844 | inline bool operator <=(const Enum &a, const Enum &b) \ 845 | { return a._to_integral() <= b._to_integral(); } \ 846 | \ 847 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 848 | inline bool operator >(const Enum &a, const Enum &b) \ 849 | { return a._to_integral() > b._to_integral(); } \ 850 | \ 851 | BETTER_ENUMS_UNUSED BETTER_ENUMS_CONSTEXPR_ \ 852 | inline bool operator >=(const Enum &a, const Enum &b) \ 853 | { return a._to_integral() >= b._to_integral(); } \ 854 | \ 855 | \ 856 | template \ 857 | std::basic_ostream& \ 858 | operator <<(std::basic_ostream& stream, const Enum &value) \ 859 | { \ 860 | return stream << value._to_string(); \ 861 | } \ 862 | \ 863 | template \ 864 | std::basic_istream& \ 865 | operator >>(std::basic_istream& stream, Enum &value) \ 866 | { \ 867 | std::basic_string buffer; \ 868 | \ 869 | stream >> buffer; \ 870 | ::better_enums::optional converted = \ 871 | Enum::_from_string_nothrow(buffer.c_str()); \ 872 | \ 873 | if (converted) \ 874 | value = *converted; \ 875 | else \ 876 | stream.setstate(std::basic_istream::failbit); \ 877 | \ 878 | return stream; \ 879 | } 880 | 881 | 882 | 883 | // Enum feature options. 884 | 885 | // C++98, C++11 886 | #define BETTER_ENUMS_CXX98_UNDERLYING_TYPE(Underlying) 887 | 888 | // C++11 889 | #define BETTER_ENUMS_CXX11_UNDERLYING_TYPE(Underlying) \ 890 | : Underlying 891 | 892 | #if defined(_MSC_VER) && _MSC_VER >= 1700 893 | // VS 2012 and above fully support strongly typed enums and will warn about 894 | // incorrect usage. 895 | # define BETTER_ENUMS_LEGACY_UNDERLYING_TYPE(Underlying) BETTER_ENUMS_CXX11_UNDERLYING_TYPE(Underlying) 896 | #else 897 | # define BETTER_ENUMS_LEGACY_UNDERLYING_TYPE(Underlying) BETTER_ENUMS_CXX98_UNDERLYING_TYPE(Underlying) 898 | #endif 899 | 900 | // C++98, C++11 901 | #define BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE(Type) \ 902 | _enumerated 903 | 904 | // C++11 905 | #define BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE(Type) \ 906 | BETTER_ENUMS_NS(Type)::_EnumClassForSwitchStatements 907 | 908 | // C++98, C++11 909 | #define BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE_GENERATE(Underlying, ...) 910 | 911 | // C++11 912 | #define BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE_GENERATE(Underlying, ...) \ 913 | enum class _EnumClassForSwitchStatements : Underlying { __VA_ARGS__ }; 914 | 915 | // C++98 916 | #define BETTER_ENUMS_CXX98_TRIM_STRINGS_ARRAYS(Enum, ...) \ 917 | inline const char** _raw_names() \ 918 | { \ 919 | static const char *value[] = \ 920 | { BETTER_ENUMS_ID(BETTER_ENUMS_STRINGIZE(__VA_ARGS__)) }; \ 921 | return value; \ 922 | } \ 923 | \ 924 | inline char* _name_storage() \ 925 | { \ 926 | static char storage[] = \ 927 | BETTER_ENUMS_ID(BETTER_ENUMS_RESERVE_STORAGE(__VA_ARGS__)); \ 928 | return storage; \ 929 | } \ 930 | \ 931 | inline const char** _name_array() \ 932 | { \ 933 | static const char *value[Enum::_size_constant]; \ 934 | return value; \ 935 | } \ 936 | \ 937 | inline bool& _initialized() \ 938 | { \ 939 | static bool value = false; \ 940 | return value; \ 941 | } 942 | 943 | // C++11 fast version 944 | #define BETTER_ENUMS_CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...) \ 945 | constexpr const char *_the_raw_names[] = \ 946 | { BETTER_ENUMS_ID(BETTER_ENUMS_STRINGIZE(__VA_ARGS__)) }; \ 947 | \ 948 | constexpr const char * const * _raw_names() \ 949 | { \ 950 | return _the_raw_names; \ 951 | } \ 952 | \ 953 | inline char* _name_storage() \ 954 | { \ 955 | static char storage[] = \ 956 | BETTER_ENUMS_ID(BETTER_ENUMS_RESERVE_STORAGE(__VA_ARGS__)); \ 957 | return storage; \ 958 | } \ 959 | \ 960 | inline const char** _name_array() \ 961 | { \ 962 | static const char *value[Enum::_size_constant]; \ 963 | return value; \ 964 | } \ 965 | \ 966 | inline bool& _initialized() \ 967 | { \ 968 | static bool value = false; \ 969 | return value; \ 970 | } 971 | 972 | // C++11 slow all-constexpr version 973 | #define BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS(Enum, ...) \ 974 | BETTER_ENUMS_ID(BETTER_ENUMS_TRIM_STRINGS(__VA_ARGS__)) \ 975 | \ 976 | constexpr const char * const _the_name_array[] = \ 977 | { BETTER_ENUMS_ID(BETTER_ENUMS_REFER_TO_STRINGS(__VA_ARGS__)) }; \ 978 | \ 979 | constexpr const char * const * _name_array() \ 980 | { \ 981 | return _the_name_array; \ 982 | } \ 983 | \ 984 | constexpr const char * const * _raw_names() \ 985 | { \ 986 | return _the_name_array; \ 987 | } 988 | 989 | // C++98, C++11 fast version 990 | #define BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD 991 | 992 | // C++11 slow all-constexpr version 993 | #define BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD \ 994 | constexpr 995 | 996 | // C++98, C++11 fast version 997 | #define BETTER_ENUMS_DO_DECLARE_INITIALIZE \ 998 | static int initialize(); 999 | 1000 | // C++11 slow all-constexpr version 1001 | #define BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE \ 1002 | static int initialize() { return 0; } 1003 | 1004 | // C++98, C++11 fast version 1005 | #define BETTER_ENUMS_DO_DEFINE_INITIALIZE(Enum) \ 1006 | inline int Enum::initialize() \ 1007 | { \ 1008 | if (BETTER_ENUMS_NS(Enum)::_initialized()) \ 1009 | return 0; \ 1010 | \ 1011 | ::better_enums::_trim_names(BETTER_ENUMS_NS(Enum)::_raw_names(), \ 1012 | BETTER_ENUMS_NS(Enum)::_name_array(), \ 1013 | BETTER_ENUMS_NS(Enum)::_name_storage(), \ 1014 | _size()); \ 1015 | \ 1016 | BETTER_ENUMS_NS(Enum)::_initialized() = true; \ 1017 | \ 1018 | return 0; \ 1019 | } 1020 | 1021 | // C++11 slow all-constexpr version 1022 | #define BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE(Enum) 1023 | 1024 | // C++98, C++11 fast version 1025 | #define BETTER_ENUMS_DO_CALL_INITIALIZE(value) \ 1026 | ::better_enums::continue_with(initialize(), value) 1027 | 1028 | // C++11 slow all-constexpr version 1029 | #define BETTER_ENUMS_DO_NOT_CALL_INITIALIZE(value) \ 1030 | value 1031 | 1032 | 1033 | 1034 | // User feature selection. 1035 | 1036 | #ifdef BETTER_ENUMS_STRICT_CONVERSION 1037 | # define BETTER_ENUMS_DEFAULT_SWITCH_TYPE \ 1038 | BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE 1039 | # define BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE \ 1040 | BETTER_ENUMS_ENUM_CLASS_SWITCH_TYPE_GENERATE 1041 | #else 1042 | # define BETTER_ENUMS_DEFAULT_SWITCH_TYPE \ 1043 | BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE 1044 | # define BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE \ 1045 | BETTER_ENUMS_REGULAR_ENUM_SWITCH_TYPE_GENERATE 1046 | #endif 1047 | 1048 | 1049 | 1050 | #ifndef BETTER_ENUMS_DEFAULT_CONSTRUCTOR 1051 | # define BETTER_ENUMS_DEFAULT_CONSTRUCTOR(Enum) \ 1052 | private: \ 1053 | Enum() : _value(0) { } 1054 | #endif 1055 | 1056 | 1057 | 1058 | #ifdef BETTER_ENUMS_HAVE_CONSTEXPR 1059 | 1060 | #ifdef BETTER_ENUMS_CONSTEXPR_TO_STRING 1061 | # define BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS \ 1062 | BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS 1063 | # define BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD \ 1064 | BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD 1065 | # define BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE \ 1066 | BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE 1067 | # define BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE \ 1068 | BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE 1069 | # define BETTER_ENUMS_DEFAULT_CALL_INITIALIZE \ 1070 | BETTER_ENUMS_DO_NOT_CALL_INITIALIZE 1071 | #else 1072 | # define BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS \ 1073 | BETTER_ENUMS_CXX11_PARTIAL_CONSTEXPR_TRIM_STRINGS_ARRAYS 1074 | # define BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD \ 1075 | BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD 1076 | # define BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE \ 1077 | BETTER_ENUMS_DO_DECLARE_INITIALIZE 1078 | # define BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE \ 1079 | BETTER_ENUMS_DO_DEFINE_INITIALIZE 1080 | # define BETTER_ENUMS_DEFAULT_CALL_INITIALIZE \ 1081 | BETTER_ENUMS_DO_CALL_INITIALIZE 1082 | #endif 1083 | 1084 | 1085 | 1086 | // Top-level macros. 1087 | 1088 | #define BETTER_ENUM(Enum, Underlying, ...) \ 1089 | BETTER_ENUMS_ID(BETTER_ENUMS_TYPE( \ 1090 | BETTER_ENUMS_CXX11_UNDERLYING_TYPE, \ 1091 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE, \ 1092 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE, \ 1093 | BETTER_ENUMS_DEFAULT_TRIM_STRINGS_ARRAYS, \ 1094 | BETTER_ENUMS_DEFAULT_TO_STRING_KEYWORD, \ 1095 | BETTER_ENUMS_DEFAULT_DECLARE_INITIALIZE, \ 1096 | BETTER_ENUMS_DEFAULT_DEFINE_INITIALIZE, \ 1097 | BETTER_ENUMS_DEFAULT_CALL_INITIALIZE, \ 1098 | Enum, Underlying, __VA_ARGS__)) 1099 | 1100 | #define SLOW_ENUM(Enum, Underlying, ...) \ 1101 | BETTER_ENUMS_ID(BETTER_ENUMS_TYPE( \ 1102 | BETTER_ENUMS_CXX11_UNDERLYING_TYPE, \ 1103 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE, \ 1104 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE, \ 1105 | BETTER_ENUMS_CXX11_FULL_CONSTEXPR_TRIM_STRINGS_ARRAYS, \ 1106 | BETTER_ENUMS_CONSTEXPR_TO_STRING_KEYWORD, \ 1107 | BETTER_ENUMS_DECLARE_EMPTY_INITIALIZE, \ 1108 | BETTER_ENUMS_DO_NOT_DEFINE_INITIALIZE, \ 1109 | BETTER_ENUMS_DO_NOT_CALL_INITIALIZE, \ 1110 | Enum, Underlying, __VA_ARGS__)) 1111 | 1112 | #else 1113 | 1114 | #define BETTER_ENUM(Enum, Underlying, ...) \ 1115 | BETTER_ENUMS_ID(BETTER_ENUMS_TYPE( \ 1116 | BETTER_ENUMS_LEGACY_UNDERLYING_TYPE, \ 1117 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE, \ 1118 | BETTER_ENUMS_DEFAULT_SWITCH_TYPE_GENERATE, \ 1119 | BETTER_ENUMS_CXX98_TRIM_STRINGS_ARRAYS, \ 1120 | BETTER_ENUMS_NO_CONSTEXPR_TO_STRING_KEYWORD, \ 1121 | BETTER_ENUMS_DO_DECLARE_INITIALIZE, \ 1122 | BETTER_ENUMS_DO_DEFINE_INITIALIZE, \ 1123 | BETTER_ENUMS_DO_CALL_INITIALIZE, \ 1124 | Enum, Underlying, __VA_ARGS__)) 1125 | 1126 | #endif 1127 | 1128 | 1129 | 1130 | namespace better_enums { 1131 | 1132 | // Maps. 1133 | 1134 | template 1135 | struct map_compare { 1136 | BETTER_ENUMS_CONSTEXPR_ static bool less(const T& a, const T& b) 1137 | { return a < b; } 1138 | }; 1139 | 1140 | template <> 1141 | struct map_compare { 1142 | BETTER_ENUMS_CONSTEXPR_ static bool less(const char *a, const char *b) 1143 | { return less_loop(a, b); } 1144 | 1145 | private: 1146 | BETTER_ENUMS_CONSTEXPR_ static bool 1147 | less_loop(const char *a, const char *b, size_t index = 0) 1148 | { 1149 | return 1150 | a[index] != b[index] ? a[index] < b[index] : 1151 | a[index] == '\0' ? false : 1152 | less_loop(a, b, index + 1); 1153 | } 1154 | }; 1155 | 1156 | // chenyao add 1157 | template <> 1158 | struct map_compare { 1159 | BETTER_ENUMS_CONSTEXPR_ static bool less(const wchar_t *a, const wchar_t *b) 1160 | { return less_loop(a, b); } 1161 | 1162 | private: 1163 | BETTER_ENUMS_CONSTEXPR_ static bool 1164 | less_loop(const wchar_t *a, const wchar_t *b, size_t index = 0) 1165 | { 1166 | return 1167 | a[index] != b[index] ? a[index] < b[index] : 1168 | a[index] == L'\0' ? false : 1169 | less_loop(a, b, index + 1); 1170 | } 1171 | }; 1172 | 1173 | template > 1174 | struct map { 1175 | typedef T (*function)(Enum); 1176 | 1177 | BETTER_ENUMS_CONSTEXPR_ explicit map(function f) : _f(f) { } 1178 | 1179 | BETTER_ENUMS_CONSTEXPR_ T from_enum(Enum value) const { return _f(value); } 1180 | BETTER_ENUMS_CONSTEXPR_ T operator [](Enum value) const 1181 | { return _f(value); } 1182 | 1183 | BETTER_ENUMS_CONSTEXPR_ Enum to_enum(T value) const 1184 | { 1185 | return 1186 | _or_throw(to_enum_nothrow(value), "map::to_enum: invalid argument"); 1187 | } 1188 | 1189 | BETTER_ENUMS_CONSTEXPR_ optional 1190 | to_enum_nothrow(T value, size_t index = 0) const 1191 | { 1192 | return 1193 | index >= Enum::_size() ? optional() : 1194 | Compare::less(_f(Enum::_values()[index]), value) || 1195 | Compare::less(value, _f(Enum::_values()[index])) ? 1196 | to_enum_nothrow(value, index + 1) : 1197 | Enum::_values()[index]; 1198 | } 1199 | 1200 | private: 1201 | const function _f; 1202 | }; 1203 | 1204 | template 1205 | BETTER_ENUMS_CONSTEXPR_ map make_map(T (*f)(Enum)) 1206 | { 1207 | return map(f); 1208 | } 1209 | 1210 | } 1211 | 1212 | #endif // #ifndef BETTER_ENUMS_ENUM_H 1213 | -------------------------------------------------------------------------------- /include/procmap/Logger.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 João Neto 3 | * A simple informative logger 4 | **/ 5 | #ifndef UNSTICKYMEM_LOGGER_HPP_ 6 | #define UNSTICKYMEM_LOGGER_HPP_ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "better-enums/enum.h" 17 | 18 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : \ 19 | __FILE__) 20 | #define LFMT "\033[2m%22s:%-4d %-28s\033[m " 21 | #define LTRACE(msg) \ 22 | L->trace(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 23 | #define LDEBUG(msg) \ 24 | L->debug(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 25 | #define LINFO(msg) \ 26 | L->info(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 27 | #define LWARN(msg) \ 28 | L->warn(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 29 | #define LERROR(msg) \ 30 | L->error(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 31 | #define LFATAL(msg) \ 32 | L->fatal(LFMT "%s", __FILENAME__, __LINE__, __FUNCTION__, msg); 33 | 34 | #define LTRACEF(fmt, ...) \ 35 | L->trace(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 36 | #define LDEBUGF(fmt, ...) \ 37 | L->debug(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 38 | #define LINFOF(fmt, ...) \ 39 | L->info(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 40 | #define LWARNF(fmt, ...) \ 41 | L->warn(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 42 | #define LERRORF(fmt, ...) \ 43 | L->error(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 44 | #define LFATALF(fmt, ...) \ 45 | L->fatal(LFMT fmt, __FILENAME__, __LINE__, __FUNCTION__, __VA_ARGS__); 46 | 47 | #define DIE(msg) \ 48 | LFATAL(msg);\ 49 | perror("perror");\ 50 | ::abort(); 51 | 52 | #define DIEIF(expr, msg) \ 53 | do {\ 54 | if (expr) {\ 55 | DIE(msg);\ 56 | }\ 57 | } while (0) 58 | 59 | namespace procmap { 60 | 61 | BETTER_ENUM(LogLevel, int, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF) 62 | 63 | static const char *loglevel_colors[] = { 64 | "\033[37m", // trace: white 65 | "\033[36m", // debug: cyan 66 | "\033[32m", // info: green 67 | "\033[43m\033[1m", // warn: yellow bold 68 | "\033[31m\033[1m", // error: red bold 69 | "\033[1m\033[41m", // fatal: bold on red 70 | "\033[m" // off: none 71 | }; 72 | 73 | class Logger { 74 | private: 75 | struct timespec startTime; 76 | 77 | public: 78 | Logger() { 79 | clock_gettime(CLOCK_MONOTONIC, &startTime); 80 | printHeaderRow(); 81 | } 82 | 83 | inline void set_log_level(LogLevel level) { 84 | _loglevel = level; 85 | } 86 | 87 | inline void printHeaderRow() { 88 | printf("\033[1m\033[41m%-15s %-6s %5s %27s %-28s %-74s\033[m\n", 89 | "TIME", "PID", "LEVEL", "FILENAME:LINE", "FUNCTION", "MESSAGE"); 90 | } 91 | 92 | inline void printHorizontalRule(std::string msg = "", int bgcolor = 1) { 93 | printf("\033[4%dm%-160s\033[m\n", bgcolor % 8, msg.c_str()); 94 | } 95 | 96 | template 97 | inline void trace(const char *fmt, const Arg1 &arg1, const Args &... args) { 98 | log(LogLevel::TRACE, fmt, arg1, args...); 99 | } 100 | 101 | template 102 | inline void debug(const char *fmt, const Arg1 &arg1, const Args &... args) { 103 | log(LogLevel::DEBUG, fmt, arg1, args...); 104 | } 105 | 106 | template 107 | inline void info(const char *fmt, const Arg1 &arg1, const Args &... args) { 108 | log(LogLevel::INFO, fmt, arg1, args...); 109 | } 110 | 111 | template 112 | inline void warn(const char *fmt, const Arg1 &arg1, const Args &... args) { 113 | log(LogLevel::WARN, fmt, arg1, args...); 114 | } 115 | 116 | template 117 | inline void error(const char *fmt, const Arg1 &arg1, const Args &... args) { 118 | log(LogLevel::ERROR, fmt, arg1, args...); 119 | } 120 | 121 | template 122 | inline void fatal(const char *fmt, const Arg1 &arg1, const Args &... args) { 123 | log(LogLevel::FATAL, fmt, arg1, args...); 124 | } 125 | 126 | template 127 | inline void trace(const T &msg) { 128 | log(LogLevel::TRACE, msg); 129 | } 130 | 131 | template 132 | inline void debug(const T &msg) { 133 | log(LogLevel::DEBUG, msg); 134 | } 135 | 136 | template 137 | inline void info(const T &msg) { 138 | log(LogLevel::INFO, msg); 139 | } 140 | 141 | template 142 | inline void warn(const T &msg) { 143 | log(LogLevel::WARN, msg); 144 | } 145 | 146 | template 147 | inline void error(const T &msg) { 148 | log(LogLevel::ERROR, msg); 149 | } 150 | 151 | template 152 | inline void fatal(const T &msg) { 153 | log(LogLevel::FATAL, msg); 154 | } 155 | 156 | private: 157 | LogLevel _loglevel = LogLevel::TRACE; 158 | 159 | inline bool should_log(LogLevel lvl) { 160 | return lvl >= _loglevel; 161 | } 162 | 163 | inline void getElapsedTimeString(char *str, size_t len) { 164 | struct timespec now; 165 | clock_gettime(CLOCK_MONOTONIC, &now); 166 | struct timespec elapsed; 167 | elapsed.tv_sec = now.tv_sec - startTime.tv_sec; 168 | elapsed.tv_nsec = now.tv_nsec - startTime.tv_nsec; 169 | if (now.tv_nsec < startTime.tv_nsec) { 170 | elapsed.tv_sec -= 1; 171 | elapsed.tv_nsec += 1000000000; 172 | } 173 | snprintf(str, len, "%05" PRIdMAX ".%09ld", elapsed.tv_sec, elapsed.tv_nsec); 174 | } 175 | 176 | template 177 | inline void log(LogLevel lvl, const char *fmt, const Args &... args) { 178 | if (!should_log(lvl)) { 179 | return; 180 | } 181 | char time_str[32]; 182 | getElapsedTimeString(time_str, sizeof(time_str)); 183 | printf("\033[2m%s \033[4%dm%6d\033[m %s%5s\033[m ", 184 | time_str, 185 | getpid() % 8, getpid(), 186 | loglevel_colors[lvl], lvl._to_string()); 187 | printf(fmt, args...); 188 | printf("\n"); 189 | } 190 | 191 | template 192 | inline void log(LogLevel lvl, const char *msg) { 193 | log(lvl, "%s", msg); 194 | } 195 | }; 196 | 197 | } // namespace procmap 198 | 199 | extern procmap::Logger* L; 200 | 201 | #endif // UNSTICKYMEM_LOGGER_HPP_ 202 | -------------------------------------------------------------------------------- /include/procmap/MemoryMap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCMAP_MEMORY_MAP 2 | #define PROCMAP_MEMORY_MAP 3 | 4 | #include "procmap/MemorySegment.hpp" 5 | 6 | namespace procmap { 7 | 8 | class MemoryMap : private std::vector { 9 | public: 10 | MemoryMap(); 11 | void print(); 12 | 13 | //allowed methods from std::vector 14 | using vector::operator[]; 15 | using vector::begin; 16 | using vector::end; 17 | 18 | }; 19 | 20 | } // namespace procmap 21 | 22 | #endif // PROCMAP_MEMORY_MAP 23 | -------------------------------------------------------------------------------- /include/procmap/MemorySegment.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROCMAP_MEMORY_SEGMENT 2 | #define PROCMAP_MEMORY_SEGMENT 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace procmap { 9 | 10 | class MemorySegment { 11 | void* _startAddress; 12 | void* _endAddress; 13 | unsigned long _offset; 14 | unsigned int _deviceMajor; 15 | unsigned int _deviceMinor; 16 | ino_t _inode; 17 | unsigned char _permissions; 18 | std::string _name; 19 | 20 | public: 21 | MemorySegment(char *unparsed_line); 22 | // getters 23 | void* startAddress(); 24 | void* endAddress(); 25 | std::string name(); 26 | size_t length(); 27 | dev_t device(); 28 | // getters for the permissions bitmask 29 | bool isReadable(); 30 | bool isWriteable(); 31 | bool isExecutable(); 32 | bool isShared(); 33 | bool isPrivate(); 34 | // other functions 35 | bool isBindable(); 36 | bool isAnonymous(); 37 | bool isHeap(); 38 | bool isStack(); 39 | void print(); 40 | }; 41 | 42 | } // namespace procmap 43 | 44 | #endif // PROCMAP_MEMORY_SEGMENT 45 | -------------------------------------------------------------------------------- /src/procmap/Logger.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 João Neto 3 | * A simple yet fancy logger 4 | **/ 5 | #include "procmap/Logger.hpp" 6 | 7 | procmap::Logger Logger; 8 | procmap::Logger *L = &Logger; 9 | -------------------------------------------------------------------------------- /src/procmap/MemoryMap.cpp: -------------------------------------------------------------------------------- 1 | #include "procmap/MemoryMap.hpp" 2 | #include "procmap/Logger.hpp" 3 | 4 | namespace procmap { 5 | 6 | MemoryMap::MemoryMap() { 7 | char *line = NULL; 8 | size_t line_size = 0; 9 | 10 | // open maps file 11 | FILE *maps = fopen("/proc/self/maps", "r"); 12 | DIEIF(maps == nullptr, "error opening maps file"); 13 | 14 | // parse the maps file 15 | while (getline(&line, &line_size, maps) > 0) { 16 | emplace_back(line); 17 | } 18 | 19 | // cleanup 20 | free(line); 21 | DIEIF(!feof(maps) || ferror(maps), "error parsing maps file"); 22 | DIEIF(fclose(maps), "error closing maps file"); 23 | } 24 | 25 | void MemoryMap::print() { 26 | for (auto &segment : *this) { 27 | segment.print(); 28 | } 29 | } 30 | 31 | } // namespace procmap 32 | -------------------------------------------------------------------------------- /src/procmap/MemorySegment.cpp: -------------------------------------------------------------------------------- 1 | #include "procmap/MemorySegment.hpp" 2 | #include "procmap/Logger.hpp" 3 | 4 | namespace procmap { 5 | 6 | MemorySegment::MemorySegment(char *line) { 7 | int name_start = 0, name_end = 0; 8 | unsigned long addr_start, addr_end; 9 | char perms_str[8]; 10 | //printf("line: %s", line); 11 | 12 | // parse string 13 | DIEIF(sscanf(line, "%lx-%lx %7s %lx %u:%u %lu %n%*[^\n]%n", 14 | &addr_start, &addr_end, perms_str, &_offset, 15 | &_deviceMajor, &_deviceMinor, &_inode, 16 | &name_start, &name_end) < 7, 17 | "FAILED TO PARSE"); 18 | 19 | // convert addresses 20 | _startAddress = reinterpret_cast(addr_start); 21 | _endAddress = reinterpret_cast(addr_end); 22 | 23 | // copy permissions 24 | _permissions = 0U; 25 | if (strchr(perms_str, 'r')) 26 | _permissions |= 1U << 0; 27 | if (strchr(perms_str, 'w')) 28 | _permissions |= 1U << 1; 29 | if (strchr(perms_str, 'x')) 30 | _permissions |= 1U << 2; 31 | if (strchr(perms_str, 's')) 32 | _permissions |= 1U << 3; 33 | if (strchr(perms_str, 'p')) 34 | _permissions |= 1U << 4; 35 | 36 | // copy name 37 | if (name_end > name_start) { 38 | line[name_end] = '\0'; 39 | _name.assign(&line[name_start]); 40 | } 41 | } 42 | 43 | void* MemorySegment::startAddress() { 44 | return _startAddress; 45 | } 46 | 47 | void* MemorySegment::endAddress() { 48 | return _endAddress; 49 | } 50 | 51 | std::string MemorySegment::name() { 52 | return _name; 53 | } 54 | 55 | size_t MemorySegment::length() { 56 | return ((char*)_endAddress) - ((char*)_startAddress); 57 | } 58 | 59 | dev_t MemorySegment::device() { 60 | return makedev(_deviceMajor, _deviceMinor); 61 | } 62 | 63 | bool MemorySegment::isReadable() { 64 | return (_permissions & 1U) != 0; 65 | } 66 | 67 | bool MemorySegment::isWriteable() { 68 | return (_permissions & 2U) != 0; 69 | } 70 | 71 | bool MemorySegment::isExecutable() { 72 | return (_permissions & 4U) != 0; 73 | } 74 | 75 | bool MemorySegment::isShared() { 76 | return (_permissions & 8U) != 0; 77 | } 78 | 79 | bool MemorySegment::isPrivate() { 80 | return (_permissions & 16U) != 0; 81 | } 82 | 83 | void MemorySegment::print() { 84 | char info[1024]; 85 | snprintf(info, sizeof(info), 86 | "[%18p-%18p] (%5lu pages) [off=%7lu] [dev=%u:%u] [inode=%8lu] %c%c%c%c '%s'", 87 | _startAddress, _endAddress, 88 | length() / sysconf(_SC_PAGESIZE), 89 | _offset, 90 | _deviceMajor, _deviceMinor, 91 | _inode, 92 | (isPrivate() ? 'P' : 'S'), 93 | (isExecutable() ? 'X' : '-'), 94 | (isWriteable() ? 'W' : '-'), 95 | (isReadable() ? 'R' : '-'), 96 | _name.c_str()); 97 | L->printHorizontalRule(info, (isWriteable() ? 2 : 1)); 98 | } 99 | 100 | bool MemorySegment::isBindable() { 101 | return name() != "[vsyscall]"; 102 | } 103 | 104 | bool MemorySegment::isHeap() { 105 | return name() == "[heap]"; 106 | } 107 | 108 | bool MemorySegment::isStack() { 109 | return name() == "[stack]"; 110 | } 111 | 112 | bool MemorySegment::isAnonymous() { 113 | return name().length() == 0; 114 | } 115 | 116 | } // namespace procmap 117 | -------------------------------------------------------------------------------- /test/simple.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main() { 8 | procmap::MemoryMap m; 9 | m.print(); 10 | return 0; 11 | } 12 | --------------------------------------------------------------------------------