├── .github ├── FUNDING.yml └── workflows │ └── build_cmake.yml ├── test ├── catch_main.cpp ├── tests.cpp ├── constexpr_tests.cpp └── CMakeLists.txt ├── .clang-tidy ├── src ├── CMakeLists.txt └── main.cpp ├── cmake ├── Doxygen.cmake ├── PreventInSourceBuilds.cmake ├── Conan.cmake ├── Cache.cmake ├── StandardProjectSettings.cmake ├── StaticAnalyzers.cmake ├── Sanitizers.cmake └── CompilerWarnings.cmake ├── .cmake-format.yaml ├── appveyor.yml ├── LICENSE ├── .travis.yml ├── CMakeLists.txt ├── .clang-format └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: lefticus 4 | patreon: lefticus 5 | -------------------------------------------------------------------------------- /test/catch_main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN // This tells the catch header to generate a main 2 | 3 | #include 4 | 5 | 6 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm-*' 3 | WarningsAsErrors: '*' 4 | HeaderFilterRegex: '' 5 | FormatStyle: none 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generic test that uses conan libs 2 | add_executable(i2c_hacking main.cpp) 3 | target_link_libraries( 4 | i2c_hacking 5 | PRIVATE i2c 6 | project_options 7 | project_warnings 8 | CONAN_PKG::docopt.cpp 9 | CONAN_PKG::fmt 10 | CONAN_PKG::spdlog) 11 | -------------------------------------------------------------------------------- /cmake/Doxygen.cmake: -------------------------------------------------------------------------------- 1 | function(enable_doxygen) 2 | option(ENABLE_DOXYGEN "Enable doxygen doc builds of source" OFF) 3 | if(ENABLE_DOXYGEN) 4 | set(DOXYGEN_CALLER_GRAPH YES) 5 | set(DOXYGEN_CALL_GRAPH YES) 6 | set(DOXYGEN_EXTRACT_ALL YES) 7 | find_package(Doxygen REQUIRED dot) 8 | doxygen_add_docs(doxygen-docs ${PROJECT_SOURCE_DIR}) 9 | 10 | endif() 11 | endfunction() 12 | -------------------------------------------------------------------------------- /test/tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned int Factorial(unsigned int number) 4 | { 5 | return number <= 1 ? number : Factorial(number - 1) * number; 6 | } 7 | 8 | TEST_CASE("Factorials are computed", "[factorial]") 9 | { 10 | REQUIRE(Factorial(1) == 1); 11 | REQUIRE(Factorial(2) == 2); 12 | REQUIRE(Factorial(3) == 6); 13 | REQUIRE(Factorial(10) == 3628800); 14 | } 15 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | additional_commands: 2 | foo: 3 | flags: 4 | - BAR 5 | - BAZ 6 | kwargs: 7 | DEPENDS: '*' 8 | HEADERS: '*' 9 | SOURCES: '*' 10 | bullet_char: '*' 11 | dangle_parens: false 12 | enum_char: . 13 | line_ending: unix 14 | line_width: 120 15 | max_pargs_hwrap: 3 16 | separate_ctrl_name_with_space: false 17 | separate_fn_name_with_space: false 18 | tab_size: 2 19 | 20 | -------------------------------------------------------------------------------- /test/constexpr_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | constexpr unsigned int Factorial(unsigned int number) 4 | { 5 | return number <= 1 ? number : Factorial(number - 1) * number; 6 | } 7 | 8 | TEST_CASE("Factorials are computed with constexpr", "[factorial]") 9 | { 10 | STATIC_REQUIRE(Factorial(1) == 1); 11 | STATIC_REQUIRE(Factorial(2) == 2); 12 | STATIC_REQUIRE(Factorial(3) == 6); 13 | STATIC_REQUIRE(Factorial(10) == 3628800); 14 | } 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: 2 | - Visual Studio 2019 3 | clone_folder: c:\projects\source 4 | 5 | environment: 6 | PYTHON: "C:\\Python38-x64\\python.exe" 7 | 8 | build_script: 9 | - cmd: >- 10 | mkdir build 11 | 12 | cd build 13 | 14 | set PATH=%PATH%;C:\Users\appveyor\AppData\Roaming\Python\Python38\Scripts 15 | 16 | "%PYTHON%" -m pip install --user conan 17 | 18 | cmake c:\projects\source -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE:STRING=Release 19 | 20 | cmake --build . --config "Release" 21 | 22 | test_script: 23 | - cmd: ctest -C Debug 24 | 25 | -------------------------------------------------------------------------------- /cmake/PreventInSourceBuilds.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This function will prevent in-source builds 3 | function(AssureOutOfSourceBuilds) 4 | # make sure the user doesn't play dirty with symlinks 5 | get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) 6 | get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) 7 | 8 | # disallow in-source builds 9 | if("${srcdir}" STREQUAL "${bindir}") 10 | message("######################################################") 11 | message("Warning: in-source builds are disabled") 12 | message("Please create a separate build directory and run cmake from there") 13 | message("######################################################") 14 | message(FATAL_ERROR "Quitting configuration") 15 | endif() 16 | endfunction() 17 | 18 | assureoutofsourcebuilds() 19 | -------------------------------------------------------------------------------- /cmake/Conan.cmake: -------------------------------------------------------------------------------- 1 | macro(run_conan) 2 | # Download automatically, you can also just copy the conan.cmake file 3 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 4 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 5 | file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake") 6 | endif() 7 | 8 | include(${CMAKE_BINARY_DIR}/conan.cmake) 9 | 10 | conan_add_remote( 11 | NAME 12 | bincrafters 13 | URL 14 | https://api.bintray.com/conan/bincrafters/public-conan) 15 | 16 | conan_cmake_run( 17 | REQUIRES 18 | ${CONAN_EXTRA_REQUIRES} 19 | catch2/2.11.0 20 | docopt.cpp/0.6.2 21 | fmt/6.2.0 22 | spdlog/1.5.0 23 | OPTIONS 24 | ${CONAN_EXTRA_OPTIONS} 25 | BASIC_SETUP 26 | CMAKE_TARGETS # individual targets to link to 27 | BUILD 28 | missing) 29 | endmacro() 30 | -------------------------------------------------------------------------------- /cmake/Cache.cmake: -------------------------------------------------------------------------------- 1 | option(ENABLE_CACHE "Enable cache if available" ON) 2 | if(NOT ENABLE_CACHE) 3 | return() 4 | endif() 5 | 6 | set(CACHE_OPTION 7 | "ccache" 8 | CACHE STRING "Compiler cache to be used") 9 | set(CACHE_OPTION_VALUES "ccache" "sccache") 10 | set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) 11 | list( 12 | FIND 13 | CACHE_OPTION_VALUES 14 | ${CACHE_OPTION} 15 | CACHE_OPTION_INDEX) 16 | 17 | if(${CACHE_OPTION_INDEX} EQUAL -1) 18 | message( 19 | STATUS 20 | "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}") 21 | endif() 22 | 23 | find_program(CACHE_BINARY ${CACHE_OPTION}) 24 | if(CACHE_BINARY) 25 | message(STATUS "${CACHE_OPTION} found and enabled") 26 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_BINARY}) 27 | else() 28 | message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") 29 | endif() 30 | -------------------------------------------------------------------------------- /cmake/StandardProjectSettings.cmake: -------------------------------------------------------------------------------- 1 | # Set a default build type if none was specified 2 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 3 | message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") 4 | set(CMAKE_BUILD_TYPE 5 | RelWithDebInfo 6 | CACHE STRING "Choose the type of build." FORCE) 7 | # Set the possible values of build type for cmake-gui, ccmake 8 | set_property( 9 | CACHE CMAKE_BUILD_TYPE 10 | PROPERTY STRINGS 11 | "Debug" 12 | "Release" 13 | "MinSizeRel" 14 | "RelWithDebInfo") 15 | endif() 16 | 17 | # Generate compile_commands.json to make it easier to work with clang based tools 18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | 20 | option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) 21 | 22 | if(ENABLE_IPO) 23 | include(CheckIPOSupported) 24 | check_ipo_supported( 25 | RESULT 26 | result 27 | OUTPUT 28 | output) 29 | if(result) 30 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 31 | else() 32 | message(SEND_ERROR "IPO is not supported: ${output}") 33 | endif() 34 | endif() 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /cmake/StaticAnalyzers.cmake: -------------------------------------------------------------------------------- 1 | option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) 2 | option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) 3 | option(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF) 4 | 5 | if(ENABLE_CPPCHECK) 6 | find_program(CPPCHECK cppcheck) 7 | if(CPPCHECK) 8 | set(CMAKE_CXX_CPPCHECK 9 | ${CPPCHECK} 10 | --suppress=missingInclude 11 | --enable=all 12 | --inline-suppr 13 | --inconclusive 14 | -i 15 | ${CMAKE_SOURCE_DIR}/imgui/lib) 16 | else() 17 | message(SEND_ERROR "cppcheck requested but executable not found") 18 | endif() 19 | endif() 20 | 21 | if(ENABLE_CLANG_TIDY) 22 | find_program(CLANGTIDY clang-tidy) 23 | if(CLANGTIDY) 24 | set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option) 25 | else() 26 | message(SEND_ERROR "clang-tidy requested but executable not found") 27 | endif() 28 | endif() 29 | 30 | if(ENABLE_INCLUDE_WHAT_YOU_USE) 31 | find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) 32 | if(INCLUDE_WHAT_YOU_USE) 33 | set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) 34 | else() 35 | message(SEND_ERROR "include-what-you-use requested but executable not found") 36 | endif() 37 | endif() 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | install: 4 | # Pip cannot install Conan without these upgrades 5 | - python3 -m pip install --upgrade pip setuptools 6 | # Install Conan and CMake >= 3.15 7 | - python3 -m pip install conan cmake 8 | 9 | # Fail if we can't run Conan. 10 | - conan --version 11 | 12 | jobs: 13 | include: 14 | - os: osx 15 | compiler: gcc 16 | osx_image: xcode11.2 # includes gcc-9 by default 17 | env: 18 | - GCC_VER="9" 19 | - MATRIX_EVAL="CC=gcc-${GCC_VER} && CXX=g++-${GCC_VER}" 20 | - os: osx 21 | compiler: clang 22 | osx_image: xcode11.2 23 | env: 24 | - MATRIX_EVAL="" 25 | - os: linux 26 | dist: bionic 27 | compiler: gcc 28 | env: 29 | - GCC_VER="9" 30 | - MATRIX_EVAL="CC=gcc-${GCC_VER} && CXX=g++-${GCC_VER}" 31 | 32 | addons: 33 | apt: 34 | sources: 35 | - ubuntu-toolchain-r-test 36 | packages: 37 | # I couldn't get ${GCC_VER} in here successfully 38 | - gcc-9 39 | - g++-9 40 | - doxygen 41 | - python3-pip 42 | after_script: 43 | - bash <(curl -s https://codecov.io/bash) -x /usr/bin/gcov-${GCC_VER} 44 | - os: linux 45 | dist: bionic 46 | compiler: clang 47 | env: 48 | - MATRIX_EVAL="CC=clang && CXX=clang++" 49 | addons: { apt: { packages: ['doxygen', 'python3-pip'] } } 50 | 51 | 52 | before_script: 53 | - eval "${MATRIX_EVAL}" 54 | 55 | script: 56 | - mkdir build 57 | - cd build 58 | - cmake -D ENABLE_COVERAGE:BOOL=TRUE ../ 59 | - cmake --build . -- -j2 60 | - ctest -j2 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # automatically enable catch2 to generate ctest targets 2 | if(CONAN_CATCH2_ROOT_DEBUG) 3 | include(${CONAN_CATCH2_ROOT_DEBUG}/lib/cmake/Catch2/Catch.cmake) 4 | else() 5 | include(${CONAN_CATCH2_ROOT}/lib/cmake/Catch2/Catch.cmake) 6 | endif() 7 | 8 | add_library(catch_main STATIC catch_main.cpp) 9 | target_link_libraries(catch_main PUBLIC CONAN_PKG::catch2) 10 | target_link_libraries(catch_main PRIVATE project_options) 11 | 12 | add_executable(tests tests.cpp) 13 | target_link_libraries(tests PRIVATE project_warnings project_options catch_main) 14 | 15 | # automatically discover tests that are defined in catch based test files you can modify the unittests. TEST_PREFIX to 16 | # whatever you want, or use different for different binaries 17 | catch_discover_tests( 18 | tests 19 | TEST_PREFIX 20 | "unittests." 21 | EXTRA_ARGS 22 | -s 23 | --reporter=xml 24 | --out=tests.xml) 25 | 26 | # Add a file containing a set of constexpr tests 27 | add_executable(constexpr_tests constexpr_tests.cpp) 28 | target_link_libraries(constexpr_tests PRIVATE project_options project_warnings catch_main) 29 | 30 | catch_discover_tests( 31 | constexpr_tests 32 | TEST_PREFIX 33 | "constexpr." 34 | EXTRA_ARGS 35 | -s 36 | --reporter=xml 37 | --out=constexpr.xml) 38 | 39 | # Disable the constexpr portion of the test, and build again this allows us to have an executable that we can debug when 40 | # things go wrong with the constexpr testing 41 | add_executable(relaxed_constexpr_tests constexpr_tests.cpp) 42 | target_link_libraries(relaxed_constexpr_tests PRIVATE project_options project_warnings catch_main) 43 | target_compile_definitions(relaxed_constexpr_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) 44 | 45 | catch_discover_tests( 46 | relaxed_constexpr_tests 47 | TEST_PREFIX 48 | "relaxed_constexpr." 49 | EXTRA_ARGS 50 | -s 51 | --reporter=xml 52 | --out=relaxed_constexpr.xml) 53 | -------------------------------------------------------------------------------- /.github/workflows/build_cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: [push] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: RelWithDebInfo 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Create Build Environment 21 | # Some projects don't allow in-source building, so create a separate build directory 22 | # We'll use this as our working directory for all subsequent commands 23 | run: cmake -E make_directory ${{runner.workspace}}/build 24 | 25 | - name: Install conan 26 | shell: bash 27 | run: | 28 | python3 -m pip install --upgrade pip setuptools 29 | python3 -m pip install conan 30 | source ~/.profile 31 | 32 | - name: Configure CMake 33 | # Use a bash shell so we can use the same syntax for environment variable 34 | # access regardless of the host operating system 35 | shell: bash 36 | working-directory: ${{runner.workspace}}/build 37 | # Note the current convention is to use the -S and -B options here to specify source 38 | # and build directories, but this is only available with CMake 3.13 and higher. 39 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 40 | # 41 | # We need to source the profile file to make sure conan is in PATH 42 | run: | 43 | source ~/.profile 44 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 45 | 46 | - name: Build 47 | working-directory: ${{runner.workspace}}/build 48 | shell: bash 49 | # Execute the build. You can specify a specific target with "--target " 50 | run: cmake --build . --config $BUILD_TYPE 51 | 52 | - name: Test 53 | working-directory: ${{runner.workspace}}/build 54 | shell: bash 55 | # Execute tests defined by the CMake configuration. 56 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 57 | run: ctest -C $BUILD_TYPE -------------------------------------------------------------------------------- /cmake/Sanitizers.cmake: -------------------------------------------------------------------------------- 1 | function(enable_sanitizers project_name) 2 | 3 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 4 | option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) 5 | 6 | if(ENABLE_COVERAGE) 7 | target_compile_options(${project_name} INTERFACE --coverage -O0 -g) 8 | target_link_libraries(${project_name} INTERFACE --coverage) 9 | endif() 10 | 11 | set(SANITIZERS "") 12 | 13 | option(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) 14 | if(ENABLE_SANITIZER_ADDRESS) 15 | list(APPEND SANITIZERS "address") 16 | endif() 17 | 18 | option(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE) 19 | if(ENABLE_SANITIZER_LEAK) 20 | list(APPEND SANITIZERS "leak") 21 | endif() 22 | 23 | option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) 24 | if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) 25 | list(APPEND SANITIZERS "undefined") 26 | endif() 27 | 28 | option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) 29 | if(ENABLE_SANITIZER_THREAD) 30 | if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) 31 | message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") 32 | else() 33 | list(APPEND SANITIZERS "thread") 34 | endif() 35 | endif() 36 | 37 | option(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) 38 | if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 39 | if("address" IN_LIST SANITIZERS 40 | OR "thread" IN_LIST SANITIZERS 41 | OR "leak" IN_LIST SANITIZERS) 42 | message(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") 43 | else() 44 | list(APPEND SANITIZERS "memory") 45 | endif() 46 | endif() 47 | 48 | list( 49 | JOIN 50 | SANITIZERS 51 | "," 52 | LIST_OF_SANITIZERS) 53 | 54 | endif() 55 | 56 | if(LIST_OF_SANITIZERS) 57 | if(NOT 58 | "${LIST_OF_SANITIZERS}" 59 | STREQUAL 60 | "") 61 | target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 62 | target_link_libraries(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 63 | endif() 64 | endif() 65 | 66 | endfunction() 67 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # Set the project name to your project name, my project isn't very descriptive 4 | project(myproject CXX) 5 | include(cmake/StandardProjectSettings.cmake) 6 | include(cmake/PreventInSourceBuilds.cmake) 7 | 8 | # Link this 'library' to set the c++ standard / compile-time options requested 9 | add_library(project_options INTERFACE) 10 | target_compile_features(project_options INTERFACE cxx_std_20) 11 | 12 | if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 13 | option(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" OFF) 14 | if(ENABLE_BUILD_WITH_TIME_TRACE) 15 | add_compile_definitions(project_options INTERFACE -ftime-trace) 16 | endif() 17 | endif() 18 | 19 | # Link this 'library' to use the warnings specified in CompilerWarnings.cmake 20 | add_library(project_warnings INTERFACE) 21 | 22 | # enable cache system 23 | include(cmake/Cache.cmake) 24 | 25 | # standard compiler warnings 26 | include(cmake/CompilerWarnings.cmake) 27 | set_project_warnings(project_warnings) 28 | 29 | # sanitizer options if supported by compiler 30 | include(cmake/Sanitizers.cmake) 31 | enable_sanitizers(project_options) 32 | 33 | # enable doxygen 34 | include(cmake/Doxygen.cmake) 35 | enable_doxygen() 36 | 37 | # allow for static analysis options 38 | include(cmake/StaticAnalyzers.cmake) 39 | 40 | option(BUILD_SHARED_LIBS "Enable compilation of shared libraries" OFF) 41 | option(ENABLE_TESTING "Enable Test Builds" ON) 42 | option(ENABLE_FUZZING "Enable Fuzzing Builds" OFF) 43 | 44 | # Very basic PCH example 45 | option(ENABLE_PCH "Enable Precompiled Headers" OFF) 46 | if(ENABLE_PCH) 47 | # This sets a global PCH parameter, each project will build its own PCH, which is a good idea if any #define's change 48 | # 49 | # consider breaking this out per project as necessary 50 | target_precompile_headers( 51 | project_options 52 | INTERFACE 53 | 54 | 55 | 56 | ) 57 | endif() 58 | 59 | # Set up some extra Conan dependencies based on our needs before loading Conan 60 | set(CONAN_EXTRA_REQUIRES "") 61 | set(CONAN_EXTRA_OPTIONS "") 62 | 63 | include(cmake/Conan.cmake) 64 | run_conan() 65 | 66 | if(ENABLE_TESTING) 67 | enable_testing() 68 | message("Building Tests. Be sure to check out test/constexpr_tests for constexpr testing") 69 | add_subdirectory(test) 70 | endif() 71 | 72 | add_subdirectory(src) 73 | 74 | 75 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -2 2 | AlignAfterOpenBracket: DontAlign 3 | AlignConsecutiveAssignments: false 4 | AlignConsecutiveDeclarations: false 5 | AlignEscapedNewlines: Left 6 | AlignOperands: true 7 | AlignTrailingComments: false 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AllowShortBlocksOnASingleLine: true 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: All 12 | AllowShortIfStatementsOnASingleLine: true 13 | AllowShortLoopsOnASingleLine: true 14 | AlwaysBreakAfterDefinitionReturnType: None 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: false 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BraceWrapping: 21 | AfterClass: true 22 | AfterControlStatement: false 23 | AfterEnum: false 24 | AfterFunction: true 25 | AfterNamespace: false 26 | AfterObjCDeclaration: false 27 | AfterStruct: true 28 | AfterUnion: false 29 | BeforeCatch: false 30 | BeforeElse: false 31 | IndentBraces: false 32 | SplitEmptyFunction: false 33 | SplitEmptyNamespace: true 34 | SplitEmptyRecord: true 35 | BreakAfterJavaFieldAnnotations: true 36 | BreakBeforeBinaryOperators: NonAssignment 37 | BreakBeforeBraces: Custom 38 | BreakBeforeInheritanceComma: true 39 | BreakBeforeTernaryOperators: true 40 | BreakConstructorInitializers: BeforeColon 41 | BreakConstructorInitializersBeforeComma: false 42 | BreakStringLiterals: true 43 | ColumnLimit: 0 44 | CommentPragmas: '^ IWYU pragma:' 45 | CompactNamespaces: false 46 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 47 | ConstructorInitializerIndentWidth: 2 48 | ContinuationIndentWidth: 2 49 | Cpp11BracedListStyle: false 50 | DerivePointerAlignment: false 51 | DisableFormat: false 52 | ExperimentalAutoDetectBinPacking: true 53 | FixNamespaceComments: true 54 | ForEachMacros: 55 | - foreach 56 | - Q_FOREACH 57 | - BOOST_FOREACH 58 | IncludeCategories: 59 | - Priority: 2 60 | Regex: ^"(llvm|llvm-c|clang|clang-c)/ 61 | - Priority: 3 62 | Regex: ^(<|"(gtest|gmock|isl|json)/) 63 | - Priority: 1 64 | Regex: .* 65 | IncludeIsMainRegex: (Test)?$ 66 | IndentCaseLabels: false 67 | IndentWidth: 2 68 | IndentWrappedFunctionNames: true 69 | JavaScriptQuotes: Leave 70 | JavaScriptWrapImports: true 71 | KeepEmptyLinesAtTheStartOfBlocks: true 72 | Language: Cpp 73 | MacroBlockBegin: '' 74 | MacroBlockEnd: '' 75 | MaxEmptyLinesToKeep: 2 76 | NamespaceIndentation: Inner 77 | ObjCBlockIndentWidth: 7 78 | ObjCSpaceAfterProperty: true 79 | ObjCSpaceBeforeProtocolList: false 80 | PointerAlignment: Right 81 | ReflowComments: true 82 | SortIncludes: false 83 | SortUsingDeclarations: false 84 | SpaceAfterCStyleCast: false 85 | SpaceAfterTemplateKeyword: false 86 | SpaceBeforeAssignmentOperators: true 87 | SpaceBeforeParens: ControlStatements 88 | SpaceInEmptyParentheses: false 89 | SpacesBeforeTrailingComments: 0 90 | SpacesInAngles: false 91 | SpacesInCStyleCastParentheses: false 92 | SpacesInContainerLiterals: true 93 | SpacesInParentheses: false 94 | SpacesInSquareBrackets: false 95 | Standard: Cpp11 96 | TabWidth: 8 97 | UseTab: Never 98 | 99 | -------------------------------------------------------------------------------- /cmake/CompilerWarnings.cmake: -------------------------------------------------------------------------------- 1 | # from here: 2 | # 3 | # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md 4 | 5 | function(set_project_warnings project_name) 6 | option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) 7 | 8 | set(MSVC_WARNINGS 9 | /W4 # Baseline reasonable warnings 10 | /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data 11 | /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 12 | /w14263 # 'function': member function does not override any base class virtual member function 13 | /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not 14 | # be destructed correctly 15 | /w14287 # 'operator': unsigned/negative constant mismatch 16 | /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside 17 | # the for-loop scope 18 | /w14296 # 'operator': expression is always 'boolean_value' 19 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 20 | /w14545 # expression before comma evaluates to a function which is missing an argument list 21 | /w14546 # function call before comma missing argument list 22 | /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect 23 | /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? 24 | /w14555 # expression has no effect; expected expression with side- effect 25 | /w14619 # pragma warning: there is no warning number 'number' 26 | /w14640 # Enable warning on thread un-safe static member initialization 27 | /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. 28 | /w14905 # wide string literal cast to 'LPSTR' 29 | /w14906 # string literal cast to 'LPWSTR' 30 | /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 31 | /permissive- # standards conformance mode for MSVC compiler. 32 | ) 33 | 34 | set(CLANG_WARNINGS 35 | -Wall 36 | -Wextra # reasonable and standard 37 | -Wshadow # warn the user if a variable declaration shadows one from a parent context 38 | -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps 39 | # catch hard to track down memory errors 40 | -Wold-style-cast # warn for c-style casts 41 | -Wcast-align # warn for potential performance problem casts 42 | -Wunused # warn on anything being unused 43 | -Woverloaded-virtual # warn if you overload (not override) a virtual function 44 | -Wpedantic # warn if non-standard C++ is used 45 | -Wconversion # warn on type conversions that may lose data 46 | -Wsign-conversion # warn on sign conversions 47 | -Wnull-dereference # warn if a null dereference is detected 48 | -Wdouble-promotion # warn if float is implicit promoted to double 49 | -Wformat=2 # warn on security issues around functions that format output (ie printf) 50 | ) 51 | 52 | if(WARNINGS_AS_ERRORS) 53 | set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) 54 | set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) 55 | endif() 56 | 57 | set(GCC_WARNINGS 58 | ${CLANG_WARNINGS} 59 | -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist 60 | -Wduplicated-cond # warn if if / else chain has duplicated conditions 61 | -Wduplicated-branches # warn if if / else branches have duplicated code 62 | -Wlogical-op # warn about logical operations being used where bitwise were probably wanted 63 | -Wuseless-cast # warn if you perform a cast to the same type 64 | ) 65 | 66 | if(MSVC) 67 | set(PROJECT_WARNINGS ${MSVC_WARNINGS}) 68 | elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 69 | set(PROJECT_WARNINGS ${CLANG_WARNINGS}) 70 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 71 | set(PROJECT_WARNINGS ${GCC_WARNINGS}) 72 | else() 73 | message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 74 | endif() 75 | 76 | target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) 77 | 78 | endfunction() 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp_starter_project 2 | 3 | [![codecov](https://codecov.io/gh/lefticus/cpp_starter_project/branch/master/graph/badge.svg)](https://codecov.io/gh/lefticus/cpp_starter_project) 4 | 5 | [![Build Status](https://travis-ci.org/lefticus/cpp_starter_project.svg?branch=master)](https://travis-ci.org/lefticus/cpp_starter_project) 6 | 7 | [![Build status](https://ci.appveyor.com/api/projects/status/ro4lbfoa7n0sy74c/branch/master?svg=true)](https://ci.appveyor.com/project/lefticus/cpp-starter-project/branch/master) 8 | 9 | ![CMake](https://github.com/lefticus/cpp_starter_project/workflows/CMake/badge.svg) 10 | 11 | 12 | ## Getting Started 13 | 14 | ### Use the Github template 15 | First, click the green `Use this template` button near the top of this page. 16 | This will take you to Github's ['Generate Repository'](https://github.com/lefticus/cpp_starter_project/generate) page. 17 | Fill in a repository name and short description, and click 'Create repository from template'. 18 | This will allow you to create a new repository in your Github account, 19 | prepopulated with the contents of this project. 20 | Now you can clone the project locally and get to work! 21 | 22 | $ git clone https://github.com//.git 23 | 24 | ### Remove frameworks you're not going to use 25 | If you know you're not going to use one or more of the optional gui/graphics 26 | frameworks (fltk, gtkmm, imgui, etc.), you can remove them with `git rm`: 27 | 28 | $ git rm -r src/ 29 | 30 | ## Dependencies 31 | 32 | Note about install commands: 33 | - for Windows, we use [choco](https://chocolatey.org/install). 34 | - for MacOS, we use [brew](https://brew.sh/). 35 | - In case of an error in cmake, make sure that the dependencies are on the PATH. 36 | 37 | ### Necessary Dependencies 38 | 1. A C++ compiler that supports C++17. 39 | See [cppreference.com](https://en.cppreference.com/w/cpp/compiler_support) 40 | to see which features are supported by each compiler. 41 | The following compilers should work: 42 | 43 | * [gcc 7+](https://gcc.gnu.org/) 44 |
45 | Install command 46 | 47 | - Debian/Ubuntu: 48 | 49 | sudo apt install build-essential 50 | 51 | - Windows: 52 | 53 | choco install mingw -y 54 | 55 | - MacOS: 56 | 57 | brew install gcc 58 |
59 | 60 | * [clang 6+](https://clang.llvm.org/) 61 |
62 | Install command 63 | 64 | - Debian/Ubuntu: 65 | 66 | bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" 67 | 68 | - Windows: 69 | 70 | Visual Studio 2019 ships with LLVM (see the Visual Studio section). However, to install LLVM separately: 71 | 72 | choco install llvm -y 73 | 74 | llvm-utils for using external LLVM with Visual Studio generator: 75 | 76 | git clone https://github.com/zufuliu/llvm-utils.git 77 | cd llvm-utils/VS2017 78 | .\install.bat 79 | 80 | - MacOS: 81 | 82 | brew install llvm 83 |
84 | 85 | * [Visual Studio 2019 or higher](https://visualstudio.microsoft.com/) 86 |
87 | Install command + Environment setup 88 | 89 | On Windows, you need to install Visual Studio 2019 because of the SDK and libraries that ship with it. 90 | 91 | Visual Studio IDE - 2019 Community (installs Clang too): 92 | 93 | choco install -y visualstudio2019community --package-parameters "add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --includeOptional --passive --locale en-US" 94 | 95 | Put MSVC compiler, Clang compiler, and vcvarsall.bat on the path: 96 | 97 | choco install vswhere -y 98 | refreshenv 99 | 100 | $clpath = vswhere -latest -prerelease -find **/Hostx64/x64/* # for x64 101 | [Environment]::SetEnvironmentVariable("Path", $env:Path + ";$clpath", "User") 102 | 103 | $clangpath = vswhere -latest -prerelease -find **/Llvm/bin/* 104 | [Environment]::SetEnvironmentVariable("Path", $env:Path + ";$clangpath", "User") 105 | 106 | $vcvarsallpath = vswhere -latest -prerelease -find **/Auxiliary/Build/* 107 | [Environment]::SetEnvironmentVariable("Path", $env:Path + ";$vcvarsallpath", "User") 108 | refreshenv 109 | 110 |
111 | 112 | 113 | 2. [Conan](https://conan.io/) 114 |
115 | Install Command 116 | 117 | - Via pip - https://docs.conan.io/en/latest/installation.html#install-with-pip-recommended 118 | 119 | pip install --user conan 120 | 121 | - Windows: 122 | 123 | choco install conan -y 124 | 125 | - MacOS: 126 | 127 | brew install conan 128 | 129 |
130 | 131 | 3. [CMake 3.15+](https://cmake.org/) 132 |
133 | Install Command 134 | 135 | - Debian/Ubuntu: 136 | 137 | sudo apt-get install cmake 138 | 139 | - Windows: 140 | 141 | choco install cmake -y 142 | 143 | - MacOS: 144 | 145 | brew install cmake 146 | 147 |
148 | 149 | ### Optional Dependencies 150 | #### C++ Tools 151 | * [Doxygen](http://doxygen.nl/) 152 |
153 | Install Command 154 | 155 | - Debian/Ubuntu: 156 | 157 | sudo apt-get install doxygen 158 | sudo apt-get install graphviz 159 | 160 | - Windows: 161 | 162 | choco install doxygen.install -y 163 | choco install graphviz -y 164 | 165 | - MacOS: 166 | 167 | brew install doxygen 168 | brew install graphviz 169 | 170 |
171 | 172 | 173 | * [ccache](https://ccache.dev/) 174 |
175 | Install Command 176 | 177 | - Debian/Ubuntu: 178 | 179 | sudo apt-get install ccache 180 | 181 | - Windows: 182 | 183 | choco install ccache -y 184 | 185 | - MacOS: 186 | 187 | brew install ccache 188 | 189 |
190 | 191 | 192 | * [Cppcheck](http://cppcheck.sourceforge.net/) 193 |
194 | Install Command 195 | 196 | - Debian/Ubuntu: 197 | 198 | sudo apt-get install cppcheck 199 | 200 | - Windows: 201 | 202 | choco install cppcheck -y 203 | 204 | - MacOS: 205 | 206 | brew install cppcheck 207 | 208 |
209 | 210 | 211 | * [include-what-you-use](https://include-what-you-use.org/) 212 |
213 | Install Command 214 | 215 | Follow instructions here: 216 | https://github.com/include-what-you-use/include-what-you-use#how-to-install 217 |
218 | 219 | #### GUI libraries 220 | This project can be made to work with several optional GUI frameworks. 221 | 222 | If desired, you should install the following optional dependencies as 223 | directed by their documentation, linked here: 224 | 225 | - [FLTK](https://www.fltk.org/doc-1.4/index.html) 226 | - [GTKMM](https://www.gtkmm.org/en/documentation.html) 227 | - [QT](https://doc.qt.io/) 228 | 229 | The following dependencies can be downloaded automatically by CMake and Conan. 230 | All you need to do to install them is to turn on a CMake flag during 231 | configuration. 232 | If you run into difficulty using them, please refer to their documentation, 233 | linked here: 234 | 235 | - [NANA](http://nanapro.org/en-us/documentation/) 236 | - [SDL](http://wiki.libsdl.org/FrontPage) 237 | - [IMGUI](https://github.com/ocornut/imgui/tree/master/docs): 238 | This framework depends on SFML, and if you are using Linux, you may need 239 | to install several of SFML's dependencies using your package manager. See 240 | [the SFML build tutorial](https://www.sfml-dev.org/tutorials/2.5/compile-with-cmake.php) 241 | for specifics. 242 | 243 | ## Build Instructions 244 | 245 | ### Build directory 246 | Make a build directory: 247 | ``` 248 | mkdir build 249 | ``` 250 | ### Specify the compiler using environment variables 251 | 252 | By default (if you don't set environment variables `CC` and `CXX`), the system default compiler will be used. 253 | 254 | Conan and CMake use the environment variables CC and CXX to decide which compiler to use. So to avoid the conflict issues only specify the compilers using these variables. 255 | 256 | CMake will detect which compiler was used to build each of the Conan targets. If you build all of your Conan targets with one compiler, and then build your CMake targets with a different compiler, the project may fail to build. 257 | 258 |
259 | Commands for setting the compilers 260 | 261 | - Debian/Ubuntu/MacOS: 262 | 263 | Set your desired compiler (`clang`, `gcc`, etc): 264 | 265 | - Temporarily (only for the current shell) 266 | 267 | Run one of the followings in the terminal: 268 | 269 | - clang 270 | 271 | CC=clang CXX=clang++ 272 | 273 | - gcc 274 | 275 | CC=gcc CXX=g++ 276 | 277 | - Permanent: 278 | 279 | Open `~/.bashrc` using your text editor: 280 | 281 | gedit ~/.bashrc 282 | 283 | Add `CC` and `CXX` to point to the compilers: 284 | 285 | export CC=clang 286 | export CXX=clang++ 287 | 288 | Save and close the file. 289 | 290 | - Windows: 291 | 292 | - Permanent: 293 | 294 | Run one of the followings in PowerShell: 295 | 296 | - Visual Studio generator and compiler (cl) 297 | 298 | [Environment]::SetEnvironmentVariable("CC", "cl.exe", "User") 299 | [Environment]::SetEnvironmentVariable("CXX", "cl.exe", "User") 300 | refreshenv 301 | 302 | Set the architecture using [vsvarsall](https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#vcvarsall-syntax): 303 | 304 | vsvarsall.bat x64 305 | 306 | - clang 307 | 308 | [Environment]::SetEnvironmentVariable("CC", "clang.exe", "User") 309 | [Environment]::SetEnvironmentVariable("CXX", "clang++.exe", "User") 310 | refreshenv 311 | 312 | - gcc 313 | 314 | [Environment]::SetEnvironmentVariable("CC", "gcc.exe", "User") 315 | [Environment]::SetEnvironmentVariable("CXX", "g++.exe", "User") 316 | refreshenv 317 | 318 | 319 | - Temporarily (only for the current shell): 320 | 321 | $Env:CC="clang.exe" 322 | $Env:CXX="clang++.exe" 323 | 324 |
325 | 326 | ### Configure your build 327 | 328 | To configure the project and write makefiles, you could use `cmake` with a bunch of command line options. 329 | The easier option is to run cmake interactively: 330 | 331 | #### **Configure via cmake-gui**: 332 | 333 | 1) Open cmake-gui from the project directory: 334 | ``` 335 | cmake-gui . 336 | ``` 337 | 2) Set the build directory: 338 | 339 | ![build_dir](https://user-images.githubusercontent.com/16418197/82524586-fa48e380-9af4-11ea-8514-4e18a063d8eb.jpg) 340 | 341 | 3) Configure the generator: 342 | 343 | In cmake-gui, from the upper menu select `Tools/Configure`. 344 | 345 | **Warning**: if you have set `CC` and `CXX` always choose the `use default native compilers` option. This picks `CC` and `CXX`. Don't change the compiler at this stage! 346 | 347 |
348 | Windows - MinGW Makefiles 349 | 350 | Choose MinGW Makefiles as the generator: 351 | 352 | mingw 353 | 354 |
355 | 356 |
357 | Windows - Visual Studio generator and compiler 358 | 359 | You should have already set `C` and `CXX` to `cl.exe`. 360 | 361 | Choose "Visual Studio 16 2019" as the generator: 362 | 363 | default_vs 364 | 365 |
366 | 367 |
368 | 369 | Windows - Visual Studio generator and Clang Compiler 370 | 371 | You should have already set `C` and `CXX` to `clang.exe` and `clang++.exe`. 372 | 373 | Choose "Visual Studio 16 2019" as the generator. To tell Visual studio to use `clang-cl.exe`: 374 | - If you use the LLVM that is shipped with Visual Studio: write `ClangCl` under "optional toolset to use". 375 | 376 | visual_studio 377 | 378 | - If you use an external LLVM: write [`LLVM_v142`](https://github.com/zufuliu/llvm-utils#llvm-for-visual-studio-2017-and-2019) 379 | under "optional toolset to use". 380 | 381 | visual_studio 382 | 383 |
384 |
385 | 386 | 4) Choose the Cmake options and then generate: 387 | 388 | ![generate](https://user-images.githubusercontent.com/16418197/82781591-c97feb80-9e1f-11ea-86c8-f2748b96f516.png) 389 | 390 | #### **Configure via ccmake**: 391 | with the Cmake Curses Dialog Command Line tool: 392 | 393 | ccmake -S . -B ./build 394 | 395 | Once `ccmake` has finished setting up, press 'c' to configure the project, 396 | press 'g' to generate, and 'q' to quit. 397 | 398 | ### Build 399 | Once you have selected all the options you would like to use, you can build the 400 | project (all targets): 401 | 402 | cmake --build ./build 403 | 404 | For Visual Studio, give the build configuration (Release, RelWithDeb, Debug, etc) like the following: 405 | 406 | cmake --build ./build -- /p:configuration=Release 407 | 408 | ## Troubleshooting 409 | 410 | ### Update Conan 411 | Many problems that users have can be resolved by updating Conan, so if you are 412 | having any trouble with this project, you should start by doing that. 413 | 414 | To update conan: 415 | 416 | $ pip install --user --upgrade conan 417 | 418 | You may need to use `pip3` instead of `pip` in this command, depending on your 419 | platform. 420 | 421 | ### Clear Conan cache 422 | If you continue to have trouble with your Conan dependencies, you can try 423 | clearing your Conan cache: 424 | 425 | $ conan remove -f '*' 426 | 427 | The next time you run `cmake` or `cmake --build`, your Conan dependencies will 428 | be rebuilt. If you aren't using your system's default compiler, don't forget to 429 | set the CC, CXX, CMAKE_C_COMPILER, and CMAKE_CXX_COMPILER variables, as 430 | described in the 'Build using an alternate compiler' section above. 431 | 432 | ### Identifying misconfiguration of Conan dependencies 433 | 434 | If you have a dependency 'A' that requires a specific version of another 435 | dependency 'B', and your project is trying to use the wrong version of 436 | dependency 'B', Conan will produce warnings about this configuration error 437 | when you run CMake. These warnings can easily get lost between a couple 438 | hundred or thousand lines of output, depending on the size of your project. 439 | 440 | If your project has a Conan configuration error, you can use `conan info` to 441 | find it. `conan info` displays information about the dependency graph of your 442 | project, with colorized output in some terminals. 443 | 444 | $ cd build 445 | $ conan info . 446 | 447 | In my terminal, the first couple lines of `conan info`'s output show all of the 448 | project's configuration warnings in a bright yellow font. 449 | 450 | For example, the package `spdlog/1.5.0` depends on the package `fmt/6.1.2`. 451 | If you were to modify the file `cmake/Conan.cmake` so that it requires an 452 | earlier version of `fmt`, such as `fmt/6.0.0`, and then run: 453 | 454 | $ conan remove -f '*' # clear Conan cache 455 | $ rm -rf build # clear previous CMake build 456 | $ mkdir build && cd build 457 | $ cmake .. # rebuild Conan dependencies 458 | $ conan info . 459 | 460 | ...the first line of output would be a warning that `spdlog` needs a more recent 461 | version of `fmt`. 462 | 463 | ## Testing 464 | See [Catch2 tutorial](https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md) 465 | 466 | ## Fuzz testing 467 | 468 | See [libFuzzer Tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) 469 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Docs and notes 2 | // 3 | // RTC: https://datasheets.maximintegrated.com/en/ds/DS1307.pdf 4 | // OLED Driver: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf 5 | // 9-axis Motion Sensor: https://invensense.tdk.com/wp-content/uploads/2015/02/PS-MPU-9250A-01-v1.1.pdf 6 | // Motion Sensor Register Map: https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-9250-Register-Map.pdf 7 | // EEPROM: https://www.st.com/resource/en/datasheet/m24c32-x.pdf 8 | // 9 | // 10 | // OLED Python Examples: https://github.com/adafruit/Adafruit_Python_SSD1306/blob/master/Adafruit_SSD1306/SSD1306.py 11 | // 12 | // RaspberryPi I2C Info: https://raspberry-projects.com/pi/programming-in-c/i2c/using-the-i2c-interface 13 | // 14 | // Kernel.org I2C Documentation: https://www.kernel.org/doc/Documentation/i2c/dev-interface 15 | // 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | extern "C" { 27 | #include 28 | } 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | static constexpr auto USAGE = 36 | R"(Naval Fate. 37 | 38 | Usage: 39 | naval_fate ship new ... 40 | naval_fate ship move [--speed=] 41 | naval_fate ship shoot 42 | naval_fate mine (set|remove) [--moored | --drifting] 43 | naval_fate (-h | --help) 44 | naval_fate --version 45 | Options: 46 | -h --help Show this screen. 47 | --version Show version. 48 | --speed= Speed in knots [default: 10]. 49 | --moored Moored (anchored) mine. 50 | --drifting Drifting mine. 51 | )"; 52 | 53 | 54 | struct Linux_i2c 55 | { 56 | using handle_type = int; 57 | 58 | void close(int handle) 59 | { 60 | ::close(handle); 61 | } 62 | 63 | int open_i2c_device(const int adapter_nr, const int deviceid) 64 | { 65 | const auto filehandle = open(fmt::format("/dev/i2c-{}", adapter_nr).c_str(), O_RDWR); 66 | if (filehandle < 0) { 67 | /* ERROR HANDLING; you can check errno to see what went wrong */ 68 | spdlog::error("Unable to open i2c-{} device for R/W", adapter_nr); 69 | exit(1); 70 | } 71 | 72 | if (ioctl(filehandle, I2C_SLAVE, deviceid) < 0) { 73 | /* ERROR HANDLING; you can check errno to see what went wrong */ 74 | spdlog::error("Unable to set I2C_SLAVE addr to {}", deviceid); 75 | exit(1); 76 | } 77 | 78 | return filehandle; 79 | } 80 | 81 | template 82 | void smbus_read_block(int device, std::uint8_t address, Iter buffer_begin, Iter buffer_end) 83 | { 84 | spdlog::warn("Read block data is not implemented, falling back to 1 byte at a time"); 85 | read_block_1_byte_at_a_time(device, address, buffer_begin, buffer_end); 86 | } 87 | 88 | 89 | auto read_byte(int device, std::uint8_t reg) -> std::uint8_t 90 | { 91 | const auto res = i2c_smbus_read_byte_data(device, reg); 92 | if (res < 0) { 93 | spdlog::error("Error reading byte of data from {} (err result: {})", reg, res); 94 | } 95 | return static_cast(res); 96 | } 97 | 98 | auto read_byte(int device) -> std::uint8_t 99 | { 100 | const auto res = i2c_smbus_read_byte(device); 101 | if (res < 0) { 102 | spdlog::error("Error reading byte of data from (err result: {})", res); 103 | } 104 | return static_cast(res); 105 | } 106 | 107 | template 108 | void i2c_read_block(int device, std::uint8_t address, Iter buffer_begin, Iter buffer_end) 109 | { 110 | const auto bytes_to_read = std::distance(buffer_begin, buffer_end); 111 | if (bytes_to_read > I2C_SMBUS_BLOCK_MAX) { 112 | spdlog::error("Error, cannot even try to read more than 255 bytes"); 113 | } 114 | auto result = i2c_smbus_read_i2c_block_data(device, address, static_cast(std::min(bytes_to_read, I2C_SMBUS_BLOCK_MAX)), &(*buffer_begin)); 115 | if (result < 0) { 116 | spdlog::error("Error reading block of data from {} (err result: {})", address, result); 117 | spdlog::error("Error description: '{}'", strerror(errno)); 118 | } 119 | } 120 | 121 | 122 | template 123 | void read_block_1_byte_at_a_time(int device, std::uint8_t starting_address, Iter buffer_begin, Iter buffer_end) 124 | { 125 | spdlog::trace("Reading {} bytes from {:02x} starting at {:02x}", std::distance(buffer_begin, buffer_end), device, starting_address); 126 | 127 | for (auto address = starting_address; buffer_begin != buffer_end; ++address, ++buffer_begin) { 128 | *buffer_begin = read_byte(device, address); 129 | } 130 | } 131 | 132 | void write_byte(int device, std::uint8_t reg, std::uint8_t value) 133 | { 134 | const auto res = i2c_smbus_write_byte_data(device, reg, value); 135 | if (res < 0) { 136 | spdlog::error("Error writing byte of data reg {} (err result: {})", reg, res); 137 | } 138 | } 139 | 140 | template 141 | void write_block_1_byte_at_a_time(int device, std::uint8_t starting_address, Iter buffer_begin, Iter buffer_end) 142 | { 143 | spdlog::trace("Writing {} bytes from {:02x} starting at {:02x}", std::distance(buffer_begin, buffer_end), device, starting_address); 144 | 145 | for (auto address = starting_address; buffer_begin != buffer_end; ++address, ++buffer_begin) { 146 | write_byte(device, address, *buffer_begin); 147 | } 148 | } 149 | 150 | template 151 | void i2c_write_block(int device, std::uint8_t starting_address, Iter buffer_begin, Iter buffer_end) 152 | { 153 | const auto bytes_to_write = std::distance(buffer_begin, buffer_end); 154 | 155 | 156 | if (bytes_to_write > I2C_SMBUS_BLOCK_MAX) { 157 | spdlog::error("Error, cannot even try to write more than {} bytes, attempted {}", I2C_SMBUS_BLOCK_MAX, bytes_to_write); 158 | } 159 | 160 | 161 | spdlog::trace("Writing {} bytes from {:02x} starting at {:02x}", bytes_to_write, device, starting_address); 162 | 163 | i2c_smbus_write_i2c_block_data(device, starting_address, static_cast(std::min(bytes_to_write, I2C_SMBUS_BLOCK_MAX)), &(*buffer_begin)); 164 | } 165 | 166 | template 167 | void smbus_write_block(int device, std::uint8_t starting_address, Iter buffer_begin, Iter buffer_end) 168 | { 169 | spdlog::warn("smbus write block not implemented, falling back to 1 byte at a time"); 170 | write_block_1_byte_at_a_time(device, starting_address, buffer_begin, buffer_end); 171 | } 172 | 173 | 174 | void send_command(int device, std::uint8_t value) 175 | { 176 | const auto res = i2c_smbus_write_byte_data(device, 0, value); 177 | if (res < 0) { 178 | spdlog::error("Error writing command data '{}' (err result: {})", value, res); 179 | } 180 | } 181 | }; 182 | 183 | 184 | //constexpr auto SSD1306_I2C_ADDRESS = 0x3C;// 011110+SA0+RW - 0x3C or 0x3D 185 | constexpr auto SSD1306_SETCONTRAST = 0x81; 186 | constexpr auto SSD1306_DISPLAYALLON_RESUME = 0xA4; 187 | constexpr auto SSD1306_DISPLAYALLON = 0xA5; 188 | constexpr auto SSD1306_NORMALDISPLAY = 0xA6; 189 | constexpr auto SSD1306_INVERTDISPLAY = 0xA7; 190 | constexpr auto SSD1306_DISPLAYOFF = 0xAE; 191 | constexpr auto SSD1306_DISPLAYON = 0xAF; 192 | constexpr auto SSD1306_SETDISPLAYOFFSET = 0xD3; 193 | constexpr auto SSD1306_SETCOMPINS = 0xDA; 194 | constexpr auto SSD1306_SETVCOMDETECT = 0xDB; 195 | constexpr auto SSD1306_SETDISPLAYCLOCKDIV = 0xD5; 196 | constexpr auto SSD1306_SETPRECHARGE = 0xD9; 197 | constexpr auto SSD1306_SETMULTIPLEX = 0xA8; 198 | constexpr auto SSD1306_SETLOWCOLUMN = 0x00; 199 | constexpr auto SSD1306_SETHIGHCOLUMN = 0x10; 200 | constexpr auto SSD1306_SETSTARTLINE = 0x40; 201 | constexpr auto SSD1306_MEMORYMODE = 0x20; 202 | constexpr auto SSD1306_COLUMNADDR = 0x21; 203 | constexpr auto SSD1306_PAGEADDR = 0x22; 204 | constexpr auto SSD1306_COMSCANINC = 0xC0; 205 | constexpr auto SSD1306_COMSCANDEC = 0xC8; 206 | constexpr auto SSD1306_SEGREMAP = 0xA0; 207 | constexpr auto SSD1306_CHARGEPUMP = 0x8D; 208 | constexpr auto SSD1306_EXTERNALVCC = 0x1; 209 | constexpr auto SSD1306_SWITCHCAPVCC = 0x2; 210 | // Scrolling constants; 211 | constexpr auto SSD1306_ACTIVATE_SCROLL = 0x2F; 212 | constexpr auto SSD1306_DEACTIVATE_SCROLL = 0x2E; 213 | constexpr auto SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3; 214 | constexpr auto SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26; 215 | constexpr auto SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27; 216 | constexpr auto SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29; 217 | constexpr auto SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A; 218 | 219 | /* 220 | */ 221 | 222 | enum struct Block_Mode { 223 | one_byte_at_a_time, 224 | smbus_block_protocol, 225 | i2c_multi_byte 226 | }; 227 | 228 | template 229 | struct i2c_device 230 | { 231 | Driver driver; 232 | 233 | typename Driver::handle_type handle{}; 234 | 235 | i2c_device(const int adapter_nr, const int deviceid) 236 | : handle(driver.open_i2c_device(adapter_nr, deviceid)) 237 | { 238 | } 239 | 240 | ~i2c_device() 241 | { 242 | driver.close(handle); 243 | } 244 | 245 | i2c_device &operator=(const i2c_device &) = delete; 246 | i2c_device &operator=(i2c_device &&) = delete; 247 | i2c_device(const i2c_device &) = delete; 248 | i2c_device(i2c_device &&) = delete; 249 | 250 | [[nodiscard]] auto read_byte(std::uint8_t reg) const -> std::uint8_t 251 | { 252 | return driver.read_byte(handle, reg); 253 | } 254 | 255 | [[nodiscard]] auto read_block(std::uint8_t reg) const 256 | { 257 | return driver.read_block(handle, reg); 258 | } 259 | 260 | void read_block(const std::uint8_t starting_address, auto begin_iterator, auto end_iterator) 261 | { 262 | switch (read_mode) { 263 | case Block_Mode::one_byte_at_a_time: 264 | driver.read_block_1_byte_at_a_time(handle, starting_address, begin_iterator, end_iterator); 265 | return; 266 | case Block_Mode::smbus_block_protocol: 267 | driver.smbus_read_block(handle, starting_address, begin_iterator, end_iterator); 268 | return; 269 | case Block_Mode::i2c_multi_byte: 270 | driver.i2c_read_block(handle, starting_address, begin_iterator, end_iterator); 271 | return; 272 | } 273 | } 274 | 275 | void write_block(const std::uint8_t starting_address, auto begin_iterator, auto end_iterator) 276 | { 277 | switch (read_mode) { 278 | case Block_Mode::one_byte_at_a_time: 279 | driver.write_block_1_byte_at_a_time(handle, starting_address, begin_iterator, end_iterator); 280 | return; 281 | case Block_Mode::smbus_block_protocol: 282 | driver.smbus_write_block(handle, starting_address, begin_iterator, end_iterator); 283 | return; 284 | case Block_Mode::i2c_multi_byte: 285 | driver.i2c_write_block(handle, starting_address, begin_iterator, end_iterator); 286 | return; 287 | } 288 | } 289 | 290 | 291 | void write_byte(std::uint8_t reg, std::uint8_t value) 292 | { 293 | driver.write_byte(handle, reg, value); 294 | } 295 | 296 | void send_command(std::uint8_t value) 297 | { 298 | driver.send_command(handle, value); 299 | } 300 | }; 301 | 302 | template 303 | struct ssd1306 : i2c_device 304 | { 305 | enum struct Address { 306 | SA0_LOW = 0x3c, 307 | SA0_HIGH = 0x3d 308 | }; 309 | 310 | int width; 311 | int height; 312 | 313 | int pages = height / 8; 314 | int used_buffer = (width * height) / 8; 315 | 316 | std::array buffer; 317 | 318 | void clear() 319 | { 320 | buffer.fill(0); 321 | } 322 | 323 | 324 | 325 | void set_pixel(int x, int y, bool value) 326 | { 327 | // 0,0 = 0 328 | // 0,1 = 0 329 | // 0,8 = 128 330 | // 1,9 = 129 331 | // 332 | const auto byte = x + ((y/8) * width); 333 | if (value) { 334 | buffer[byte] |= static_cast(1 << (y%8)); 335 | } else { 336 | buffer[byte] &= static_cast(~(1 << (y%8))); 337 | } 338 | } 339 | 340 | void push_buffer() 341 | { 342 | 343 | // """Write display buffer to physical display.""" 344 | this->send_command(SSD1306_COLUMNADDR); 345 | this->send_command(0); // Column start address. (0 = reset) 346 | this->send_command(width-1); // Column end address. 347 | this->send_command(SSD1306_PAGEADDR); 348 | this->send_command(0); // Page start address. (0 = reset) 349 | this->send_command(pages-1); // Page end address. 350 | 351 | auto buffer_begin = buffer.begin(); 352 | const auto buffer_end = std::next(buffer.begin(), used_buffer); 353 | 354 | while( buffer_begin != buffer_end ) 355 | { 356 | const auto bytes_to_write = std::min(std::distance(buffer_begin, buffer_end), 16); 357 | this->write_block(0x40, buffer_begin, std::next(buffer_begin, bytes_to_write)); 358 | std::advance(buffer_begin, bytes_to_write); 359 | } 360 | } 361 | 362 | ssd1306(const int adapter, const Address address, const int width_, const int height_) 363 | : i2c_device{ adapter, static_cast(address) }, 364 | width{width_}, height{height_} 365 | { 366 | // 128x32 pixel specific initialization. 367 | this->send_command(SSD1306_DISPLAYOFF); // 0xAE 368 | this->send_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 369 | this->send_command(0x80); // the suggested ratio 0x80 370 | this->send_command(SSD1306_SETMULTIPLEX); // 0xA8 371 | this->send_command(0x1F); 372 | this->send_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 373 | this->send_command(0x0); // no offset 374 | this->send_command(SSD1306_SETSTARTLINE | 0x0); // line //0 375 | this->send_command(SSD1306_CHARGEPUMP); // 0x8D 376 | //if self._vccstate == SSD1306_EXTERNALVCC: 377 | // this->send_command(0x10); 378 | //else: 379 | this->send_command(0x14); 380 | this->send_command(SSD1306_MEMORYMODE); // 0x20 381 | this->send_command(0x00); // 0x0 act like ks0108 382 | this->send_command(SSD1306_SEGREMAP | 0x1); 383 | this->send_command(SSD1306_COMSCANDEC); 384 | this->send_command(SSD1306_SETCOMPINS); // 0xDA 385 | this->send_command(0x02); 386 | this->send_command(SSD1306_SETCONTRAST); // 0x81 387 | this->send_command(0x8F); 388 | this->send_command(SSD1306_SETPRECHARGE); // 0xd9 389 | //if self._vccstate == SSD1306_EXTERNALVCC: 390 | // this->send_command(0x22); 391 | //else: 392 | this->send_command(0xF1); 393 | this->send_command(SSD1306_SETVCOMDETECT); // 0xDB 394 | this->send_command(0x40); 395 | this->send_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 396 | this->send_command(SSD1306_NORMALDISPLAY); // 0xA6 397 | this->send_command(SSD1306_DISPLAYON); // 0xAE 398 | } 399 | 400 | void write_glyph(const int x, const int y, auto glyph) 401 | { 402 | // this is terribly wrong if we break assumptions 403 | std::size_t cur_col = 0; 404 | const auto row_offset = y % 8; 405 | for (const auto &byte : glyph) { 406 | const auto start_byte = (y / 8) * width + x + cur_col; 407 | if (row_offset != 0) { 408 | buffer[start_byte] |= (byte << row_offset); 409 | buffer[start_byte + width] |= (byte >> (7-row_offset)); 410 | } else { 411 | buffer[start_byte] = byte; 412 | } 413 | ++cur_col; 414 | } 415 | } 416 | 417 | void display_all_on() 418 | { 419 | this->send_command(SSD1306_DISPLAYALLON); // 0xAE 420 | } 421 | 422 | }; 423 | 424 | template 425 | struct ds1307_rtc : i2c_device 426 | { 427 | static constexpr int ds1307_rtc_address = 0x68; 428 | 429 | ds1307_rtc(const int adapter) : i2c_device{ adapter, ds1307_rtc_address } 430 | { 431 | } 432 | 433 | std::array buffer{}; 434 | 435 | void sync_buffer() 436 | { 437 | this->read_block(0, begin(buffer), end(buffer)); 438 | } 439 | 440 | [[nodiscard]] constexpr std::uint8_t get_buffered_byte(std::size_t offset) const noexcept 441 | { 442 | return buffer[offset]; 443 | } 444 | 445 | 446 | [[nodiscard]] constexpr static auto time_field(const std::uint8_t input) noexcept 447 | { 448 | return (0b1111 & input) + ((input >> 4) & 0b1111) * 10; 449 | }; 450 | 451 | [[nodiscard]] int seconds() const 452 | { 453 | return time_field(static_cast(0b0111'1111 & get_buffered_byte(0x00))); 454 | } 455 | 456 | [[nodiscard]] int minutes() const 457 | { 458 | return time_field(get_buffered_byte(0x01)); 459 | } 460 | 461 | [[nodiscard]] int hours() const 462 | { 463 | return time_field(static_cast(0b111111 & get_buffered_byte(0x02))); 464 | } 465 | 466 | [[nodiscard]] int day_of_week() const 467 | { 468 | return get_buffered_byte(0x03); 469 | } 470 | 471 | [[nodiscard]] int day_of_month() const 472 | { 473 | return time_field(get_buffered_byte(0x04)); 474 | } 475 | 476 | [[nodiscard]] int month() const 477 | { 478 | return time_field(get_buffered_byte(0x05)); 479 | } 480 | 481 | [[nodiscard]] int year() const 482 | { 483 | return time_field(get_buffered_byte(0x06)) + 2000; 484 | } 485 | 486 | 487 | std::chrono::system_clock::time_point current_time() 488 | { 489 | sync_buffer(); 490 | 491 | std::tm time; 492 | time.tm_sec = seconds(); 493 | time.tm_min = minutes(); 494 | time.tm_hour = hours(); 495 | time.tm_mday = day_of_month(); 496 | time.tm_mon = month() - 1; 497 | time.tm_year = year() - 1970; 498 | time.tm_wday = day_of_week() - 1; 499 | 500 | return std::chrono::system_clock::from_time_t(std::mktime(&time)); 501 | } 502 | 503 | 504 | template 505 | void write_object(const DataType &data) 506 | { 507 | static_assert(sizeof(DataType) + Offset <= 56); 508 | static_assert(std::is_trivial_v); 509 | 510 | const auto *ptr = reinterpret_cast(&data); 511 | constexpr auto start = 0x08; 512 | this->write_block(start, ptr, std::next(ptr, sizeof(DataType))); 513 | } 514 | 515 | 516 | template 517 | DataType read_object() 518 | { 519 | static_assert(sizeof(DataType) + Offset <= 56); 520 | static_assert(std::is_trivial_v); 521 | 522 | DataType data; 523 | auto *ptr = reinterpret_cast(&data); 524 | 525 | constexpr auto start = 0x08; 526 | 527 | this->read_block(start, ptr, std::next(ptr, sizeof(DataType))); 528 | 529 | return data; 530 | } 531 | }; 532 | 533 | 534 | constexpr auto build_glyph(const std::array &data) { 535 | std::array result{}; 536 | 537 | // transpose the data for happy ds1307 controller 538 | 539 | const auto get_bit = [](const auto x, const auto y, const auto &input){ 540 | return ((input[y] >> (7-x)) & 1) == 1; 541 | }; 542 | 543 | const auto set_bit = [](const auto x, const auto y, auto &input, bool value) { 544 | if (value) { 545 | input[y] |= (1 << (7-x)); 546 | } else { 547 | input[y] &= ~(1 << (7-x)); 548 | } 549 | }; 550 | 551 | 552 | for (std::size_t row = 0; row < 8; ++row) { 553 | for (std::size_t col = 0; col < 8; ++col) { 554 | set_bit(row, col, result, get_bit(col, 7-row, data)); 555 | } 556 | } 557 | 558 | return result; 559 | } 560 | 561 | 562 | int main(/*int argc, const char **argv*/) 563 | { 564 | /* 565 | std::map args = docopt::docopt(USAGE, 566 | { std::next(argv), std::next(argv, argc) }, 567 | true,// show help if requested 568 | "Naval Fate 2.0");// version string 569 | 570 | for (auto const &arg : args) { 571 | std::cout << arg.first << arg.second << std::endl; 572 | } 573 | */ 574 | 575 | //Use the default logger (stdout, multi-threaded, colored) 576 | spdlog::info("Starting i2c Experiments"); 577 | spdlog::set_level(spdlog::level::debug); 578 | ds1307_rtc real_time_clock(1); 579 | 580 | const std::array data{ 'J', 'E', 'l', 'l', 'O' }; 581 | real_time_clock.write_object<0>(data); 582 | 583 | fmt::print("Pre sync: Current Time: {:02}:{:02}:{:02}\n", real_time_clock.hours(), real_time_clock.minutes(), real_time_clock.seconds()); 584 | real_time_clock.sync_buffer(); 585 | fmt::print("Post sync: Current Time: {:02}:{:02}:{:02}\n", real_time_clock.hours(), real_time_clock.minutes(), real_time_clock.seconds()); 586 | 587 | // fmt::print("{}", real_time_clock.current_time()); 588 | 589 | 590 | const auto read_data = real_time_clock.read_object<0, std::array>(); 591 | 592 | for (const auto c : read_data) { 593 | fmt::print("Read: '{}'\n", c); 594 | } 595 | 596 | ssd1306 display(1, ssd1306::Address::SA0_LOW, 128, 32); 597 | display.clear(); 598 | // display.buffer[0] = 0x0F; 599 | // display.buffer[128] = 0xF0; 600 | display.set_pixel(0,0, true); 601 | display.set_pixel(127,0, true); 602 | display.set_pixel(127,31, true); 603 | display.set_pixel(0,31, true); 604 | display.push_buffer(); 605 | 606 | 607 | static constexpr auto H_glyph = build_glyph({ 608 | 0b11000110, 609 | 0b11000110, 610 | 0b11000110, 611 | 0b11111110, 612 | 0b11000110, 613 | 0b11000110, 614 | 0b11000110, 615 | 0b00000000}); 616 | 617 | static constexpr auto e_glyph = build_glyph({ 618 | 0b00000000, 619 | 0b00000000, 620 | 0b01111100, 621 | 0b11000110, 622 | 0b11111110, 623 | 0b11000000, 624 | 0b01111100, 625 | 0b00000000}); 626 | 627 | static constexpr auto l_glyph = build_glyph({ 628 | 0b01100000, 629 | 0b01100000, 630 | 0b01100000, 631 | 0b01100000, 632 | 0b01100000, 633 | 0b01100000, 634 | 0b01100000, 635 | 0b00000000}); 636 | 637 | 638 | static constexpr auto o_glyph = build_glyph({ 639 | 0b00000000, 640 | 0b00000000, 641 | 0b01111100, 642 | 0b11000110, 643 | 0b11000110, 644 | 0b11000110, 645 | 0b01111100, 646 | 0b00000000}); 647 | 648 | 649 | static constexpr std::array expected_glyph( 650 | { 651 | 0b01111111, 652 | 0b01111111, 653 | 0b00001000, 654 | 0b00001000, 655 | 0b00001000, 656 | 0b01111111, 657 | 0b01111111, 658 | 0b00000000 659 | }); 660 | 661 | 662 | for (int x = 0; x < 95; ++x) { 663 | for (int y = 0; y < 30; ++y) { 664 | display.clear(); 665 | display.write_glyph(x+0,y,H_glyph); 666 | display.write_glyph(x+8,y,e_glyph); 667 | display.write_glyph(x+16,y,l_glyph); 668 | display.write_glyph(x+21,y,l_glyph); 669 | display.write_glyph(x+26,y,o_glyph); 670 | display.push_buffer(); 671 | } 672 | } 673 | assert(H_glyph == expected_glyph); 674 | 675 | 676 | // display.display_all_on(); 677 | 678 | } 679 | --------------------------------------------------------------------------------