+ xoshiro256ss (copied from kalman project)

This commit is contained in:
Roland Conybeare 2023-09-23 13:07:45 -04:00
commit fadbcd7b54
12 changed files with 1130 additions and 0 deletions

73
CMakeLists.txt Normal file
View file

@ -0,0 +1,73 @@
# using indentlog/CMakeLists.txt as model
cmake_minimum_required(VERSION 3.10)
project(randomgen VERSION 0.1)
enable_language(CXX)
include(cmake/cxx.cmake)
include(cmake/code-coverage.cmake)
# ----------------------------------------------------------------
# unit test setup
enable_testing()
# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON)
add_code_coverage()
# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc.
# we're not interested in code coverage for these sources.
# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves;
# rather, want coverage on the code that the unit tests exercise.
#
add_code_coverage_all_targets(EXCLUDE /nix/store/* utest/*)
# ----------------------------------------------------------------
# c++ settings
set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2")
add_definitions(${PROJECT_CXX_FLAGS})
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED True)
# always write compile_commands.json
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
# ----------------------------------------------------------------
# default install
if(NOT USER)
set(USER $ENV{USER})
endif()
if(NOT CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX /home/${USER}/local CACHE STRING "install directory")
endif()
if(NOT CMAKE_INSTALL_RPATH)
set(CMAKE_INSTALL_RPATH /home/${USER}/local/lib CACHE STRING "runpath in installed libraries/executables")
endif()
# ----------------------------------------------------------------
# external dependencies
#
# set CMAKE_INSTALL_PREFIX to analog of /usr
# to use .cmake assistants from /usr/lib/cmake/indentlog
#
find_package(indentlog REQUIRED)
# ----------------------------------------------------------------
add_subdirectory(example)
#add_subdirectory(utest)
# ----------------------------------------------------------------
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
install(TARGETS ex1 DESTINATION bin/randomgen/example)

View file

@ -1 +1,16 @@
# random number generators
# to build + install locally
```
$ cd randomgen
$ mkdir build
$ cd build
$ cmake -DCMAKE_PREFIX_PATH=$(HOME)/local ..
$ make
$ make install
```
# to build + install to /usr/local (deprecated)
same as above, but set `CMAKE_PREFIX_PATH` to `/usr/local`

678
cmake/code-coverage.cmake Normal file
View file

@ -0,0 +1,678 @@
#
# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# USAGE: To enable any code coverage instrumentation/targets, the single CMake
# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or
# on the command line.
#
# From this point, there are two primary methods for adding instrumentation to
# targets:
#
# 1 - A blanket instrumentation by calling `add_code_coverage()`, where
# all targets in that directory and all subdirectories are automatically
# instrumented.
#
# 2 - Per-target instrumentation by calling
# `target_code_coverage(<TARGET_NAME>)`, where the target is given and thus only
# that target is instrumented. This applies to both libraries and executables.
#
# To add coverage targets, such as calling `make ccov` to generate the actual
# coverage information for perusal or consumption, call
# `target_code_coverage(<TARGET_NAME>)` on an *executable* target.
#
# Example 1: All targets instrumented
#
# In this case, the coverage information reported will will be that of the
# `theLib` library target and `theExe` executable.
#
# 1a: Via global command
#
# ~~~
# add_code_coverage() # Adds instrumentation to all targets
#
# add_library(theLib lib.cpp)
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target
# # (instrumentation already added via global anyways)
# # for generating code coverage reports.
# ~~~
#
# 1b: Via target commands
#
# ~~~
# add_library(theLib lib.cpp)
# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets.
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports.
# ~~~
#
# Example 2: Target instrumented, but with regex pattern of files to be excluded
# from report
#
# ~~~
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
#
# Example 3: Target added to the 'ccov' and 'ccov-all' targets
#
# ~~~
# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders.
#
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
# Options
option(
CODE_COVERAGE
"Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
OFF)
# Programs
find_program(LLVM_COV_PATH llvm-cov)
find_program(LLVM_PROFDATA_PATH llvm-profdata)
find_program(LCOV_PATH lcov)
find_program(GENHTML_PATH genhtml)
# Hide behind the 'advanced' mode flag for GUI/ccmake
mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH)
# Variables
set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1)
# Common initialization/checks
if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
set(CODE_COVERAGE_ADDED ON)
# Common Targets
add_custom_target(
ccov-preprocessing
COMMAND ${CMAKE_COMMAND} -E make_directory
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}
DEPENDS ccov-clean)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# Messages
message(STATUS "Building with llvm Code Coverage Tools")
if(NOT LLVM_COV_PATH)
message(FATAL_ERROR "llvm-cov not found! Aborting.")
else()
# Version number checking for 'EXCLUDE' compatibility
execute_process(COMMAND ${LLVM_COV_PATH} --version
OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION
${LLVM_COV_VERSION_CALL_OUTPUT})
if(LLVM_COV_VERSION VERSION_LESS "7.0.0")
message(
WARNING
"target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0"
)
endif()
endif()
# Targets
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-clean
COMMAND ${CMAKE_COMMAND} -E remove -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND ${CMAKE_COMMAND} -E remove -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
else()
add_custom_target(
ccov-clean
COMMAND ${CMAKE_COMMAND} -E rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND ${CMAKE_COMMAND} -E rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
endif()
# Used to get the shared object file list before doing the main all-
# processing
add_custom_target(
ccov-libs
COMMAND ;
COMMENT "libs ready for coverage report.")
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
# Messages
message(STATUS "Building with lcov Code Coverage Tools")
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
if(NOT ${upper_build_type} STREQUAL "DEBUG")
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
else()
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif()
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Targets
add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory
${CMAKE_BINARY_DIR} --zerocounters)
else()
message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
endif()
endif()
# Adds code coverage instrumentation to a library, or instrumentation/targets
# for an executable target.
# ~~~
# EXECUTABLE ADDED TARGETS:
# GCOV/LCOV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
#
# LLVM-COV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report.
# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information.
# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file.
# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line.
# ccov-all-export : Exports the coverage report to a JSON file.
#
# Required:
# TARGET_NAME - Name of the target to generate code coverage for.
# Optional:
# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE.
# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE.
# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects)
# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets.
# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets.
# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory
# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`.
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.**
# OBJECTS <TARGETS> - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output
# ARGS <ARGUMENTS> - For executables ONLY, appends the given arguments to the associated ccov-* executable call
# ~~~
function(target_code_coverage TARGET_NAME)
# Argument parsing
set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN)
set(single_value_keywords COVERAGE_TARGET_NAME)
set(multi_value_keywords EXCLUDE OBJECTS ARGS)
cmake_parse_arguments(
target_code_coverage "${options}" "${single_value_keywords}"
"${multi_value_keywords}" ${ARGN})
# Set the visibility of target functions to PUBLIC, INTERFACE or default to
# PRIVATE.
if(target_code_coverage_PUBLIC)
set(TARGET_VISIBILITY PUBLIC)
set(TARGET_LINK_VISIBILITY PUBLIC)
elseif(target_code_coverage_INTERFACE)
set(TARGET_VISIBILITY INTERFACE)
set(TARGET_LINK_VISIBILITY INTERFACE)
elseif(target_code_coverage_PLAIN)
set(TARGET_VISIBILITY PUBLIC)
set(TARGET_LINK_VISIBILITY)
else()
set(TARGET_VISIBILITY PRIVATE)
set(TARGET_LINK_VISIBILITY PRIVATE)
endif()
if(NOT target_code_coverage_COVERAGE_TARGET_NAME)
# If a specific name was given, use that instead.
set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME})
endif()
if(CODE_COVERAGE)
# Add code coverage instrumentation to the target's linker command
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY}
-fprofile-instr-generate -fcoverage-mapping)
target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY}
-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs
-ftest-coverage)
target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov)
endif()
# Targets
get_target_property(target_type ${TARGET_NAME} TYPE)
# Add shared library to processing for 'all' targets
if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>" >>
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
DEPENDS ccov-preprocessing ${TARGET_NAME})
if(NOT TARGET ccov-libs)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-libs
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
# For executables add targets to run and produce output
if(target_type STREQUAL "EXECUTABLE")
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# If there are shared objects to also work with, generate the string to
# add them here
foreach(SO_TARGET ${target_code_coverage_OBJECTS})
# Check to see if the target is a shared object
if(TARGET ${SO_TARGET})
get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE)
if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
set(SO_OBJECTS ${SO_OBJECTS} -object=$<TARGET_FILE:${SO_TARGET}>)
endif()
endif()
endforeach()
# Run the executable, generating raw profile data Make the run data
# available for further processing. Separated to allow Windows to run
# this target serially.
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${CMAKE_COMMAND} -E env
LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw
$<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${CMAKE_COMMAND} -E echo "-object=$<TARGET_FILE:${TARGET_NAME}>"
${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND
${CMAKE_COMMAND} -E echo
"${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw"
>> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list
JOB_POOL ccov_serial_pool
DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME})
# Merge the generated profile data so llvm-cov can process it
add_custom_target(
ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_PROFDATA_PATH} merge -sparse
${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o
${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
# Ignore regex only works on LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print out details of the coverage information to the command line
add_custom_target(
ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-show-line-counts-or-regions ${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Print out a summary of the coverage information to the command line
add_custom_target(
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} report $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Export coverage information so continuous integration tools (e.g.
# Jenkins) can consume it
add_custom_target(
ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} export $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-format="text" ${EXCLUDE_REGEX} >
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${LLVM_COV_PATH} show $<TARGET_FILE:${TARGET_NAME}> ${SO_OBJECTS}
-instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME})
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
set(COVERAGE_INFO
"${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info"
)
# Run the executable, generating coverage information
add_custom_target(
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
DEPENDS ccov-preprocessing ${TARGET_NAME})
# Generate exclusion string for use
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
if(NOT ${target_code_coverage_EXTERNAL})
set(EXTERNAL_OPTION --no-external)
endif()
# Capture coverage data
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ${TARGET_NAME})
else()
add_custom_target(
ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters
COMMAND $<TARGET_FILE:${TARGET_NAME}> ${target_code_coverage_ARGS}
COMMAND
${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory
${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ${TARGET_NAME})
endif()
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
COMMAND
${GENHTML_PATH} -o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}
${COVERAGE_INFO}
DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
add_custom_command(
TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME}
POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report."
)
# AUTO
if(target_code_coverage_AUTO)
if(NOT TARGET ccov)
add_custom_target(ccov)
endif()
add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME})
if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID
MATCHES "GNU")
if(NOT TARGET ccov-report)
add_custom_target(ccov-report)
endif()
add_dependencies(
ccov-report
ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
# ALL
if(target_code_coverage_ALL)
if(NOT TARGET ccov-all-processing)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-all-processing
ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME})
endif()
endif()
endif()
endfunction()
# Adds code coverage instrumentation to all targets in the current directory and
# any subdirectories. To add coverage instrumentation to only specific targets,
# use `target_code_coverage`.
function(add_code_coverage)
if(CODE_COVERAGE)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
add_compile_options(-fprofile-arcs -ftest-coverage)
link_libraries(gcov)
endif()
endif()
endfunction()
# Adds the 'ccov-all' type targets that calls all targets added via
# `target_code_coverage` with the `ALL` parameter, but merges all the coverage
# data from them into a single large report instead of the numerous smaller
# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for
# use with coverage dashboards (e.g. codecov.io, coveralls).
# ~~~
# Optional:
# EXCLUDE <PATTERNS> - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex!
# ~~~
function(add_code_coverage_all_targets)
# Argument parsing
set(multi_value_keywords EXCLUDE)
cmake_parse_arguments(add_code_coverage_all_targets "" ""
"${multi_value_keywords}" ${ARGN})
if(CODE_COVERAGE)
if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang"
OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang")
# Merge the profile data for all of the run executables
if(WIN32)
add_custom_target(
ccov-all-processing
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe
merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-sparse $$FILELIST)
else()
add_custom_target(
ccov-all-processing
COMMAND
${LLVM_PROFDATA_PATH} merge -o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`)
endif()
# Regex exclude only available for LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print summary of the code coverage information to the command line
if(WIN32)
add_custom_target(
ccov-all-report
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe
report $$FILELIST
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
else()
add_custom_target(
ccov-all-report
COMMAND
${LLVM_COV_PATH} report `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
endif()
# Export coverage information so continuous integration tools (e.g.
# Jenkins) can consume it
add_custom_target(
ccov-all-export
COMMAND
${LLVM_COV_PATH} export `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-format="text" ${EXCLUDE_REGEX} >
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
DEPENDS ccov-all-processing)
# Generate HTML output of all added targets for perusal
if(WIN32)
add_custom_target(
ccov-all
COMMAND
powershell -Command $$FILELIST = Get-Content
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show
$$FILELIST
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
else()
add_custom_target(
ccov-all
COMMAND
${LLVM_COV_PATH} show `cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
-format="html" ${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES
"GNU")
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
# Nothing required for gcov
add_custom_target(ccov-all-processing COMMAND ;)
# Exclusion regex string creation
set(EXCLUDE_REGEX)
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
# Capture coverage data
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
add_custom_target(
ccov-all-capture
COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
--output-file ${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ccov-all-processing)
else()
add_custom_target(
ccov-all-capture
COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO}
COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture
--output-file ${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
DEPENDS ccov-preprocessing ccov-all-processing)
endif()
# Generates HTML output of all targets for perusal
add_custom_target(
ccov-all
COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR}
DEPENDS ccov-all-capture)
endif()
add_custom_command(
TARGET ccov-all
POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report."
)
endif()
endfunction()

40
cmake/cxx.cmake Normal file
View file

@ -0,0 +1,40 @@
# ----------------------------------------------------------------
# use this in subdirs that compile c++ code
#
macro(xo_include_options target)
# ----------------------------------------------------------------
# PROJECT_SOURCE_DIR:
# so we can for example write
# #include "ordinaltree/foo.hpp"
# from anywhere in the project
# PROJECT_BINARY_DIR:
# since generated version file will be in build directory,
# need that build directory to also appear in
# compiler's include path
#
target_include_directories(
${target} PUBLIC
${PROJECT_SOURCE_DIR}/include
${PROJECT_BINARY_DIR}
)
# ----------------------------------------------------------------
# make standard directories for std:: includes explicit
# so that
# (1) they appear in compile_commands.json.
# (2) clangd (run from emacs lsp-mode) can find them
#
if(CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
endmacro()
# ----------------------------------------------------------------
# use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers
#
macro(xo_indentlog_dependency target)
find_package(indentlog REQUIRED)
#add_dependencies(${target} indentlog)
target_link_libraries(${target} PUBLIC indentlog)
#target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include)
endmacro()

2
example/CMakeLists.txt Normal file
View file

@ -0,0 +1,2 @@
add_subdirectory(ex1)
add_subdirectory(ex2)

View file

@ -0,0 +1,2 @@
add_executable(ex1 ex1.cpp)
xo_include_options(ex1)

26
example/ex1/ex1.cpp Normal file
View file

@ -0,0 +1,26 @@
/* @file ex1.cpp */
#include "randomgen/xoshiro256.hpp"
#include <algorithm>
#include <iostream>
//#include <array>
//#include <cstdint>
using namespace xo;
using namespace xo::rng;
int
main(int argc, char ** argv) {
xoshiro256ss rng{123456789};
std::array<std::uint64_t, 20> v;
std::generate(v.begin(), v.end(), rng);
for (std::uint64_t i=0; i<v.size(); ++i)
std::cout << "v[" << i << "]: " << v[i] << std::endl;
return 0;
} /*main*/
/* end ex1.cpp */

View file

@ -0,0 +1,3 @@
add_executable(ex2 ex2.cpp)
xo_include_options(ex2)
xo_indentlog_dependency(ex2)

16
example/ex2/ex2.cpp Normal file
View file

@ -0,0 +1,16 @@
/* @file ex2.cpp */
#include "randomgen/xoshiro256.hpp"
#include "randomgen/random_seed.hpp"
using namespace xo;
using namespace xo::rng;
int
main(int argc, char ** argv) {
Seed<xoshiro256ss> seed;
xoshiro256ss eng(seed);
} /*main*/
/* end ex2.cpp */

View file

@ -0,0 +1,36 @@
/* @file engine_concept.hpp */
#pragma once
#include <concepts>
#include <random>
namespace xo {
namespace rng {
/* an engine generates psuedo-random bits.
* given
* RngEngine eng = ...;
*
* RngEngine::result_type x = eng();
*
* puts random bits into x.
*/
template <class RngEngine>
concept engine_concept = requires(RngEngine engine, typename RngEngine::result_type r) {
/* note: the first 4 requirements characterize UniformRandomBitGenerator */
typename RngEngine::result_type;
{ RngEngine(r) };
{ engine.min() } -> std::same_as<typename RngEngine::result_type>;
{ engine.max() } -> std::same_as<typename RngEngine::result_type>;
/* must return value in closed interval [.min(), .max()] */
{ engine() } -> std::same_as<typename RngEngine::result_type>;
{ engine.seed() };
{ engine.seed(r) };
{ engine == engine };
{ engine != engine };
} && std::copyable<RngEngine> && std::uniform_random_bit_generator<RngEngine>;
} /*namespace rng*/
} /*namespace xo*/
/* end engine_concept.hpp */

View file

@ -0,0 +1,70 @@
/* @file random_seed.hpp */
#include "indentlog/print/array.hpp"
#include <iostream>
#include <cstdint>
#include <stdlib.h>
namespace xo {
namespace rng {
/* generate a 64-bit random seed using /dev/urandom or similar source.
* This is relatively expensive; at least cost of a system call
* + may block if host has rebooted recently
*
* Require:
* - T is null-constructible.
*
* return value will contain a T-instance in which representation
* has been populated with random bits. Expecting T to be something
* like int32_t, or std::array<uint64_t, ..>
*/
template<typename T>
void random_seed(T * p_seed) {
/* NOTE: arc4random_buf() works on darwin/nix;
* probably need to do something else on intel linux
*/
arc4random_buf(p_seed, sizeof(*p_seed));
} /*random_seed*/
template<typename T>
T random_seed() {
T retval;
random_seed(&retval);
return retval;
} /*random_seed*/
/* RAII-style random-number seed
*
* Usage:
*
* Seed<xoshiro256ss> seed;
*
* auto eng = xoshiro256ss(seed);
* or
* auto rng = UnitIntervalGen<xoshiro256ss>::make(seed);
*/
template<typename Engine>
struct Seed {
using seed_type = typename Engine::seed_type;
Seed() { random_seed(&seed_); }
operator seed_type const & () const { return seed_; }
seed_type seed_;
}; /*Seed*/
template<typename T>
inline std::ostream &
operator<<(std::ostream & os,
Seed<T> const & x)
{
os << x.seed_;
return os;
} /*operator<<*/
} /*namespace rng*/
} /*namespace xo*/
/* end random_seed.hpp */

View file

@ -0,0 +1,169 @@
/* @file xoshiro256.hpp */
#pragma once
#include "engine_concept.hpp"
#include <iostream>
#include <array>
#include <limits>
#include <cstdint>
namespace xo {
namespace rng {
/* engine for producing 64-bit random numbers
*
* see https:/en.wikipedia.org/wiki/Xorshift#xoshiro256**
*
* - satisfies c++ UniformRandomBitGenerator
* - satisfies c++
*
* Note: zero seed --> constant output sequence {0, 0, 0, ...}
*/
class xoshiro256ss {
public:
using result_type = std::uint64_t;
using seed_type = std::array<std::uint64_t, 4>;
public:
/* null state -- generates constant stream of 0 bits */
xoshiro256ss() : xoshiro256ss(0) {}
/* copy ctor */
xoshiro256ss(xoshiro256ss const & x) = default;
xoshiro256ss(seed_type const & seed) : s_(seed) {}
/* fallback version -- deprecated */
xoshiro256ss(std::uint64_t seed)
{
this->s_[0] = 0;
this->s_[1] = seed;
this->s_[2] = 0;
this->s_[3] = 0;
generate();
}
static constexpr std::uint64_t min() { return 0; }
static constexpr std::uint64_t max() { return std::numeric_limits<std::uint64_t>::max(); }
static std::uint64_t rol64(std::uint64_t x, std::int64_t k)
{
return (x << k) | (x >> (64 - k));
}
static bool equal(xoshiro256ss const & x, xoshiro256ss const & y) {
return ((x.s_[0] == y.s_[0])
&& (x.s_[1] == y.s_[1])
&& (x.s_[2] == y.s_[2])
&& (x.s_[3] == y.s_[3]));
}
/* puts generator into null state */
void seed() { *this = xoshiro256ss(); }
void seed(std::uint64_t s) { *this = xoshiro256ss{s}; }
/* e.g. used with std::seed_seq<> */
template <typename SeedSeq>
void seed(SeedSeq & sseq) {
sseq.generate(s_.begin(), s_.end());
}
std::uint64_t generate() {
std::array<std::uint64_t, 4> & s = (this->s_);
std::uint64_t const result = rol64(s[1] * 5, 7) * 9;
std::uint64_t const t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = rol64(s[3], 45);
return result;
} /*generate*/
/* advance to same state as obtained from z calls to .generate(). O(z) !
* usually better to use jump().
*
* providing .discard() to satisfy c++ named requirement _RandomNumberEngine_
*/
void discard(std::uint64_t z) {
for (std::uint64_t i=0; i<z; ++i)
this->generate();
}
/* equivalent to .discard(2^128), but uses O(1) time
*
* (may use in multithreaded program to get determinsitic non-overlapping random sequences)
*/
void jump() {
std::array<std::uint64_t, 4> const s_jump_v
= {{0x180ec6d33cfd0aba,
0xd5a61266f0c9392c,
0xa9582618e03fc9aa,
0x39abdc4529b1661c}};
std::array<std::uint64_t, 4> & s = (this->s_);
std::uint64_t s0 = 0;
std::uint64_t s1 = 0;
std::uint64_t s2 = 0;
std::uint64_t s3 = 0;
for (std::uint32_t i = 0; i < s_jump_v.size(); ++i) {
for (std::uint32_t bit = 0; bit < 64; ++bit) {
if (s_jump_v[i] & 1UL << bit) {
s0 ^= s[0];
s1 ^= s[1];
s2 ^= s[2];
s3 ^= s[3];
}
this->generate();
}
}
s[0] = s0;
s[1] = s1;
s[2] = s2;
s[3] = s3;
} /*jump*/
/* inverse of .load() */
void print(std::ostream & os) const {
os << "<xoshiro256ss " << s_[0] << " " << s_[1] << " " << s_[2] << " " << s_[3] << ">";
}
/* inverse of .print() */
void load(std::istream & is) {
std::string header, trailer;
std::array<std::uint64_t, 4> sv;
is >> header >> sv[0] >> sv[1] >> sv[2] >> sv[3] >> trailer;
if ((header != "<xoshiro256ss") || trailer != ">")
throw std::runtime_error("xoshiro256ss.load: bad input format, expecting input like <xoshiro256ss $s0 $s1 $s2 $s3>");
this->s_ = sv;
} /*load*/
std::uint64_t operator()() { return generate(); }
private:
/* state */
std::array<std::uint64_t, 4> s_;
}; /*xoshiro256ss*/
inline bool operator==(xoshiro256ss const & x, xoshiro256ss const & y) {
return xoshiro256ss::equal(x, y);
}
inline bool operator!=(xoshiro256ss const & x, xoshiro256ss const & y) {
return !xoshiro256ss::equal(x, y);
}
static_assert(engine_concept<xoshiro256ss>);
} /*namespace rng*/
} /*namespace xo*/
/* end xoshiro256.hpp */