diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..a97774fb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +#general: +*.swp +*.o +*.so +*.a +*.dylib + +#mac +.DS_Store + +#from Eclipse +.project +.cproject + +#from cmake +CMakeCache.txt +CMakeFiles/ +Makefile +build*/ +cbuild*/ +cmake_install.cmake +*Dict.h +*Dict.cxx +Documentation/ReferenceGuide/Doxyfile +Source/Main/ExtractEvents +Source/Main/SimpleElectronHunt +Source/Applications/Main/SimpleElectronHunt +install_manifest.txt + +# from emacs +*~ diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..008b742f --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,257 @@ +name: Build and Test Nymph + +on: + pull_request: + push: + branches: [main, develop] + tags: ['*'] + +env: + Nymph_BUILD_NYMPH_EXE: ON + Nymph_ENABLE_EXECUTABLES: ON + Nymph_ENABLE_PYTHON: OFF + Nymph_ENABLE_TESTING: ON + Nymph_SINGLETHREADED: OFF + Nymph_BUILD_TYPE: Debug + Nymph_TAG: test + NARG: 2 + +jobs: + + docker_build: + name: Build and Test in Docker + + runs-on: ubuntu-latest + + steps: + + - name: Checkout the repo + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build + id: build + uses: docker/build-push-action@v5 + with: + context: . + push: false + load: true + build-args: | + img_repo=${{ env.BASE_IMAGE_REPO }} + img_tag=${{ env.BASE_IMAGE_TAG }} + build_type=${{ env.Nymph_BUILD_TYPE }} + build_tests_exe=${{ env.Nymph_ENABLE_TESTING }} + nymph_tag=${{ env.Nymph_TAG }} + narg=${{ env.NARG }} + platforms: linux/amd64 + tags: ${{ env.Nymph_TAG }} + + - name: Unit Tests + run: | + docker run --rm ${{ env.Nymph_TAG }} bash -c "/usr/local/p8/nymph/${{ env.Nymph_TAG }}/bin/RunTests" + + + build_cpp: + name: Build and Test Cpp + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macOS-latest] + python-version: [3] + + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Mac Dependencies + if: startsWith(matrix.os, 'macos') # Mac only + run: | + brew install \ + boost \ + rapidjson \ + yaml-cpp + sudo mkdir -p /usr/local/include && \ + sudo chown -R $(whoami) /usr/local/include + sudo mkdir -p /usr/local/lib && \ + sudo chown -R $(whoami) /usr/local/lib + sudo mkdir -p /usr/local/bin && \ + sudo chown -R $(whoami) /usr/local/bin + sudo mkdir -p /usr/local/share && \ + sudo chown -R $(whoami) /usr/local/share + git clone https://github.com/pybind/pybind11.git + cd pybind11 + git checkout v3.0.0 + mkdir build + cd build + cmake -DPYBIND11_TEST=FALSE .. + make -j${{ env.NARG }} install + + - name: Linux Dependencies + if: startsWith(matrix.os, 'ubuntu') # Linux only + run: | + sudo apt-get update + sudo apt-get install -yq \ + libboost-all-dev \ + libyaml-cpp-dev \ + rapidjson-dev + git clone https://github.com/pybind/pybind11.git + cd pybind11 + git checkout v3.0.0 + mkdir build + cd build + sudo cmake -DPYBIND11_TEST=FALSE .. + sudo make -j${{ env.NARG }} install + cd ../.. + pip install "pybind11[global]" + + - name: Configure CMake + run: | + mkdir -p build + cd build + cmake -DCMAKE_BUILD_TYPE=${Nymph_BUILD_TYPE} \ + -DNymph_BUILD_NYMPH_EXE=${Nymph_BUILD_NYMPH_EXE} \ + -DNymph_ENABLE_EXECUTABLES=${Nymph_ENABLE_EXECUTABLES} \ + -DNymph_ENABLE_PYTHON=${Nymph_ENABLE_PYTHON} \ + -DNymph_ENABLE_TESTING=${Nymph_ENABLE_TESTING} \ + -DNymph_SINGLETHREADED=${Nymph_SINGLETHREADED} \ + .. + + - name: CMake Build + run: | + cd build + make -j${{ env.NARG }} install + + - name: Run tests + run: | + cd build + Testing/RunTests + + - name: Build CppRepo Example + env: + Nymph_ENABLE_TESTING: OFF + Nymph_TAG: nymph2_2/cppexample + run: | + cd Examples/CppRepo + ./BUILD_ME.sh + + build_python: + name: Build and Test Python + + runs-on: ${{ matrix.os }} + + env: + Nymph_ENABLE_PYTHON: ON + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macOS-latest] + python-version: [3] + + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Mac Dependencies + if: startsWith(matrix.os, 'macos') # Mac only + run: | + brew install \ + boost \ + rapidjson \ + yaml-cpp + sudo mkdir -p /usr/local/include && \ + sudo chown -R $(whoami) /usr/local/include + sudo mkdir -p /usr/local/lib && \ + sudo chown -R $(whoami) /usr/local/lib + sudo mkdir -p /usr/local/bin && \ + sudo chown -R $(whoami) /usr/local/bin + sudo mkdir -p /usr/local/share && \ + sudo chown -R $(whoami) /usr/local/share + git clone https://github.com/pybind/pybind11.git + cd pybind11 + git checkout v3.0.0 + mkdir build + cd build + cmake -DPYBIND11_TEST=FALSE .. + make -j${{ env.NARG }} install + + - name: Linux Dependencies + if: startsWith(matrix.os, 'ubuntu') # Linux only + run: | + sudo apt-get update + sudo apt-get install -yq \ + libboost-all-dev \ + libyaml-cpp-dev \ + rapidjson-dev + git clone https://github.com/pybind/pybind11.git + cd pybind11 + git checkout v3.0.0 + mkdir build + cd build + sudo cmake -DPYBIND11_TEST=FALSE .. + sudo make -j${{ env.NARG }} install + + - name: Build Nymph/Cpp + run: | + mkdir -p build + cd build + cmake -DCMAKE_BUILD_TYPE=${Nymph_BUILD_TYPE} \ + -DNymph_BUILD_NYMPH_EXE=${Nymph_BUILD_NYMPH_EXE} \ + -DNymph_ENABLE_EXECUTABLES=${Nymph_ENABLE_EXECUTABLES} \ + -DNymph_ENABLE_PYTHON=${Nymph_ENABLE_PYTHON} \ + -DNymph_ENABLE_TESTING=${Nymph_ENABLE_TESTING} \ + -DNymph_SINGLETHREADED=${Nymph_SINGLETHREADED} \ + -DPBUILDER_PY_INSTALL_IN_SITELIB=TRUE \ + .. + make -j${{ env.NARG }} install + + - name: Install Nymph/Python + run: | + env + pip -v install -e . + + - name: Run Tests + run: | + cd Testing/Python/Bindings + python -m unittest discover -v + +# eventually include these tests +# cd ../nymph +# python -m unittest discover -v + + - name: Build PyRepo Example + env: + Nymph_ENABLE_TESTING: OFF + Nymph_ENABLE_PYTHON: ON + Nymph_TAG: nymph2_2/cppexample + run: | + cd Examples/PyRepo + ./BUILD_ME.sh + hello-world + + +# For debugging + - name: Setup tmate session + if: ${{ ! success() }} + uses: mxschmitt/action-tmate@v3 + diff --git a/.gitignore b/.gitignore index a97774fb..b0dd6e7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,49 @@ -#general: -*.swp +# from emacs +*~ + +# Compiled Object files +*.slo +*.lo *.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries *.so -*.a *.dylib +*.dll -#mac -.DS_Store +# Fortran module files +*.mod +*.smod -#from Eclipse +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Eclipse .project .cproject +.settings/ -#from cmake -CMakeCache.txt -CMakeFiles/ -Makefile +# VSCode +.vscode/ + +# Build directories build*/ -cbuild*/ -cmake_install.cmake -*Dict.h -*Dict.cxx -Documentation/ReferenceGuide/Doxyfile -Source/Main/ExtractEvents -Source/Main/SimpleElectronHunt -Source/Applications/Main/SimpleElectronHunt -install_manifest.txt -# from emacs -*~ +# Python Package +dist*/ +Python/*.egg-info +Examples/PyRepo/*.egg-info +__pycache__ diff --git a/.gitmodules b/.gitmodules index 5e770120..4a117285 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Scarab"] path = Scarab url = https://github.com/project8/scarab +[submodule "External/cereal"] + path = External/cereal + url = https://github.com/USCiLab/cereal.git diff --git a/AUTHORS b/AUTHORS index e2b4c3ee..9b85a753 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,8 +4,10 @@ Authors and Copyright Holders for Nymph Individuals ----------- Noah S. Oblath [1] +Benjamin H. LaRoque [2] Institutions ------------ 1. Pacific Northwest National Laboratory, Richland, WA +2. University of California, Santa Barbara, CA diff --git a/CMakeLists.txt b/CMakeLists.txt index 33e397fe..08f9633e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,11 @@ cmake_minimum_required (VERSION 3.12) # Setup # ######### +# load the version number from the nymph/VERSION file +file( STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/VERSION nymph_version ) + cmake_policy( SET CMP0048 NEW ) # version in project() -project( Nymph VERSION 1.5.2 ) +project( Nymph VERSION ${nymph_version} ) list( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/Scarab/cmake ) include( PackageBuilder ) @@ -23,14 +26,14 @@ include( Nymph ) # Nymph options # ################# -# require C++11 -set( CMAKE_CXX_STANDARD 11 ) +# require C++17 (for: if constexpr) +set( CMAKE_CXX_STANDARD 17 ) # Build the Nymph executable (also requires Nymph_ENABLE_EXECUTABLES) option( Nymph_BUILD_NYMPH_EXE "Flag to build the Nymph executable; also requires Nymph_ENABLE_EXECUTABLES" ON ) # Build python bindings (requires boost::python) -option( Nymph_ENABLE_PYTHON "Build python bindings (NymphPy)" OFF ) +option( Nymph_ENABLE_PYTHON "Build python bindings" OFF ) # Add a "Standard" build type #set (CMAKE_BUILD_TYPE standard) @@ -66,23 +69,40 @@ set( PRIVATE_EXT_LIBS ) ####### # Boost (1.46 required for filesystem version 3) -list( APPEND BOOST_COMPONENTS date_time filesystem program_options system thread ) +list( APPEND BOOST_COMPONENTS chrono date_time filesystem thread ) find_package( Boost 1.46.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS} ) -list( APPEND PUBLIC_EXT_LIBS Boost::boost Boost::date_time Boost::program_options Boost::thread Boost::filesystem ) +list( APPEND PUBLIC_EXT_LIBS Boost::boost Boost::chrono Boost::date_time Boost::thread Boost::filesystem ) # make sure dynamic linking is assumed for all boost libraries set( Boost_USE_STATIC_LIBS OFF ) set( Boost_USE_STATIC_RUNTIME OFF ) set( Boost_ALL_DYN_LINK ON ) -add_definitions (-DBOOST_ALL_DYN_LINK) +add_definitions( -DBOOST_ALL_DYN_LINK ) -# Python +########## +# Pybind11 +########## if( Nymph_ENABLE_PYTHON ) + find_package( Python3 REQUIRED COMPONENTS Interpreter Development ) + set(PYBIND11_PYTHON_VERSION ${Python3_VERSION} CACHE STRING "") + find_package( pybind11 REQUIRED ) + include_directories( ${Python3_INCLUDE_DIRS} ) + add_compile_definitions( NYMPH_USING_PYTHON ) set_option( Scarab_BUILD_PYTHON TRUE ) + add_subdirectory( Python/Bindings ) else( Nymph_ENABLE_PYTHON ) + remove_definitions( NYMPH_USING_PYTHON ) set_option( Scarab_BUILD_PYTHON FALSE ) endif( Nymph_ENABLE_PYTHON ) +######## +# Cereal +######## + +#set( JUST_INSTALL_CEREAL ON CACHE BOOL "Cereal option: only install library" FORCE) +#add_subdirectory( External/cereal ) +#include_directories( External/cereal/include ) + ##################### # Prepare for build # @@ -105,7 +125,7 @@ pbuilder_add_submodule( Scarab Scarab ) pbuilder_use_sm_library( Scarab Scarab ) # this is needed for anything that wants to read or write JSON files -get_directory_property( RAPIDJSON_FILE_BUFFER_SIZE DIRECTORY Scarab/library DEFINITION RAPIDJSON_FILE_BUFFER_SIZE ) +get_directory_property( RAPIDJSON_FILE_BUFFER_SIZE DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Scarab DEFINITION RAPIDJSON_FILE_BUFFER_SIZE ) set( RAPIDJSON_FILE_BUFFER_SIZE ${RAPIDJSON_FILE_BUFFER_SIZE} PARENT_SCOPE ) @@ -113,16 +133,25 @@ set( RAPIDJSON_FILE_BUFFER_SIZE ${RAPIDJSON_FILE_BUFFER_SIZE} PARENT_SCOPE ) # Build Nymph # ############### -add_subdirectory( Library ) +add_subdirectory( Cpp/Library ) -if (Nymph_ENABLE_TESTING) - add_subdirectory( Executables/Validation ) -endif (Nymph_ENABLE_TESTING) +#if (Nymph_ENABLE_EXECUTABLES) +# add_subdirectory( Cpp/Executables_v1 ) +#endif (Nymph_ENABLE_EXECUTABLES ) if (Nymph_ENABLE_EXECUTABLES) - add_subdirectory( Executables/Main ) + add_subdirectory( Cpp/Executables/Main ) + #add_subdirectory( Cpp/Executables/Validation ) endif (Nymph_ENABLE_EXECUTABLES ) +######### +# Tests # +######### + +if( Nymph_ENABLE_TESTING ) + add_subdirectory( Testing ) +endif() + ########## # Config # diff --git a/Cpp/Executables/Main/CMakeLists.txt b/Cpp/Executables/Main/CMakeLists.txt new file mode 100644 index 00000000..c7ad8421 --- /dev/null +++ b/Cpp/Executables/Main/CMakeLists.txt @@ -0,0 +1,46 @@ +# CMakeLists for Nymph/Executables/Main +# Author: N. Oblath + +if( NOT Nymph_AS_SUBMODULE ) + set( Nymph_package_libs + Nymph + ) +endif() + +set( Nymph_SOURCES +# ConfigCheck.cc +) +if( Nymph_BUILD_NYMPH_EXE ) + set( Nymph_SOURCES + ${Nymph_SOURCES} + Nymph.cc + ) +endif( Nymph_BUILD_NYMPH_EXE ) + +if( Nymph_ENABLE_PYTHON ) + set( Nymph_SOURCES + ${Nymph_SOURCES} + PyImportTest.cc + ) + set( Nymph_EXE_LIBRARIES + ${Nymph_EXE_LIBRARIES} + pybind11::embed + pybind11::module + ) +endif() + +if( Nymph_SOURCES ) + set( Nymph_PROGRAMS ) + pbuilder_executables( + SOURCES ${Nymph_SOURCES} + TARGETS_VAR Nymph_PROGRAMS + PROJECT_LIBRARIES ${Nymph_package_libs} + PUBLIC_EXTERNAL_LIBRARIES ${Nymph_EXE_LIBRARIES} + # PRIVATE_EXTERNAL_LIBRARIES ${PRIVATE_EXT_LIBS} + ) + + pbuilder_component_install_and_export( + COMPONENT Executables + EXETARGETS ${Nymph_PROGRAMS} + ) +endif( Nymph_SOURCES ) diff --git a/Cpp/Executables/Main/Nymph.cc b/Cpp/Executables/Main/Nymph.cc new file mode 100644 index 00000000..636f580c --- /dev/null +++ b/Cpp/Executables/Main/Nymph.cc @@ -0,0 +1,97 @@ +/* + * Nymph_Exe.cc + * + * Created on: Sep 28, 2012 + * Author: nsoblath + * Updated on: Jun 14, 2021 + * Author: jkgaison + * + * This program will run any processor-based code in packages built with Nymph. + * All of the action is setup with a config file. + * See scarab::application for details on the configuration option. + */ + +#include "RunNymph.hh" + +#include "application.hh" +#include "logger.hh" +#include "param_codec.hh" +#include "signal_handler.hh" + +LOGGER( nymphlog, "Nymph" ); + +int main( int argc, char** argv ) +{ + + auto splash = [](){ + LPROG( nymphlog, "Welcome to Nymph!" ); + LDEBUG( nymphlog, + "\n" << + " Z \n" << + " =M \n" << + " M \n" << + " M \n" << + " =M \n" << + " N \n" << + " M NINMMMMMZ...M \n" << + " M $$ \n" << + " M: N. IM \n" << + " ZD. M? M \n" << + " $ M ?M MM .$ \n" << + " DM M O 8 .+MZ \n" << + " ~M M~ 8 M~ MM:~M \n" << + " O:MMIZ+,?DNMD$.... M,MM MZ ? D$= .8MOM+ \n" << + " ,= MN ZZ D: M:MM~MM== MMZM \n" << + " ...MM MMM8,M+ I:ND $MMM \n" << + " MMMMO ~M, ,MM7MOD$ \n" << + " MMMMMM ~~8M? ,O~= MDNM M , \n" << + " MMMM:M Z8 MMM :MNM = .., MZ \n" << + " MNMMNM MM IM NM,M N M MM +M .NM M\n" << + " ~MMD8 : D MM=MM, O MO 8= + $ .IM \n" << + " ONMMMMMM..? 8 :MMMMM = M N M :M \n" << + " ..+M8=...:=MMMMMMMMMN OMM:M, M ~ 7 MMM8 \n" << + "ZM8 ~M+:MZMMM~ M OM M \n" << + " .:M..$D..$D ? M, MM MN \n" << + " $M7 M M+ M NMMMM:.. \n" << + " M..M :N 7M \n" << + " MM, M D \n" << + " M \n" << + " M= \n" << + " M \n" << + " = \n" << + " M \n"); + }; + + // Start handling signals + scarab::signal_handler t_sig_hand; + + int the_return = -1; + + // Create the application + scarab::main_app the_main; + //Runs RunNymph() and sets the_return based on its return value + auto t_callback = [&](){ + // If any subcommands were called, we don't execute the main callback + if( the_main.get_subcommands().size() > 0 ) return; + the_return = Nymph::RunNymph( the_main.primary_config() ); + }; + the_main.callback( t_callback ); + the_main.splash() = splash; + + // Checks for the existence of a processor type and lists processors + scarab::config_decorator* t_proc_check = the_main.add_config_subcommand( "proc-check", "Check if a processor type is known" ); + t_proc_check->this_app()->callback( [&](){ the_return = Nymph::ProcessorCheck(the_main.primary_config()); } ); + + the_main.require_subcommand(0, 1); + the_main.set_global_verbosity(scarab::logger::ELevel::eDebug); + + // add the typical CL options + Nymph::AddRunNymphOptions( the_main ); + Nymph::AddProcessorCheckOptions( t_proc_check ); + + // Parse CL options and run the application + CLI11_PARSE( the_main, argc, argv ); + + return the_return; + +} diff --git a/Cpp/Executables/Main/PyImportTest.cc b/Cpp/Executables/Main/PyImportTest.cc new file mode 100644 index 00000000..e52669e0 --- /dev/null +++ b/Cpp/Executables/Main/PyImportTest.cc @@ -0,0 +1,15 @@ + +#include +#include + +namespace py = pybind11; + +int main() +{ + py::scoped_interpreter guard{}; + + py::module_ sys = py::module_::import("sys"); + py::print(sys.attr("path")); + + return 0; +} diff --git a/Executables/Main/CMakeLists.txt b/Cpp/Executables_v1/Main/CMakeLists.txt similarity index 88% rename from Executables/Main/CMakeLists.txt rename to Cpp/Executables_v1/Main/CMakeLists.txt index 3644b97c..a7ba2b18 100644 --- a/Executables/Main/CMakeLists.txt +++ b/Cpp/Executables_v1/Main/CMakeLists.txt @@ -1,4 +1,4 @@ -# CMakeLists for Nymph/Executables/Main +# CMakeLists for Nymph/Executables_v1/Main # Author: N. Oblath if( NOT Nymph_AS_SUBMODULE ) @@ -8,12 +8,12 @@ if( NOT Nymph_AS_SUBMODULE ) endif() set( Nymph_SOURCES - ConfigCheck.cc +# ConfigCheck.cc ) if( Nymph_BUILD_NYMPH_EXE ) set( Nymph_SOURCES ${Nymph_SOURCES} - Nymph + Nymph.cc ) endif( Nymph_BUILD_NYMPH_EXE ) diff --git a/Executables/Main/ConfigCheck.cc b/Cpp/Executables_v1/Main/ConfigCheck.cc similarity index 100% rename from Executables/Main/ConfigCheck.cc rename to Cpp/Executables_v1/Main/ConfigCheck.cc diff --git a/Executables/Main/Nymph.cc b/Cpp/Executables_v1/Main/Nymph.cc similarity index 100% rename from Executables/Main/Nymph.cc rename to Cpp/Executables_v1/Main/Nymph.cc diff --git a/Executables/Validation/CMakeLists.txt b/Cpp/Executables_v1/Validation/CMakeLists.txt similarity index 97% rename from Executables/Validation/CMakeLists.txt rename to Cpp/Executables_v1/Validation/CMakeLists.txt index b40d9296..90bc734a 100644 --- a/Executables/Validation/CMakeLists.txt +++ b/Cpp/Executables_v1/Validation/CMakeLists.txt @@ -1,4 +1,4 @@ -# CMakeLists for Nymph/Executables/Validation +# CMakeLists for Nymph/Executables_v1/Validation # Author: N. Oblath ############################ diff --git a/Executables/Validation/ConfigFiles/TestConfigurator.json b/Cpp/Executables_v1/Validation/ConfigFiles/TestConfigurator.json similarity index 100% rename from Executables/Validation/ConfigFiles/TestConfigurator.json rename to Cpp/Executables_v1/Validation/ConfigFiles/TestConfigurator.json diff --git a/Executables/Validation/ConfigFiles/TestParameterStoreConfig.json b/Cpp/Executables_v1/Validation/ConfigFiles/TestParameterStoreConfig.json similarity index 100% rename from Executables/Validation/ConfigFiles/TestParameterStoreConfig.json rename to Cpp/Executables_v1/Validation/ConfigFiles/TestParameterStoreConfig.json diff --git a/Cpp/Executables_v1/Validation/KTJSONReader.cc b/Cpp/Executables_v1/Validation/KTJSONReader.cc new file mode 100644 index 00000000..2da348bf --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTJSONReader.cc @@ -0,0 +1,39 @@ +/* + * KTJSONReader.cc + * + * Created on: Feb 1, 2018 + * Author: N.S. Oblath + */ + +// Data classes need to be included before the archive header +#include "KTTestData.hh" + +#include "KTJSONReader.hh" + + +namespace Nymph +{ + + KTJSONReader::KTJSONReader( const std::string& name) : + KTProcessor( name ), + fFilename( "json_reader_default_file.json" ), + fStreamInPtr( nullptr ), + fArchiveInPtr( nullptr ) + { + } + + KTJSONReader::~KTJSONReader() + { + // archive must be delete before the stream! + delete fArchiveInPtr; + delete fStreamInPtr; + } + + bool KTJSONReader::Configure( const scarab::param_node& node ) + { + SetFilename( node.get_value( "filename" , fFilename )); + + return true; + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTJSONReader.hh b/Cpp/Executables_v1/Validation/KTJSONReader.hh new file mode 100644 index 00000000..ca6506dc --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTJSONReader.hh @@ -0,0 +1,62 @@ +/* + * KTJSONReader.hh + * + * Created on: Feb 1, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTJSONREADER_HH_ +#define NYMPH_KTJSONREADER_HH_ + +#include "KTProcessor.hh" + +#include "KTLogger.hh" + +#include "cereal/archives/json.hpp" + + +KTLOGGER( jsrlog_hh, "KTJSONReader" ) + + +namespace Nymph +{ + + class KTJSONReader : public KTProcessor + { + public: + KTJSONReader( const std::string& name = "json-reader" ); + virtual ~KTJSONReader(); + + bool Configure( const scarab::param_node& node ); + + MEMBERVARIABLE( std::string, Filename ); + + public: + template< class XDataType > + void ReadData( XDataType& data ); + + private: + std::ifstream* fStreamInPtr; + cereal::JSONInputArchive* fArchiveInPtr; + + }; + + template< class XDataType > + void KTJSONReader::ReadData( XDataType& data ) + { + if( fStreamInPtr == nullptr ) + { + KTDEBUG( jsrlog_hh, "Creating stream and archive" ); + fStreamInPtr = new std::ifstream( fFilename ); + fArchiveInPtr = new cereal::JSONInputArchive( *fStreamInPtr ); + } + + KTDEBUG( jsrlog_hh, "Reading an object from JSON" ); + (*fArchiveInPtr)( data ); + + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_KTJSONREADER_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.cc b/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.cc new file mode 100644 index 00000000..3e9cc281 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.cc @@ -0,0 +1,32 @@ +/* + * KTJSONWriterValidation.cc + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#include "KTJSONTypeWriterValidation.hh" +#include "KTRegisterNymphValidationExtData.hh" + +namespace Nymph +{ + KT_REGISTER_TYPE_WRITER( KTJSONWriter, KTJSONTypeWriter, Validation ); + + KTJSONTypeWriterValidation::KTJSONTypeWriterValidation( KTJSONWriter* writer) : + KTJSONTypeWriter( writer ), + fTestSlot( "test", fWriter, &KTJSONWriter::WriteData< KTTestDataExt > ), + fTestDerived1Slot( "test-derived-1", fWriter, &KTJSONWriter::WriteData< KTTestDerived1DataExt > ), + fTestDerived2Slot( "test-derived-2", fWriter, &KTJSONWriter::WriteData< KTTestDerived2DataExt > ) + { + std::cout << "Created a KTJSONWriterValidation" << std::endl; + } + + KTJSONTypeWriterValidation::~KTJSONTypeWriterValidation() + { + } + + void KTJSONTypeWriterValidation::RegisterSlots() + { + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.hh b/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.hh new file mode 100644 index 00000000..80436e50 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTJSONTypeWriterValidation.hh @@ -0,0 +1,36 @@ +/* + * KTJSONTypeWriterValidation.hh + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTJSONTYPEWRITERVALIDATION_HH_ +#define NYMPH_KTJSONTYPEWRITERVALIDATION_HH_ + +#include "KTJSONWriter.hh" + +namespace Nymph +{ + class KTTestDataExt; + class KTTestDerived1DataExt; + class KTTestDerived2DataExt; + + class KTJSONTypeWriterValidation : public KTJSONTypeWriter + { + public: + KTJSONTypeWriterValidation( KTJSONWriter* writer ); + virtual ~KTJSONTypeWriterValidation(); + + virtual void RegisterSlots(); + + private: + KTSlotData< void, KTTestDataExt > fTestSlot; + KTSlotData< void, KTTestDerived1DataExt > fTestDerived1Slot; + KTSlotData< void, KTTestDerived2DataExt > fTestDerived2Slot; + + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_KTJSONTYPEWRITERVALIDATION_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTPyTestClass.cc b/Cpp/Executables_v1/Validation/KTPyTestClass.cc new file mode 100644 index 00000000..43c8f3b6 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTPyTestClass.cc @@ -0,0 +1,22 @@ +/* + * KTPyTestClass.cc + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#include "KTPyTestClass.hh" + +namespace Nymph +{ + + KTPyTestClass::KTPyTestClass() : + fValue( 5 ) + { + } + + KTPyTestClass::~KTPyTestClass() + { + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTPyTestClass.hh b/Cpp/Executables_v1/Validation/KTPyTestClass.hh new file mode 100644 index 00000000..9e6ad273 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTPyTestClass.hh @@ -0,0 +1,44 @@ +/* + * KTPyTestClass.hh + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#ifndef NYMPH_KTPYTESTCLASS_HH_ +#define NYMPH_KTPYTESTCLASS_HH_ + +#include + +namespace Nymph +{ + + class KTPyTestClass + { + public: + KTPyTestClass(); + ~KTPyTestClass(); + + void SayHello() + { + std::cout << "Hello" << std::endl; + } + + int GetValue() + { + return fValue; + } + + void SetValue( int value ) + { + fValue = value; + return; + } + + private: + int fValue; + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_KTPYTESTCLASS_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTPyTestClassPybind.hh b/Cpp/Executables_v1/Validation/KTPyTestClassPybind.hh new file mode 100644 index 00000000..459ecce2 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTPyTestClassPybind.hh @@ -0,0 +1,26 @@ +/* + * KTPyTestClassPybind.cc + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#include "KTPyTestClass.hh" +#include "KTTestProcessor.hh" + +#include "pybind11/pybind11.h" + +namespace Nymph +{ + + void ExportKTPyTestClass( pybind11::module& mod ) + { + pybind11::class_< Nymph::KTPyTestClass >( mod, "KTPyTestClass" ) + .def( pybind11::init<>() ) + .def( "SayHello", &Nymph::KTPyTestClass::SayHello ) + .def( "GetValue", &Nymph::KTPyTestClass::GetValue ) + .def( "SetValue", &Nymph::KTPyTestClass::SetValue ) + ; + }; + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTRegisterNymphValidationExtData.hh b/Cpp/Executables_v1/Validation/KTRegisterNymphValidationExtData.hh new file mode 100644 index 00000000..efa5da6d --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTRegisterNymphValidationExtData.hh @@ -0,0 +1,19 @@ +/* + * KTRegisterNymphValidationExtData.hh + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTREGISTERNYMPHVALIDATIONEXTDATA_HH_ +#define NYMPH_KTREGISTERNYMPHVALIDATIONEXTDATA_HH_ + +#include "KTRegisterNymphExtData.hh" + +#include "KTTestData.hh" + +CEREAL_REGISTER_TYPE( Nymph::KTTestDataExt ); +CEREAL_REGISTER_TYPE( Nymph::KTTestDerived1DataExt ); +CEREAL_REGISTER_TYPE( Nymph::KTTestDerived2DataExt ); + +#endif /* NYMPH_KTREGISTERNYMPHVALIDATIONEXTDATA_HH_ */ diff --git a/Executables/Validation/KTTestConfigurable.cc b/Cpp/Executables_v1/Validation/KTTestConfigurable.cc similarity index 100% rename from Executables/Validation/KTTestConfigurable.cc rename to Cpp/Executables_v1/Validation/KTTestConfigurable.cc diff --git a/Executables/Validation/KTTestConfigurable.hh b/Cpp/Executables_v1/Validation/KTTestConfigurable.hh similarity index 100% rename from Executables/Validation/KTTestConfigurable.hh rename to Cpp/Executables_v1/Validation/KTTestConfigurable.hh diff --git a/Executables/Validation/KTTestCuts.cc b/Cpp/Executables_v1/Validation/KTTestCuts.cc similarity index 55% rename from Executables/Validation/KTTestCuts.cc rename to Cpp/Executables_v1/Validation/KTTestCuts.cc index c3e7723c..d78aeba3 100644 --- a/Executables/Validation/KTTestCuts.cc +++ b/Cpp/Executables_v1/Validation/KTTestCuts.cc @@ -8,32 +8,21 @@ #include "KTTestCuts.hh" #include "KTLogger.hh" +#include "KTTestData.hh" namespace Nymph { KTLOGGER(testlog, "KTTestCuts"); - const std::string KTTestData::sName = "test-data"; - - const std::string KTAwesomeCut::Result::sName = "awesome-cut"; - const std::string KTNotAwesomeCut::Result::sName = "not-awesome-cut"; - - KT_REGISTER_CUT(KTAwesomeCut); - KT_REGISTER_CUT(KTNotAwesomeCut); - - - KTTestData::KTTestData() : - KTExtensibleData< KTTestData >(), - fIsAwesome(false) - {} - - KTTestData::~KTTestData() - {} + KT_REGISTER_CUT(KTAwesomeCut, "awesome-cut"); + KT_REGISTER_CUT(KTNotAwesomeCut, "not-awesome-cut" ); KTAwesomeCut::KTAwesomeCut(const std::string& name) : - KTCutOneArg(name) - {} + KTCutOnData(name) + { + SetApplyFunc( this, &KTAwesomeCut::Apply ); + } KTAwesomeCut::~KTAwesomeCut() {} @@ -43,19 +32,21 @@ namespace Nymph return true; } - bool KTAwesomeCut::Apply(KTData& data, KTTestData& testData) + bool KTAwesomeCut::Apply(KTCoreData& data, const KTTestData& testData) { bool isCut = ! testData.GetIsAwesome(); KTDEBUG(testlog, "Is data awesome? " << testData.GetIsAwesome()); KTDEBUG(testlog, "Is data cut? " << isCut); - data.GetCutStatus().AddCutResult< KTAwesomeCut::Result >(isCut); + data.CutStatus().SetCutState(fConfigName, isCut); return isCut; } KTNotAwesomeCut::KTNotAwesomeCut(const std::string& name) : - KTCutOneArg(name) - {} + KTCutOnData(name) + { + SetApplyFunc( this, &KTNotAwesomeCut::Apply ); + } KTNotAwesomeCut::~KTNotAwesomeCut() {} @@ -65,13 +56,12 @@ namespace Nymph return true; } - bool KTNotAwesomeCut::Apply(KTData& data, KTTestData& testData) + bool KTNotAwesomeCut::Apply(KTCoreData& data, const KTTestData& testData) { bool isCut = testData.GetIsAwesome(); KTDEBUG(testlog, "Is data awesome? " << testData.GetIsAwesome()); KTDEBUG(testlog, "Is data cut? " << isCut); - // use the name-based AddCutResult - data.GetCutStatus().AddCutResult("not-awesome-cut", isCut); + data.CutStatus().SetCutState(fConfigName, isCut); return isCut; } diff --git a/Cpp/Executables_v1/Validation/KTTestCuts.hh b/Cpp/Executables_v1/Validation/KTTestCuts.hh new file mode 100644 index 00000000..5ebe0d6d --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestCuts.hh @@ -0,0 +1,48 @@ +/* + * KTTestCuts.hh + * + * Created on: Nov 2, 2016 + * Author: obla999 + */ + +#ifndef NYMPH_KTTESTCUTS_HH_ +#define NYMPH_KTTESTCUTS_HH_ + +#include "KTCut.hh" +#include "KTMemberVariable.hh" +#include "KTCoreData.hh" + + +namespace Nymph +{ + class KTTestData; + class KTTestDataExt; + + // Cuts data that is NOT awesome + class KTAwesomeCut : public KTCutOnData< KTTestDataExt > + { + public: + KTAwesomeCut(const std::string& name = "default-awesome-cut"); + virtual ~KTAwesomeCut(); + + bool Configure(const scarab::param_node& node); + + bool Apply(KTCoreData& data, const KTTestData& testData); + }; + + // Cuts data that is IS awesome + class KTNotAwesomeCut : public KTCutOnData< KTTestDataExt > + { + public: + KTNotAwesomeCut(const std::string& name = "default-not-awesome-cut"); + virtual ~KTNotAwesomeCut(); + + bool Configure(const scarab::param_node& node); + + bool Apply(KTCoreData& data, const KTTestData& testData); + }; + +} + + +#endif /* NYMPH_KTTESTCUTS_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTTestData.cc b/Cpp/Executables_v1/Validation/KTTestData.cc new file mode 100644 index 00000000..3bc6f328 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestData.cc @@ -0,0 +1,38 @@ +/* + * KTTestData.cc + * + * Created on: Jun 1, 2017 + * Author: obla999 + */ + +#include "KTTestData.hh" + +namespace Nymph +{ + KTTestData::KTTestData() : + KTData(), + fIsAwesome( false ) + { + std::cout << "### KTTestData constructor" << std::endl; + } + + KTTestData::~KTTestData() + { + std::cout << "### KTTestData destructor" << std::endl; + } + + KTTestBaseData::KTTestBaseData() : + KTData(), + fFunniness(1000.) + { + std::cout << "### KTTestBaseData constructor" << std::endl; + } + + KTTestBaseData::~KTTestBaseData() + { + std::cout << "### KTTestBaseData destructor" << std::endl; + } + + +} /* namespace Nymph */ + diff --git a/Cpp/Executables_v1/Validation/KTTestData.hh b/Cpp/Executables_v1/Validation/KTTestData.hh new file mode 100644 index 00000000..c28bf552 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestData.hh @@ -0,0 +1,71 @@ +/* + * KTTestData.hh + * + * Created on: Jun 1, 2017 + * Author: obla999 + */ + +#ifndef NYMPH_KTTESTDATA_HH_ +#define NYMPH_KTTESTDATA_HH_ + +#include "KTCoreData.hh" +#include "KTMemberVariable.hh" + + +namespace Nymph +{ + + class KTTestData : public KTData + { + public: + KTTestData(); + virtual ~KTTestData(); + + MEMBERVARIABLE(bool, IsAwesome); + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); + }; + + DEFINE_EXT_DATA( KTTestData, "test" ); // defines KTTestDataExt + + + class KTTestBaseData : public KTData + { + public: + KTTestBaseData(); + virtual ~KTTestBaseData(); + + MEMBERVARIABLE(double, Funniness); + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); + }; + + DEFINE_EXT_DATA_2( KTTestDerived1DataExt, KTTestBaseData, "test-derived-1" ); + DEFINE_EXT_DATA_2( KTTestDerived2DataExt, KTTestBaseData, "test-derived-2" ); + + + template< class Archive > + void KTTestData::serialize( Archive& ar ) + { + std::cout << "### serialize for KTTestData" << std::endl; + ar( cereal::base_class< KTData >( this ), CEREAL_NVP(fIsAwesome) ); + } + + template< class Archive > + void KTTestBaseData::serialize( Archive& ar ) + { + std::cout << "### serialize for KTTestBaseData" << std::endl; + ar( cereal::base_class< KTData >( this ), CEREAL_NVP(fFunniness) ); + } + +} /* namespace Nymph */ + +#endif /* NYMPH_KTTESTDATA_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.cc b/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.cc new file mode 100644 index 00000000..218f4956 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.cc @@ -0,0 +1,62 @@ +/* + * KTTestDataPrimaryProcessor.cc + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#include "KTTestDataPrimaryProcessor.hh" + +#include "KTTestData.hh" + +#include "KTLogger.hh" + +LOGGER( testlog, "KTTestDataPrimaryProcessor" ); + +namespace Nymph +{ + + KT_REGISTER_PROCESSOR( KTTestDataPrimaryProcessor, "test-data-p-proc" ); + + KTTestDataPrimaryProcessor::KTTestDataPrimaryProcessor( const std::string& name ) : + KTPrimaryProcessor( {"test-derived-1"}, name ), + fIterations(10), + fTheSignal("test-derived-1", this) + { + } + + KTTestDataPrimaryProcessor::~KTTestDataPrimaryProcessor() + { + } + + bool KTTestDataPrimaryProcessor::Configure( const scarab::param_node& node ) + { + SetIterations( node.get_value( "iterations", GetIterations() ) ); + + return true; + } + + bool KTTestDataPrimaryProcessor::Run() + { + unsigned funniness = 867; + + for( unsigned iIteration = 0; iIteration < fIterations; ++iIteration ) + { + KTINFO( testlog, "Beginning iteration " << iIteration ); + + // e.g. for a real processor, do some work here instead of sleeping + boost::this_thread::sleep_for( boost::chrono::milliseconds(1) ); + + if( fThreadRef && fThreadRef->GetCanceled() ) break; + + KTDataHandle handle = CreateNewDataHandle(); + KTTestDerived1DataExt& testData = handle->Of< KTTestDerived1DataExt >(); + testData.SetFunniness( funniness++ ); + KTINFO( testlog, "Test derived1 data funniness: " << testData.GetFunniness() ); + + fTheSignal( handle ); + } + return true; + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.hh b/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.hh new file mode 100644 index 00000000..96b69555 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestDataPrimaryProcessor.hh @@ -0,0 +1,40 @@ +/* + * KTTestPrimaryProcessor.hh + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#ifndef KTTESTDATAPRIMARYPROCESSOR_HH_ +#define KTTESTDATAPRIMARYPROCESSOR_HH_ + +#include "KTPrimaryProcessor.hh" + +#include "KTMemberVariable.hh" +#include "KTSignal.hh" + +namespace Nymph +{ + + class KTTestDataPrimaryProcessor : public KTPrimaryProcessor + { + public: + KTTestDataPrimaryProcessor( const std::string& name = "test-data-p-proc" ); + virtual ~KTTestDataPrimaryProcessor(); + + public: + bool Configure( const scarab::param_node& node ); + + MEMBERVARIABLE( unsigned, Iterations ); + + public: + virtual bool Run(); + + private: + KTSignalData fTheSignal; + + }; + +} /* namespace Nymph */ + +#endif /* KTTESTDATAPRIMARYPROCESSOR_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.cc b/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.cc new file mode 100644 index 00000000..43692c1a --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.cc @@ -0,0 +1,47 @@ +/* + * KTTestPrimaryProcessor.cc + * + * Created on: May 4, 2017 + * Author: N.S. Oblath + */ + +#include "KTTestPrimaryProcessor.hh" + +namespace Nymph +{ + + KT_REGISTER_PROCESSOR(KTTestPrimaryProcessor, "test-p-proc"); + + KTTestPrimaryProcessor::KTTestPrimaryProcessor( const std::string& name ) : + KTPrimaryProcessor( {"the-signal"}, name ), + fIterations(10), + fTheSignal("the-signal", this) + { + } + + KTTestPrimaryProcessor::~KTTestPrimaryProcessor() + { + } + + bool KTTestPrimaryProcessor::Configure( const scarab::param_node& node ) + { + SetIterations( node.get_value( "iterations", GetIterations() ) ); + + return true; + } + + bool KTTestPrimaryProcessor::Run() + { + for( unsigned iIteration = 0; iIteration < fIterations; ++iIteration ) + { + // e.g. for a real processor, do some work here instead of sleeping + boost::this_thread::sleep_for( boost::chrono::milliseconds(1) ); + + if( fThreadRef && fThreadRef->GetCanceled() ) break; + + fTheSignal( iIteration ); + } + return true; + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.hh b/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.hh new file mode 100644 index 00000000..4eb76399 --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestPrimaryProcessor.hh @@ -0,0 +1,40 @@ +/* + * KTTestPrimaryProcessor.hh + * + * Created on: May 4, 2017 + * Author: N.S. Oblath + */ + +#ifndef KTTESTPRIMARYPROCESSOR_HH_ +#define KTTESTPRIMARYPROCESSOR_HH_ + +#include "KTPrimaryProcessor.hh" + +#include "KTMemberVariable.hh" +#include "KTSignal.hh" + +namespace Nymph +{ + + class KTTestPrimaryProcessor : public KTPrimaryProcessor + { + public: + KTTestPrimaryProcessor( const std::string& name = "test-p-proc" ); + virtual ~KTTestPrimaryProcessor(); + + public: + bool Configure( const scarab::param_node& node ); + + MEMBERVARIABLE( unsigned, Iterations ); + + public: + virtual bool Run(); + + private: + KTSignal< int > fTheSignal; + + }; + +} /* namespace Nymph */ + +#endif /* KTTESTPRIMARYPROCESSOR_HH_ */ diff --git a/Cpp/Executables_v1/Validation/KTTestProcessor.cc b/Cpp/Executables_v1/Validation/KTTestProcessor.cc new file mode 100644 index 00000000..f19a3eed --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestProcessor.cc @@ -0,0 +1,151 @@ +/* + * KTTestProcessor.cc + * + * Created on: Aug 15, 2012 + * Author: nsoblath + */ + +#include "KTTestProcessor.hh" + +#include "KTException.hh" +#include "KTLogger.hh" + +//#include "KTTestData.hh" + +namespace Nymph +{ + KTLOGGER(testsiglog, "KTTestProcessor") + + KT_REGISTER_PROCESSOR(KTTestProcessorA, "test-proc-a"); + + KTTestProcessorA::KTTestProcessorA( const std::string& name ) : + KTProcessor( name ), + fTheSignal("the-signal", this) + { + } + + KTTestProcessorA::~KTTestProcessorA() + { + } + + bool KTTestProcessorA::Configure(const scarab::param_node&) + { + return true; + } + + void KTTestProcessorA::EmitSignals(int value) + { + fTheSignal(value); + return; + } + + + KT_REGISTER_PROCESSOR(KTTestProcessorB, "test-proc-b"); + + KTTestProcessorB::KTTestProcessorB( const std::string& name ) : + KTProcessor( name ), + fSlot1("first-slot", this, &KTTestProcessorB::SlotFunc1), + fSlot2("second-slot", this, &KTTestProcessorB::SlotFunc2) + { + fSlot1Wrapper = GetSlot( "first-slot" ); + fSlot2Wrapper = GetSlot( "second-slot" ); + } + + KTTestProcessorB::~KTTestProcessorB() + { + } + + bool KTTestProcessorB::Configure(const scarab::param_node&) + { + return true; + } + + void KTTestProcessorC::SlotFunc1(int input) + { + std::shared_ptr< KTThreadReference > ref = fSlot1.GetSlotWrapper()->GetThreadRef(); + + KTINFO(testsiglog, "Slot1: input is " << input); + BOOST_THROW_EXCEPTION( KTException() << "A HUGE problem occurred!!!! (just kidding, this is the expected result)" << eom ); + + return; + } + + + KT_REGISTER_PROCESSOR(KTTestProcessorD, "test-proc-d"); + + KTTestProcessorD::KTTestProcessorD( const std::string& name ) : + KTProcessor( name ), + fDataSlot("test", this, &KTTestProcessorD::AnalysisFunc), + fDataSignal("test", this) + { + } + + KTTestProcessorD::~KTTestProcessorD() + { + } + + bool KTTestProcessorD::Configure(const scarab::param_node&) + { + return true; + } + + void KTTestProcessorD::EmitSignal(bool isAwesome) + { + KTDataHandle dataHandle = CreateNewDataHandle(); + KTTestDataExt& data = dataHandle->Of< KTTestDataExt >(); + data.SetIsAwesome(isAwesome); + fDataSignal(dataHandle); + return; + } + + void KTTestProcessorD::AnalysisFunc(const KTTestData& data) + { + KTINFO(testsiglog, "Is the data awesome? " << data.GetIsAwesome()); + return; + } + + + KT_REGISTER_PROCESSOR(KTTestProcessorE, "test-proc-e"); + + KTTestProcessorE::KTTestProcessorE( const std::string& name ) : + KTProcessor( name ), + fDerived1DataSlot("derived-1", this, &KTTestProcessorE::BaseAnalysisFunc), + fDerived2DataSlot("derived-2", this, &KTTestProcessorE::BaseAnalysisFunc), + fDerived1DataSignal("derived-1", this), + fDerived2DataSignal("derived-2", this) + { + } + + KTTestProcessorE::~KTTestProcessorE() + { + } + + bool KTTestProcessorE::Configure(const scarab::param_node&) + { + return true; + } + + void KTTestProcessorE::EmitSignals() + { + KTDataHandle dataHandle = CreateNewDataHandle(); + + KTINFO(testsiglog, "Creating data objects"); + dataHandle->Of< KTTestDerived1DataExt >(); + dataHandle->Of< KTTestDerived2DataExt >(); + + KTINFO(testsiglog, "Emitting data-1 signal"); + fDerived1DataSignal( dataHandle ); + + KTINFO(testsiglog, "Emitting data-2 signal"); + fDerived2DataSignal( dataHandle ); + + return; + } + + void KTTestProcessorE::BaseAnalysisFunc(const KTTestBaseData& data) + { + KTINFO(testsiglog, "Data funniness measured to be: <" << data.GetFunniness() << ">"); + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Executables_v1/Validation/KTTestProcessor.hh b/Cpp/Executables_v1/Validation/KTTestProcessor.hh new file mode 100644 index 00000000..d704f9ff --- /dev/null +++ b/Cpp/Executables_v1/Validation/KTTestProcessor.hh @@ -0,0 +1,108 @@ +/* + * KTTestProcessor.hh + * + * Created on: Aug 15, 2012 + * Author: nsoblath + */ + +#ifndef KTTESTPROCESSOR_HH_ +#define KTTESTPROCESSOR_HH_ + +#include "KTProcessor.hh" +#include "KTSlot.hh" + +//using namespace py; + +#include "KTTestData.hh" + +namespace Nymph +{ + + /*! + * A simple test processor that emits a signal with an integer argument + */ + class KTTestProcessorA : public KTProcessor + { + public: + KTTestProcessorA( const std::string& name = "test-proc-a" ); + virtual ~KTTestProcessorA(); + + bool Configure(const scarab::param_node& node); + + void EmitSignals(int); + + private: + KTSignal< int > fTheSignal; + }; + + /*! + * A simple test processor that has two slots with integer arguments + */ + class KTTestProcessorB : public KTProcessor + { + public: + KTTestProcessorB( const std::string& name = "test-proc-b" ); + virtual ~KTTestProcessorB(); + + bool Configure(const scarab::param_node& node); + + private: + KTSlot< int > fSlot1; + }; + + +// class KTTestData; + + /*! + * A simple test processor that has a slot for KTTestData + */ + class KTTestProcessorD : public KTProcessor + { + public: + KTTestProcessorD( const std::string& name = "test-proc-d" ); + virtual ~KTTestProcessorD(); + + bool Configure(const scarab::param_node& node); + + // Creates a KTTestData object, sets the awesomeness to the given value, and emits fSignal + void EmitSignal(bool isAwesome = true); + + void AnalysisFunc(const KTTestData& data); + + private: + KTSlotData< void, KTTestDataExt > fDataSlot; + + KTSignalData fDataSignal; + }; + + +// template< class XDerivedDataType > +// class KTTestPolyDataBase< XDerivedDataType >; +// class KTTestDerived1Data; +// class KTTestDerived2Data; + + /*! + * A simple test processor for testing polymorphic data, signals, and slots + */ + class KTTestProcessorE : public KTProcessor + { + public: + KTTestProcessorE( const std::string& name = "test-proc-d" ); + virtual ~KTTestProcessorE(); + + bool Configure(const scarab::param_node& node); + + void EmitSignals(); + + void BaseAnalysisFunc(const KTTestBaseData& data); + + private: + KTSlotData< void, KTTestDerived1DataExt > fDerived1DataSlot; + KTSlotData< void, KTTestDerived2DataExt > fDerived2DataSlot; + + KTSignalData fDerived1DataSignal; + KTSignalData fDerived2DataSignal; + }; + +} /* namespace Nymph */ +#endif /* KTTESTPROCESSOR_HH_ */ diff --git a/Cpp/Executables_v1/Validation/NymphValidationPybind.cc b/Cpp/Executables_v1/Validation/NymphValidationPybind.cc new file mode 100644 index 00000000..f2811813 --- /dev/null +++ b/Cpp/Executables_v1/Validation/NymphValidationPybind.cc @@ -0,0 +1,20 @@ +/* + * NymphValidationPy.cc + * + * Created on: Jan 25, 2018 + * Author: obla999 + */ + +#include "KTPyTestClassPybind.hh" +#include "TestPythonBasics.hh" +#include "pybind11/pybind11.h" +#include + +PYBIND11_MODULE( py_nymph_validation, mod ) +{ + Nymph::ExportKTPyTestClass( mod ); + Nymph::ExportTestPythonBasics( mod ); + +} + + diff --git a/Executables/Validation/TestApplication.cc b/Cpp/Executables_v1/Validation/TestApplication.cc similarity index 100% rename from Executables/Validation/TestApplication.cc rename to Cpp/Executables_v1/Validation/TestApplication.cc diff --git a/Cpp/Executables_v1/Validation/TestApplyCut.cc b/Cpp/Executables_v1/Validation/TestApplyCut.cc new file mode 100644 index 00000000..8998ac93 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestApplyCut.cc @@ -0,0 +1,71 @@ +/* + * TestApplyCut.cc + * + * Created on: Oct 07, 2014 + * Author: nsoblath + */ + +#include "KTTestCuts.hh" + +#include "KTApplyCut.hh" +#include "KTLogger.hh" +#include "KTTestData.hh" + +KTLOGGER(testlog, "TestApplyCut"); + +using namespace Nymph; +using namespace std; + +int main() +{ + KTDataHandle dataHandle = CreateNewDataHandle(); + KTTestData& testData = dataHandle->Of< KTTestDataExt >(); + + KTCutStatus& cutStatus = dataHandle->CutStatus(); + unsigned awesomeCutPos = 0; + string awesomeCutName("awesome-cut"); + unsigned notAwesomeCutPos = 1; + string notAwesomeCutName("not-awesome-cut"); + cutStatus.AssignCutResult(awesomeCutPos, awesomeCutName); + cutStatus.AssignCutResult(notAwesomeCutPos, notAwesomeCutName); + KTINFO(testlog, "Initial cut state: " << cutStatus.IsCut()); + + KTApplyCut applyCut; + + KTINFO(testlog, "Applying awesome cut"); + applyCut.SetCut(new KTAwesomeCut()); + applyCut.ApplyCut(dataHandle); + + KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << ">is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); + KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); + KTINFO(testlog, "Is cut (with mask \"0\")? " << cutStatus.IsCut("0")); + + KTINFO(testlog, "Applying not-awesome cut"); + applyCut.SelectCut("not-awesome-cut"); + applyCut.ApplyCut(dataHandle); + + KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << "> is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); + KTINFO(testlog, "Has cut result <" << notAwesomeCutName << ">? " << cutStatus.HasCutResult(notAwesomeCutName)); + KTINFO(testlog, "Has cut result at " << notAwesomeCutPos << "? " << cutStatus.HasCutResult(notAwesomeCutPos)); + KTINFO(testlog, "Cut state of <" << notAwesomeCutName << "> is: " << cutStatus.GetCutState(notAwesomeCutName)); + KTINFO(testlog, "Cut state at " << notAwesomeCutPos << " is: " << cutStatus.GetCutState(notAwesomeCutPos)); + KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); + KTINFO(testlog, "Is cut with mask \"00\"? " << cutStatus.IsCut("00")); + KTINFO(testlog, "Is cut with mask \"01\"? " << cutStatus.IsCut("01")); + KTINFO(testlog, "Is cut with mask \"10\"? " << cutStatus.IsCut("10")); + KTINFO(testlog, "Is cut with mask \"11\"? " << cutStatus.IsCut("11")); + KTINFO(testlog, "Is cut with mask 0? " << cutStatus.IsCut(0)); + KTINFO(testlog, "Is cut with mask 1? " << cutStatus.IsCut(1)); + KTINFO(testlog, "Is cut with mask 2? " << cutStatus.IsCut(2)); + KTINFO(testlog, "Is cut with mask 3? " << cutStatus.IsCut(3)); + + return 0; +} diff --git a/Executables/Validation/TestCacheDirectory.cc b/Cpp/Executables_v1/Validation/TestCacheDirectory.cc similarity index 100% rename from Executables/Validation/TestCacheDirectory.cc rename to Cpp/Executables_v1/Validation/TestCacheDirectory.cc diff --git a/Cpp/Executables_v1/Validation/TestCut.cc b/Cpp/Executables_v1/Validation/TestCut.cc new file mode 100644 index 00000000..37b55cd4 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestCut.cc @@ -0,0 +1,81 @@ +/* + * TestCut.cc + * + * Created on: Sep 29, 2014 + * Author: nsoblath + */ + +#include "KTTestCuts.hh" + +#include "KTLogger.hh" +#include "KTTestData.hh" + +KTLOGGER(testlog, "TestCut"); + +using namespace Nymph; +using namespace std; + +int main() +{ + try + { + KTCoreDataExt data; + KTTestDataExt& testData = data.Of< KTTestDataExt >(); + + KTCutStatus& cutStatus = data.CutStatus(); + + KTINFO(testlog, "Assigning awesome-cut"); + unsigned awesomeCutPos = 0; + string awesomeCutName("awesome-cut"); + cutStatus.AssignCutResult(awesomeCutPos, awesomeCutName); + + KTINFO(testlog, "Initial cut state: " << cutStatus.IsCut()); + + KTINFO(testlog, "Applying awesome cut"); + KTAwesomeCut cut(awesomeCutName); + cut.Apply(data, testData); + + KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << ">is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); + KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); + KTINFO(testlog, "Current cut status: " << cutStatus); + KTINFO(testlog, "Is cut (with mask \"0\")? " << cutStatus.IsCut("0")); + + KTINFO(testlog, "Assigning not-awesome-cut"); + unsigned notAwesomeCutPos = 1; + string notAwesomeCutName("not-awesome-cut"); + cutStatus.AssignCutResult(notAwesomeCutPos, notAwesomeCutName); + + KTINFO(testlog, "Applying not-awesome cut"); + KTNotAwesomeCut naCut(notAwesomeCutName); + naCut.Apply(data, testData); + + KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << "> is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); + KTINFO(testlog, "Has cut result <" << notAwesomeCutName << ">? " << cutStatus.HasCutResult(notAwesomeCutName)); + KTINFO(testlog, "Has cut result at " << notAwesomeCutPos << "? " << cutStatus.HasCutResult(notAwesomeCutPos)); + KTINFO(testlog, "Cut state of <" << notAwesomeCutName << "> is: " << cutStatus.GetCutState(notAwesomeCutName)); + KTINFO(testlog, "Cut state at " << notAwesomeCutPos << " is: " << cutStatus.GetCutState(notAwesomeCutPos)); + KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); + KTINFO(testlog, "Is cut with mask \"00\"? " << cutStatus.IsCut("00")); + KTINFO(testlog, "Is cut with mask \"01\"? " << cutStatus.IsCut("01")); + KTINFO(testlog, "Is cut with mask \"10\"? " << cutStatus.IsCut("10")); + KTINFO(testlog, "Is cut with mask \"11\"? " << cutStatus.IsCut("11")); + KTINFO(testlog, "Is cut with mask 0? " << cutStatus.IsCut(0)); + KTINFO(testlog, "Is cut with mask 1? " << cutStatus.IsCut(1)); + KTINFO(testlog, "Is cut with mask 2? " << cutStatus.IsCut(2)); + KTINFO(testlog, "Is cut with mask 3? " << cutStatus.IsCut(3)); + } + catch(std::exception& e) + { + KTERROR(testlog, "Exception caught: " << e.what()); + } + + return 0; +} diff --git a/Executables/Validation/TestCutFilter.cc b/Cpp/Executables_v1/Validation/TestCutFilter.cc similarity index 50% rename from Executables/Validation/TestCutFilter.cc rename to Cpp/Executables_v1/Validation/TestCutFilter.cc index 718a8faf..04a96dfc 100644 --- a/Executables/Validation/TestCutFilter.cc +++ b/Cpp/Executables_v1/Validation/TestCutFilter.cc @@ -10,6 +10,7 @@ #include "KTApplyCut.hh" #include "KTCutFilter.hh" #include "KTLogger.hh" +#include "KTTestData.hh" KTLOGGER(testlog, "TestCutFilter"); @@ -18,42 +19,48 @@ using namespace std; int main() { - KTDataPtr dataPtr(new KTData()); - KTTestData& testData = dataPtr->Of< KTTestData >(); - - KTCutStatus& cutStatus = dataPtr->GetCutStatus(); + KTDataHandle dataHandle = CreateNewDataHandle(); + KTTestData& testData = dataHandle->Of< KTTestDataExt >(); + + KTCutStatus& cutStatus = dataHandle->CutStatus(); + unsigned awesomeCutPos = 0; + string awesomeCutName("awesome-cut"); + unsigned notAwesomeCutPos = 1; + string notAwesomeCutName("not-awesome-cut"); + cutStatus.AssignCutResult(awesomeCutPos, awesomeCutName); + cutStatus.AssignCutResult(notAwesomeCutPos, notAwesomeCutName); KTINFO(testlog, "Initial cut state: " << cutStatus.IsCut()); KTApplyCut applyCut; KTINFO(testlog, "Applying awesome cut"); applyCut.SetCut(new KTAwesomeCut()); - applyCut.ApplyCut(dataPtr); + applyCut.ApplyCut(dataHandle); KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << ">is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); KTINFO(testlog, "Is cut (with mask \"0\")? " << cutStatus.IsCut("0")); KTINFO(testlog, "Applying not-awesome cut"); applyCut.SelectCut("not-awesome-cut"); - applyCut.ApplyCut(dataPtr); + applyCut.ApplyCut(dataHandle); KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); - KTINFO(testlog, "Has cut result \"not-awesome-cut\"? " << cutStatus.HasCutResult("not-awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTNotAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"not-awesome-cut\" is: " << cutStatus.GetCutState("not-awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTNotAwesomeCut::Result >()); + KTINFO(testlog, "Has cut result <" << awesomeCutName << ">? " << cutStatus.HasCutResult(awesomeCutName)); + KTINFO(testlog, "Has cut result at " << awesomeCutPos << "? " << cutStatus.HasCutResult(awesomeCutPos)); + KTINFO(testlog, "Cut state of <" << awesomeCutName << "> is: " << cutStatus.GetCutState(awesomeCutName)); + KTINFO(testlog, "Cut state at " << awesomeCutPos << " is: " << cutStatus.GetCutState(awesomeCutPos)); + KTINFO(testlog, "Has cut result <" << notAwesomeCutName << ">? " << cutStatus.HasCutResult(notAwesomeCutName)); + KTINFO(testlog, "Has cut result at " << notAwesomeCutPos << "? " << cutStatus.HasCutResult(notAwesomeCutPos)); + KTINFO(testlog, "Cut state of <" << notAwesomeCutName << "> is: " << cutStatus.GetCutState(notAwesomeCutName)); + KTINFO(testlog, "Cut state at " << notAwesomeCutPos << " is: " << cutStatus.GetCutState(notAwesomeCutPos)); KTCutFilter cutFilter; - KTData& data = dataPtr->Of< KTData >(); + KTCoreDataExt& data = dataHandle->Of< KTCoreDataExt >(); KTINFO(testlog, "Filtering with all cuts"); cutFilter.SetCutMaskAll(); diff --git a/Cpp/Executables_v1/Validation/TestData.cc b/Cpp/Executables_v1/Validation/TestData.cc new file mode 100644 index 00000000..18ea6bed --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestData.cc @@ -0,0 +1,25 @@ +/* + * TestData.cc + * + * Created on: Feb 10, 2018 + * Author: obla999 + */ + +#include "KTTestData.hh" + +#include "KTLogger.hh" + +using namespace Nymph; + +LOGGER(testlog, "TestData") + +int main() +{ + + KTCoreDataExt data; + KTTestDataExt& testData = data.Of< KTTestDataExt >(); + KTINFO(testlog, data.Name()); + KTINFO(testlog, testData.Name()); + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestExceptionInSlot.cc b/Cpp/Executables_v1/Validation/TestExceptionInSlot.cc new file mode 100644 index 00000000..bc4887ca --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestExceptionInSlot.cc @@ -0,0 +1,93 @@ +/* + * TestExceptionInSlot.cc + * + * Created on: May 4, 2017 + * Author: N. Oblath + * + * Intended outcome: Exception is thrown in first-slot when the first signal (5) is emitted; the second signal should not be emitted. + */ + +#include "KTTestProcessor.hh" + +#include "KTException.hh" +#include "KTLogger.hh" + +#include +#include + +using namespace Nymph; + +KTLOGGER( testexclog, "TestExceptionInSlot" ) + +int main() +{ + + try + { + KTTestProcessorA tpA; + KTTestProcessorC tpC; + + KTINFO( testexclog, "Connecting the-signal to first-slot" ); + tpA.ConnectASlot( "the-signal", &tpC, "first-slot", 20 ); + + // setup to execute processors asynchronously + std::shared_ptr< KTThreadReference > exeThreadRef = std::make_shared< KTThreadReference >(); + tpC.GetSlot( "first-slot" )->SetThreadRef( exeThreadRef ); + + std::atomic< bool > canceled( false ); + + auto exeFunc = [&]() { + try + { + boost::this_thread::sleep_for( boost::chrono::milliseconds(1) ); // delay to let the main thread get to the std::future::wait() call + + KTINFO( testexclog, "Emitting signals" ); + + if( canceled.load() ) return; + KTINFO( testexclog, "First test signal: 5" ); + tpA.EmitSignals( 5 ); + + boost::this_thread::sleep_for( boost::chrono::milliseconds(1) ); // delay to let the main thread react to the exception + + if( canceled.load() ) return; + KTINFO( testexclog, "Second test signal: 18" ); + tpA.EmitSignals( 18 ); + } + catch( boost::exception& e ) + { + exeThreadRef->SetReturnException( boost::current_exception() ); + } + + return; + }; + + // run the thread + boost::thread thread( exeFunc ); + + // wait for a result to be set + exeThreadRef->GetDataPtrRetFuture().wait(); + + try + { + exeThreadRef->GetReturnValue(); + } + catch( boost::exception& e ) + { + canceled = true; + //ADD_ERROR_MSG( e, "Added error message!" ); + e << KTErrorMsgInfo< struct testEIS_GetRetVal >( "Added error message!" ); + KTERROR( testexclog, "An error occurred while running a processor: " << diagnostic_information( e ) ); + } + + thread.join(); + + KTINFO( testexclog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testexclog, "An unexpected exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestExt.cc b/Cpp/Executables_v1/Validation/TestExt.cc new file mode 100644 index 00000000..7dcbdbbd --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestExt.cc @@ -0,0 +1,79 @@ +/* + * TestExt.cc + * + * Created on: Feb 11, 2018 + * Author: N.S. Oblath + */ + +#include "KTLogger.hh" + +#include "KTExtensible.hh" + +KTLOGGER( testlog, "KTExtTest" ); + +using namespace Nymph; + +namespace Nymph +{ + class MyBaseClass + { + public: + MyBaseClass() {} + virtual ~MyBaseClass() {} + }; + + class MyClass1 : public KTExtensible< MyClass1, MyBaseClass > + { + public: + MyClass1() : KTExtensible< MyClass1, MyBaseClass >(), fValue() {} + MyClass1( const MyClass1& orig ) : KTExtensible< MyClass1, MyBaseClass >( orig ), fValue( orig.fValue ) {} + virtual ~MyClass1() {}; + public: + int fValue; + }; + + class MyClass2 : public KTExtensible< MyClass2, MyBaseClass > + { + public: + MyClass2() : KTExtensible< MyClass2, MyBaseClass >(), fValue() {} + MyClass2( const MyClass2& orig ) : KTExtensible< MyClass2, MyBaseClass >( orig ), fValue( orig.fValue ) {} + virtual ~MyClass2() {}; + public: + int fValue; + }; + +} + +int main() +{ + std::shared_ptr< MyClass1 > data1 = std::make_shared< MyClass1 >(); + data1->fValue = 5; + + MyClass2& data2 = data1->Of< MyClass2 >(); + data2.fValue = 3; + + std::shared_ptr< MyClass2 > data2Shared = data1->Share< MyClass2 >(); + + KTINFO( testlog, "data1 has data1: " << data1->Has< MyClass1 >() ); + KTINFO( testlog, "data1 has data2: " << data1->Has< MyClass2 >() ); + KTINFO( testlog, "data2 has data1: " << data2.Has< MyClass1 >() ); + KTINFO( testlog, "data2 has data2: " << data2.Has< MyClass2 >() ); + KTINFO( testlog, "Value of data1: " << data1->fValue ); + KTINFO( testlog, "Value of data2: " << data2.fValue ); + KTINFO( testlog, "Value of data2Shared: " << data2Shared->fValue ); + KTINFO( testlog, "Size of data1: " << data1->size() ); + KTINFO( testlog, "Size of data2: " << data2.size() ); + + std::shared_ptr< MyClass1 > copyOfData1 = std::make_shared< MyClass1 >( *data1 ); + MyClass2& copyOfData2 = copyOfData1->Of< MyClass2 >(); + + KTINFO( testlog, "Value of data1's copy: " << copyOfData1->fValue ); + KTINFO( testlog, "Value of data2's copy: " << copyOfData2.fValue ); + + std::shared_ptr< MyClass2 > detatchedCoypOfData2 = copyOfData1->Detatch< MyClass2 >(); + + KTINFO( testlog, "Size of copy of data1 after detatch: " << copyOfData1->size() ); + KTINFO( testlog, "Size of detatched copy of data2: " << detatchedCoypOfData2->size() ); + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestExtSerialization.cc b/Cpp/Executables_v1/Validation/TestExtSerialization.cc new file mode 100644 index 00000000..ae14d300 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestExtSerialization.cc @@ -0,0 +1,80 @@ +/* + * TestSerialization.cc + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#include "KTLogger.hh" + +#include "KTTestData.hh" + +#include "cereal/archives/json.hpp" + +#include "KTRegisterNymphExtData.hh" + +#include + +LOGGER( testlog, "TestSerialization" ); + +using namespace Nymph; + +// register the data classes so they can be serialized from base-class pointers. +// json archive is already included, so it has been registered before these classes. +CEREAL_REGISTER_TYPE( KTTestDataExt ); + +int main() +{ + const std::string filename( "test_ext_serialization_output.json" ); + + // save data to archive + { + std::shared_ptr< KTCoreDataExt > coreData = std::make_shared< KTCoreDataExt >(); + std::shared_ptr< KTTestDataExt > testData = coreData->Share< KTTestDataExt >(); + std::shared_ptr< KTTestDerived1DataExt > testD1Data = coreData->Share< KTTestDerived1DataExt >(); + + // change data values from their defaults + coreData->SetCounter( 5 ); + testData->SetIsAwesome( true ); + testD1Data->SetFunniness( 924 ); + + KTINFO( testlog, "KTCoreData counter: " << coreData->GetCounter() ); + KTINFO( testlog, "KTTestData is-awesome: " << testData->GetIsAwesome() ); + KTINFO( testlog, "KTTestDerived1Data funniness: " << testD1Data->GetFunniness() ); + + // create and open a file for output + std::ofstream fileOut( filename ); + + cereal::JSONOutputArchive archiveOut( fileOut ); + + KTINFO( testlog, "Writing data to JSON archive" ); + archiveOut( coreData ); + + // archive and stream closed when destructors are called + } + + // ... some time later restore the class instance to its original state + { + std::shared_ptr< KTCoreDataExt > newCoreData = std::make_shared< KTCoreDataExt >(); + + // create and open an archive for input + std::ifstream fileIn( filename ); + + cereal::JSONInputArchive archiveIn( fileIn ); + + KTINFO( testlog, "Reading data from JSON archive" ); + archiveIn( newCoreData ); + + std::shared_ptr< KTTestDataExt > newTestData = newCoreData->Share< KTTestDataExt >(); + std::shared_ptr< KTTestDerived1DataExt > newD1Data = newCoreData->Share< KTTestDerived1DataExt >(); + + KTINFO(testlog, "KTCoreData counter: " << newCoreData->GetCounter()); + KTINFO(testlog, "KTTestData is-awesome: " << newTestData->GetIsAwesome()); + KTINFO( testlog, "KTTestDerived1Data funniness: " << newD1Data->GetFunniness() ); + + // archive and stream closed when destructors are called + } + + return 0; +} + diff --git a/Cpp/Executables_v1/Validation/TestJSONWriter.cc b/Cpp/Executables_v1/Validation/TestJSONWriter.cc new file mode 100644 index 00000000..79f27bde --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestJSONWriter.cc @@ -0,0 +1,52 @@ +/* + * TestSerialWriter.cc + * + * Created on: Feb 1, 2018 + * Author: E Zayas + * +*/ + +#include "../../Library/IO/KTJSONWriter.hh" +#include "KTJSONReader.hh" +#include "KTTestData.hh" + +KTLOGGER( testlog, "TestJSONWriter" ); + +using namespace Nymph; + +int main() +{ + std::string filename( "test_json_writer_output.json" ); + + { + // Create the test data + KTTestData testData; + testData.SetIsAwesome( true ); // because it isn't by default :( + + KTINFO( testlog, "KTTestData is awesome? " << testData.GetIsAwesome() ); + + // Create the writer + KTJSONWriter writer; + writer.SetFilename( filename ); + + KTINFO( testlog, "Writing data" ); + + writer.WriteData< KTTestData >( testData ); + + // destruction of the stream and archive (in the writer's destructor) properly closes the file + } +/* + { + KTTestData newTestData; + + KTJSONReader reader; + reader.SetFilename( filename ); + + reader.ReadData< KTTestData >( newTestData ); + + KTINFO( testlog, "Is the read data awesome? " << newTestData.GetIsAwesome() ); + } +*/ + KTINFO( testlog, "Validation script concluded" ); + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestJSONWriterViaSlot.cc b/Cpp/Executables_v1/Validation/TestJSONWriterViaSlot.cc new file mode 100644 index 00000000..d7575877 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestJSONWriterViaSlot.cc @@ -0,0 +1,84 @@ +/* + * TestJSONWriterViaSlot.cc + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#include "KTJSONTypeWriterValidation.hh" +#include "KTTestDataPrimaryProcessor.hh" +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER( testlog, "TestJSONWriterViaSlot" ) + +int main() +{ + + try + { + KTPROG( testlog, "Testing single-data-object writing" ); + + KTINFO( testlog, "Creating processors" ); + KTTestDataPrimaryProcessor tdpp; + KTJSONWriter jw; + + KTINFO( testlog, "Configuring processors" ); + scarab::param_node jwConfig; + jwConfig.add( "filename", scarab::param_value( "test_jsonwriter_slot_singleobj.json" ) ); + jw.Configure( jwConfig ); + + KTINFO( testlog, "Connecting the-signal to first-slot" ); + tdpp.ConnectASlot( "test-derived-1", &jw, "test-derived-1", 20 ); + + KTINFO( testlog, "Running the primary processor" ) + + if( ! tdpp.Run() ) + { + KTERROR( testlog, "Something went wrong while running the primary processor" ); + return -1; + } + + KTINFO( testlog, "Test complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testlog, "An unexpected exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + try + { + KTPROG( testlog, "Testing extensible-data-object writing" ); + + KTINFO( testlog, "Creating processors" ); + KTTestDataPrimaryProcessor tdpp; + KTJSONWriter jw; + + KTINFO( testlog, "Configuring processors" ); + scarab::param_node jwConfig; + jwConfig.add( "filename", scarab::param_value( "test_jsonwriter_slot_extdata.json" ) ); + jw.Configure( jwConfig ); + + KTINFO( testlog, "Connecting the-signal to first-slot" ); + tdpp.ConnectASlot( "test-derived-1", &jw, "ext-data", 20 ); + + KTINFO( testlog, "Running the primary processor" ) + + if( ! tdpp.Run() ) + { + KTERROR( testlog, "Something went wrong while running the primary processor" ); + return -1; + } + + KTINFO( testlog, "Test complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testlog, "An unexpected exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} diff --git a/Executables/Validation/TestLogger.cc b/Cpp/Executables_v1/Validation/TestLogger.cc similarity index 91% rename from Executables/Validation/TestLogger.cc rename to Cpp/Executables_v1/Validation/TestLogger.cc index 0a2e8719..4190f03e 100644 --- a/Executables/Validation/TestLogger.cc +++ b/Cpp/Executables_v1/Validation/TestLogger.cc @@ -10,14 +10,13 @@ #include #include -using namespace Nymph; - KTLOGGER(logger, "TestLogger") int main() { try { + KTTRACE(logger, "This is a TRACE message from Nymph"); KTDEBUG(logger, "This is a DEBUG message from Nymph"); KTINFO(logger, "This is an INFO message from Nymph"); KTPROG(logger, "This is a PROG message from Nymph"); diff --git a/Cpp/Executables_v1/Validation/TestParamBindings.py b/Cpp/Executables_v1/Validation/TestParamBindings.py new file mode 100644 index 00000000..5608755d --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestParamBindings.py @@ -0,0 +1,51 @@ +# Demonstrates access to param classes + +import py_nymph as nymph + +# want to instantiate this will enough values to test all types: +# # TypeError: get_value(): incompatible function arguments. The following argument types are supported: +# 1. (self: py_nymph.ParamNodePybind, arg0: str) -> bool +# 2. (self: py_nymph.ParamNodePybind, arg0: str) -> int +# 3. (self: py_nymph.ParamNodePybind, arg0: str) -> int +# 4. (self: py_nymph.ParamNodePybind, arg0: str) -> float +# 5. (self: py_nymph.ParamNodePybind, arg0: str) -> str +# 6. (self: py_nymph.ParamNodePybind, arg0: str, arg1: bool) -> bool +# 7. (self: py_nymph.ParamNodePybind, arg0: str, arg1: int) -> int +# 8. (self: py_nymph.ParamNodePybind, arg0: str, arg1: int) -> int +# 9. (self: py_nymph.ParamNodePybind, arg0: str, arg1: float) -> float +# 10. (self: py_nymph.ParamNodePybind, arg0: str, arg1: str) -> str + + +# Value testing +int_value = nymph.param_value(5) +print("What type of value is stored? (bool, uint, int, double, string): ", int_value.is_bool(), int_value.is_uint(), int_value.is_int(), int_value.is_double(), int_value.is_string()) +print("Get value set to 5:", int_value.as_int()) + +string_value = nymph.param_value("Hello!") +print("What type of value is stored? (bool, uint, int, double, string): ", string_value.is_bool(), string_value.is_uint(), string_value.is_int(), string_value.is_double(), string_value.is_string()) +print("Get value set to \"Hello!\":", string_value.as_string()) + + +# Node testing +a_node = nymph.param_node() +a_node.add("int-value", int_value) +a_node.add("string-value", string_value) + +print("Int value from node:", a_node.get_value_int("int-value")) +print("String value from node:", a_node.get_value_string("string-value")) + +# Test the "get_value" function for all the different types: +print("Get int value from the node as different types (bool, uint, int, double, string): ", a_node.get_value_bool("int-value"), a_node.get_value_uint("int-value"), a_node.get_value_int("int-value"), a_node.get_value_double("int-value"), a_node.get_value_string("int-value")) + + +# Array testing +an_array = nymph.param_array() +an_array.resize(2) +an_array.assign(0, int_value) +an_array.assign(1, string_value) + +print("Int value from array:", an_array.get_value_int(0)) +print("String value from array:", an_array.get_value_string(1)) + +# Test the "get_value" function for all the different types: +print("Get int value from the array as different types (bool, uint, int, double, string): ", an_array.get_value_bool(0), an_array.get_value_uint(0), an_array.get_value_int(0), an_array.get_value_double(0), an_array.get_value_string(0)) diff --git a/Cpp/Executables_v1/Validation/TestPolymorphicSlotData.cc b/Cpp/Executables_v1/Validation/TestPolymorphicSlotData.cc new file mode 100644 index 00000000..76b03e8a --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPolymorphicSlotData.cc @@ -0,0 +1,41 @@ +/* + * TestSlotData.cc + * + * Created on: Jun 1, 2017 + * Author: obla999 + */ + + +#include "KTTestData.hh" +#include "KTTestProcessor.hh" +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER(testslotdata, "TestPolymorphicSlotData") + +int main() +{ + try + { + KTTestProcessorE tpE; + + KTINFO(testslotdata, "Connecting derived-1 to derived-1 and derived-2 to derived-2"); + // for simplicity the signal and slot are in the same processor, but that need not be, of course + tpE.ConnectASlot("derived-1", &tpE, "derived-1" ); + tpE.ConnectASlot("derived-2", &tpE, "derived-2" ); + + KTINFO(testslotdata, "Emitting signals"); + tpE.EmitSignals(); + + KTINFO(testslotdata, "Tests complete"); + } + catch( boost::exception& e ) + { + KTERROR( testslotdata, "An exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} + diff --git a/Cpp/Executables_v1/Validation/TestPrimaryProcessor.cc b/Cpp/Executables_v1/Validation/TestPrimaryProcessor.cc new file mode 100644 index 00000000..0fc9499d --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPrimaryProcessor.cc @@ -0,0 +1,72 @@ +/* + * TestPrimaryProcessor.cc + * + * Created on: May 4, 2017 + * Author: N.S. Oblath + */ + +#include "KTTestPrimaryProcessor.hh" +#include "KTTestProcessor.hh" + +#include "KTLogger.hh" + +#include + +using namespace Nymph; + +KTLOGGER( testpplog, "TestPrimaryProcessor" ) + +int main() +{ + + try + { + KTTestPrimaryProcessor tpp; + KTTestProcessorB tp; + + KTINFO( testpplog, "Connecting the-signal to first-slot" ); + tpp.ConnectASlot( "the-signal", &tp, "first-slot", 20 ); + + // setup to execute processors asynchronously + std::shared_ptr< KTThreadReference > exeThreadRef( std::make_shared< KTThreadReference >() ); + exeThreadRef->Name() = std::string( "tpp" ); + + // run the thread + boost::condition_variable threadStartedCV; + boost::mutex threadStartedMutex; + bool threadStartedFlag = false; + + boost::unique_lock< boost::mutex > threadStartedLock( threadStartedMutex ); + boost::thread thread( [&](){ tpp( exeThreadRef, threadStartedCV, threadStartedFlag ); } ); + + while( ! threadStartedFlag ) + { + threadStartedCV.wait( threadStartedLock ); + } + KTDEBUG( testpplog, "Thread has started" ); + + // wait for a result to be set + exeThreadRef->GetDataPtrRetFuture().wait(); + + try + { + exeThreadRef->GetReturnValue(); + } + catch( boost::exception& e ) + { + exeThreadRef->SetCanceled( true ); + KTERROR( testpplog, "An error occurred while running a processor: " << diagnostic_information( e ) ); + } + + thread.join(); + + KTINFO( testpplog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testpplog, "Exception caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestPrimaryProcessorNoThreading.cc b/Cpp/Executables_v1/Validation/TestPrimaryProcessorNoThreading.cc new file mode 100644 index 00000000..4ee12dee --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPrimaryProcessorNoThreading.cc @@ -0,0 +1,45 @@ +/* + * TestPrimaryProcessorNoThreading.cc + * + * Created on: May 5, 2017 + * Author: N.S. Oblath + */ + +#include "KTTestPrimaryProcessor.hh" +#include "KTTestProcessor.hh" + +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER( testpplog, "TestPrimaryProcessorNoThreading" ) + +int main() +{ + + try + { + KTTestPrimaryProcessor tpp; + KTTestProcessorB tp; + + KTINFO( testpplog, "Connecting the-signal to first-slot" ); + tpp.ConnectASlot( "the-signal", &tp, "first-slot", 20 ); + + KTINFO( testpplog, "Running the primary processor" ) + + if( ! tpp.Run() ) + { + KTERROR( testpplog, "Something went wrong while running the primary processor" ); + return -1; + } + + KTINFO( testpplog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testpplog, "An unexpected exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} diff --git a/Executables/Validation/TestPrintData.cc b/Cpp/Executables_v1/Validation/TestPrintData.cc similarity index 70% rename from Executables/Validation/TestPrintData.cc rename to Cpp/Executables_v1/Validation/TestPrintData.cc index 3b77cd21..f22bf196 100644 --- a/Executables/Validation/TestPrintData.cc +++ b/Cpp/Executables_v1/Validation/TestPrintData.cc @@ -17,9 +17,9 @@ using namespace std; int main() { - KTDataPtr dataPtr(new KTData()); - KTData& data = dataPtr->Of< KTData >(); - KTTestData& testData = dataPtr->Of< KTTestData >(); + KTDataHandle dataHandle = CreateNewDataHandle(); + KTCoreDataExt& data = dataHandle->Of< KTCoreDataExt >(); + KTTestDataExt& testData = dataHandle->Of< KTTestDataExt >(); KTINFO(testlog, "Applying awesome cut"); KTAwesomeCut cut; @@ -32,10 +32,10 @@ int main() KTPrintDataStructure printer; KTINFO(testlog, "Printing data structure"); - printer.PrintDataStructure(dataPtr); + printer.PrintDataStructure(dataHandle); KTINFO(testlog, "Printing cut structure"); - printer.PrintCutStructure(dataPtr); + printer.PrintCutStructure(dataHandle); return 0; } diff --git a/Cpp/Executables_v1/Validation/TestProcessorToolbox.py b/Cpp/Executables_v1/Validation/TestProcessorToolbox.py new file mode 100644 index 00000000..8453a798 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestProcessorToolbox.py @@ -0,0 +1,27 @@ +# TestProcessorToolbox.py +# Created on: Jan 29, 2018 +# Author: N.S. Oblath +# +# Tests the ability to use the processor toolbox from a Python script + +import py_nymph as nymph +import py_nymph_validation as nv + + +ptb = nymph.KTProcessorToolbox('pt') + +print('Configuring') + +ptb.AddProcessor('test-p-proc', 'tpp') +ptb.AddProcessor('test-proc-b', 'tp') +ptb.MakeConnection('tpp:the-signal', 'tp:first-slot') +ptb.MakeConnection('tpp:the-signal', 'tp:first-slot') + + +ptb.PushBackToRunQueue('tpp') + +print('Running') + +ptb.Run() + +print('All done') diff --git a/Cpp/Executables_v1/Validation/TestProcessorToolbox2Threads.cc b/Cpp/Executables_v1/Validation/TestProcessorToolbox2Threads.cc new file mode 100644 index 00000000..65cb6ddc --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestProcessorToolbox2Threads.cc @@ -0,0 +1,114 @@ +/* + * TestProcessorToolboxMultithreaded.cc + * + * Created on: May 8, 2017 + * Author: N.S. Oblath + */ + +#include "KTProcessorToolbox.hh" + +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER( testptlog, "TestProcessorToolbox" ) + +int main() +{ + try + { + KTINFO( testptlog, "Preparing to run" ); + + KTProcessorToolbox ptb; + + // add the primary processor, thread 1 + if( ! ptb.AddProcessor( "test-p-proc", "tpp1" ) ) + { + KTERROR( testptlog, "Unable to create test primary processor (thread 1)" ); + return -1; + } + + // add processor b, thread 1 + if( ! ptb.AddProcessor( "test-proc-b", "tp1" ) ) + { + KTERROR( testptlog, "Unable to create test processor b (thread 1)" ); + return -1; + } + + // add the primary processor, thread 2 + if( ! ptb.AddProcessor( "test-p-proc", "tpp2" ) ) + { + KTERROR( testptlog, "Unable to create test primary processor (thread 2)" ); + return -1; + } + + // add processor b, thread 2 + if( ! ptb.AddProcessor( "test-proc-b", "tp2" ) ) + { + KTERROR( testptlog, "Unable to create test processor b (thread 2)" ); + return -1; + } + + // set breakpoint, thread 1 + if( ! ptb.SetBreakpoint( "tp1", "first-slot" ) ) + { + KTERROR( testptlog, "Unable to set the breakpoint" ); + return -1; + } + + // no breakpoint, thread2 + + // make connections, thread 1 + if( ! ptb.MakeConnection( "tpp1:the-signal", "tp1:first-slot" ) ) + { + KTERROR( testptlog, "Unable to connect tpp1 to tp1" ); + return -1; + } + + // make connections, thread 2 + if( ! ptb.MakeConnection( "tpp2:the-signal", "tp2:second-slot" ) ) + { + KTERROR( testptlog, "Unable to connect tpp2 to tp2" ); + return -1; + } + + // set the run queue + if( ! ptb.PushBackToRunQueue( {"tpp1", "tpp2"} ) ) + { + KTERROR( testptlog, "Unable to add {tpp1, tpp2} to the run queue" ); + return -1; + } + + // go! + KTINFO( testptlog, "Starting asynchronous run" ); + ptb.AsyncRun(); + + KTINFO( testptlog, "Starting asynchronous breakpoint user" ); + auto buFuture = boost::async( boost::launch::async, [&](){ + KTINFO( testptlog, "In breakpoint user thread" ); + while( ptb.WaitForBreak() ) + { + KTPROG( testptlog, "Reached breakpoint; waiting for user input" ); + std::string temp; + KTPROG( testptlog, "Please press [return]" ); + getline( std::cin, temp ); + ptb.Continue(); + } + KTINFO( testptlog, "Finished breakpoint user thread" ); + return; + }); + + KTINFO( testptlog, "Waiting for the end of the run" ); + ptb.WaitForEndOfRun(); + + KTINFO( testptlog, "Processing complete" ); + + KTINFO( testptlog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testptlog, "Exception caught: " << diagnostic_information( e ) ); + return -1; + } + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestProcessorToolboxMultithreaded.cc b/Cpp/Executables_v1/Validation/TestProcessorToolboxMultithreaded.cc new file mode 100644 index 00000000..de583b98 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestProcessorToolboxMultithreaded.cc @@ -0,0 +1,76 @@ +/* + * TestProcessorToolboxMultithreaded.cc + * + * Created on: May 8, 2017 + * Author: N.S. Oblath + */ + +#include "KTProcessorToolbox.hh" + +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER( testptlog, "TestProcessorToolboxMultithreaded" ) + +int main() +{ + try + { + KTINFO( testptlog, "Preparing to run" ); + + KTProcessorToolbox ptb; + + // set for multi-threaded running + ptb.SetRunSingleThreaded( false ); + + // add the primary processor + if( ! ptb.AddProcessor( "test-p-proc", "tpp" ) ) + { + KTERROR( testptlog, "Unable to create test primary processor" ); + return -1; + } + + // add processor b + if( ! ptb.AddProcessor( "test-proc-b", "tp" ) ) + { + KTERROR( testptlog, "Unable to create test processor b" ); + return -1; + } + + // make connections + if( ! ptb.MakeConnection( "tpp:the-signal", "tp:first-slot" ) ) + { + KTERROR( testptlog, "Unable to connect tpp to tp" ); + return -1; + } + + // set the run queue + if( ! ptb.PushBackToRunQueue( "tpp" ) ) + { + KTERROR( testptlog, "Unable to add tpp to the run queue" ); + return -1; + } + + // go! + KTINFO( testptlog, "Starting asynchronous run" ); + ptb.AsyncRun(); + + // wait for run completion, and ignore breakpoints + while( ptb.WaitForBreak() ) + { + KTINFO( testptlog, "Reached breakpoint; continuing" ); + ptb.Continue(); + } + + KTINFO( testptlog, "Processing complete" ); + + KTINFO( testptlog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testptlog, "Exception caught: " << diagnostic_information( e ) ); + return -1; + } + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestProcessorToolboxSinglethreaded.cc b/Cpp/Executables_v1/Validation/TestProcessorToolboxSinglethreaded.cc new file mode 100644 index 00000000..5e91926d --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestProcessorToolboxSinglethreaded.cc @@ -0,0 +1,77 @@ +/* + * TestProcessorToolboxSinglethreaded.cc + * + * Created on: May 4, 2017 + * Author: N.S. Oblath + */ + +#include "KTProcessorToolbox.hh" + +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER( testptlog, "TestProcessorToolboxSinglethreaded" ) + +int main() +{ + try + { + KTINFO( testptlog, "Preparing to run" ); + + KTProcessorToolbox ptb; + + // set for single-threaded running + ptb.SetRunSingleThreaded( true ); + + // add the primary processor + if( ! ptb.AddProcessor( "test-p-proc", "tpp" ) ) + { + KTERROR( testptlog, "Unable to create test primary processor" ); + return -1; + } + + // add processor b + if( ! ptb.AddProcessor( "test-proc-b", "tp" ) ) + { + KTERROR( testptlog, "Unable to create test processor b" ); + return -1; + } + + // make connections + if( ! ptb.MakeConnection( "tpp:the-signal", "tp:first-slot" ) ) + { + KTERROR( testptlog, "Unable to connect tpp to tp" ); + return -1; + } + + // set the run queue + if( ! ptb.PushBackToRunQueue( "tpp" ) ) + { + KTERROR( testptlog, "Unable to add tpp to the run queue" ); + return -1; + } + + // go! + KTINFO( testptlog, "Starting asynchronous run" ); + ptb.AsyncRun(); + + // wait for run completion, and ignore breakpoints + while( ptb.WaitForBreak() ) + { + KTINFO( testptlog, "Reached breakpoint; continuing" ); + ptb.Continue(); + } + + KTINFO( testptlog, "Processing complete" ); + + KTINFO( testptlog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testptlog, "Exception caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestPyTestClass.py b/Cpp/Executables_v1/Validation/TestPyTestClass.py new file mode 100644 index 00000000..1db02646 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPyTestClass.py @@ -0,0 +1,13 @@ +# TestPyTestClass.py +# Created on: Feb 1, 2018 +# Author: C. Claessens +# +# Demonstrates access to KTPyTestClass + +import py_nymph_validation as nv + +ptc = nv.KTPyTestClass() + +print( 'Ok, KTPyTestClass, say hello . . .' ) +print( ptc.SayHello() ) +print( 'This should print the default value of KTPyTestClass::fValue, which is 5:', ptc.GetValue() ) diff --git a/Cpp/Executables_v1/Validation/TestPythonBasics.hh b/Cpp/Executables_v1/Validation/TestPythonBasics.hh new file mode 100644 index 00000000..f1f6ef0b --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPythonBasics.hh @@ -0,0 +1,26 @@ +/* + * TestPythonBasics.hh + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#include "pybind11/pybind11.h" + + +namespace Nymph +{ + namespace PyTest + { + int add( int i, int j ) + { + return i + j; + } + } + + void ExportTestPythonBasics( pybind11::module& mod ) + { + mod.def( "add", &PyTest::add, "A function that adds two integers" ); + } + +} diff --git a/Cpp/Executables_v1/Validation/TestPythonBasics.py b/Cpp/Executables_v1/Validation/TestPythonBasics.py new file mode 100644 index 00000000..0babf31b --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestPythonBasics.py @@ -0,0 +1,10 @@ +# TestPythonBasics.py +# Created on: Jan 29, 2018 +# Author: N.S. Oblath +# +# Verifies that we can import Nymph python modules and call wrapped functions. +# Uses Nymph::PyTest::add(), which is defined in TestPythonBasics.cc + +import py_nymph_validation + +print( '1 + 2 =', py_nymph_validation.add(1, 2) ) diff --git a/Cpp/Executables_v1/Validation/TestSerialization.cc b/Cpp/Executables_v1/Validation/TestSerialization.cc new file mode 100644 index 00000000..ab97d7b1 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestSerialization.cc @@ -0,0 +1,73 @@ +/* + * TestSerialization.cc + * + * Created on: Jan 24, 2018 + * Author: obla999 + */ + +#include "KTLogger.hh" + +#include "KTTestData.hh" + +#include "cereal/archives/json.hpp" + +#include + +LOGGER( testlog, "TestSerialization" ); + +using namespace Nymph; + +int main() +{ + const std::string filename( "test_serialization_output.json" ); + + // save data to archive + { + KTCoreData coreData; + KTTestData testData; + KTTestBaseData testBaseData; + + // change data values from their defaults + coreData.SetCounter( 5 ); + testData.SetIsAwesome( true ); + testBaseData.SetFunniness( 923 ); + + KTINFO( testlog, "KTCoreData counter: " << coreData.GetCounter() ); + KTINFO( testlog, "KTTestData is-awesome: " << testData.GetIsAwesome() ); + KTINFO( testlog, "KTTestBaseData funniness: " << testBaseData.GetFunniness() ); + + // create and open a file for output + std::ofstream fileOut( filename ); + + cereal::JSONOutputArchive archiveOut( fileOut ); + + KTINFO( testlog, "Writing data to JSON archive" ); + archiveOut( CEREAL_NVP(coreData), CEREAL_NVP(testData), CEREAL_NVP(testBaseData) ); + + // archive and stream closed when destructors are called + } + + // ... some time later restore the class instance to its original state + { + KTCoreData newCoreData; + KTTestData newTestData; + KTTestBaseData newTestBaseData; + + // create and open an archive for input + std::ifstream fileIn( filename ); + + cereal::JSONInputArchive archiveIn( fileIn ); + + KTINFO( testlog, "Reading data from JSON archive" ); + archiveIn( newCoreData, newTestData, newTestBaseData ); + + KTINFO( testlog, "KTCoreData counter: " << newCoreData.GetCounter() ); + KTINFO( testlog, "KTTestData is-awesome: " << newTestData.GetIsAwesome() ); + KTINFO( testlog, "KTTestBaseData funniness: " << newTestBaseData.GetFunniness() ); + + // archive and stream closed when destructors are called + } + + return 0; +} + diff --git a/Cpp/Executables_v1/Validation/TestSignalsAndSlots.cc b/Cpp/Executables_v1/Validation/TestSignalsAndSlots.cc new file mode 100644 index 00000000..6d11dd2d --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestSignalsAndSlots.cc @@ -0,0 +1,58 @@ +/* + * TestSignalsAndSlots.cc + * + * Created on: Aug 15, 2012 + * Author: nsoblath + */ + + +#include "KTTestProcessor.hh" +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER(testsiglog, "TestSignalsAndSlots") + +int main() +{ + try + { + KTTestProcessorA tpA; + KTTestProcessorB tpB; + + /* Basic Test + KTSignalWrapper* signalPtr = tpA.GetSignal("the_signal"); + KTSlotWrapper* slot1Ptr = tpB.GetSlot("first_slot"); + + slot1Ptr->SetConnection(signalPtr); + + tpA.EmitSignals(5); + + slot1Ptr->Disconnect(); + + return 0; + */ + + /* More Complicated Test */ + KTINFO(testsiglog, "Connecting the-signal to first-slot and second-slot"); + tpA.ConnectASlot("the-signal", &tpB, "first-slot", 20); + tpB.ConnectASignal(&tpA, "the-signal", "second-slot", 10); + + KTINFO(testsiglog, "Emitting signals"); + KTINFO(testsiglog, "First test signal: 5"); + tpA.EmitSignals(5); + KTINFO(testsiglog, "Second test signal: 18"); + tpA.EmitSignals(18); + + KTINFO(testsiglog, "Tests complete"); + } + catch( boost::exception& e ) + { + KTERROR( testsiglog, "An exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; + /**/ +} + diff --git a/Cpp/Executables_v1/Validation/TestSlotData.cc b/Cpp/Executables_v1/Validation/TestSlotData.cc new file mode 100644 index 00000000..52853d85 --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestSlotData.cc @@ -0,0 +1,43 @@ +/* + * TestSlotData.cc + * + * Created on: Jun 1, 2017 + * Author: obla999 + */ + + +#include "KTTestData.hh" +#include "KTTestProcessor.hh" +#include "KTLogger.hh" + +using namespace Nymph; + +KTLOGGER(testslotdata, "TestSlotData") + +int main() +{ + try + { + KTTestProcessorD tpD; + + KTINFO(testslotdata, "Connecting test-data-signal to test-data-slot"); + // for simplicity the signal and slot are in the same processor, but that need not be, of course + tpD.ConnectASlot("test", &tpD, "test" ); + + KTINFO(testslotdata, "Emitting signals"); + KTINFO(testslotdata, "First test signal: true"); + tpD.EmitSignal(true); + KTINFO(testslotdata, "Second test signal: false"); + tpD.EmitSignal(false); + + KTINFO(testslotdata, "Tests complete"); + } + catch( boost::exception& e ) + { + KTERROR( testslotdata, "An exception was caught: " << diagnostic_information( e ) ); + return -1; + } + + return 0; +} + diff --git a/Executables/Validation/TestThroughputProfiler.cc b/Cpp/Executables_v1/Validation/TestThroughputProfiler.cc similarity index 100% rename from Executables/Validation/TestThroughputProfiler.cc rename to Cpp/Executables_v1/Validation/TestThroughputProfiler.cc diff --git a/Cpp/Executables_v1/Validation/TestUseBreakpoint.cc b/Cpp/Executables_v1/Validation/TestUseBreakpoint.cc new file mode 100644 index 00000000..e9f76f4e --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestUseBreakpoint.cc @@ -0,0 +1,93 @@ +/* + * TestUseBreakpoint.cc + * + * Created on: May 5, 2017 + * Author: N.S. Oblath + */ + +#include "KTProcessorToolbox.hh" + +#include "KTLogger.hh" + + +using namespace Nymph; + +KTLOGGER( testptlog, "TestUseBreakpoint" ) + +int main() +{ + try + { + KTINFO( testptlog, "Preparing to run" ); + + KTProcessorToolbox ptb; + + // add the primary processor + if( ! ptb.AddProcessor( "test-p-proc", "tpp" ) ) + { + KTERROR( testptlog, "Unable to create test primary processor" ); + return -1; + } + + // add processor b + if( ! ptb.AddProcessor( "test-proc-b", "tp" ) ) + { + KTERROR( testptlog, "Unable to create test processor b" ); + return -1; + } + // set breakpoint + if( ! ptb.SetBreakpoint( "tp", "first-slot" ) ) + { + KTERROR( testptlog, "Unable to set the breakpoint" ); + return -1; + } + + // make connections + if( ! ptb.MakeConnection( "tpp:the-signal", "tp:first-slot" ) ) + { + KTERROR( testptlog, "Unable to connect tpp to tp" ); + return -1; + } + + // set the run queue + if( ! ptb.PushBackToRunQueue( "tpp" ) ) + { + KTERROR( testptlog, "Unable to add tpp to the run queue" ); + return -1; + } + + // go! + KTINFO( testptlog, "Starting asynchronous run" ); + ptb.AsyncRun(); + + KTINFO( testptlog, "Starting asynchronous breakpoint user" ); + auto buFuture = boost::async( boost::launch::async, [&](){ + KTINFO( testptlog, "In breakpoint user thread" ); + while( ptb.WaitForBreak() ) + { + KTPROG( testptlog, "Reached breakpoint; waiting for user input" ); + std::string temp; + KTPROG( testptlog, "Please press [return]" ); + getline( std::cin, temp ); + ptb.Continue(); + } + KTINFO( testptlog, "Finished breakpoint user thread" ); + return; + }); + + KTINFO( testptlog, "Waiting for the end of the run" ); + ptb.WaitForEndOfRun(); + + KTINFO( testptlog, "Processing complete" ); + + ptb.JoinRunThread(); + + KTINFO( testptlog, "Tests complete" ); + } + catch( boost::exception& e ) + { + KTERROR( testptlog, "Caught exception: " << diagnostic_information( e ) ); + } + + return 0; +} diff --git a/Cpp/Executables_v1/Validation/TestWrapProcessor.py b/Cpp/Executables_v1/Validation/TestWrapProcessor.py new file mode 100644 index 00000000..75337adf --- /dev/null +++ b/Cpp/Executables_v1/Validation/TestWrapProcessor.py @@ -0,0 +1,51 @@ +# TestWrapProcessor.py +# Created on: Feb 1, 2018 +# Author: C. Claessens +# +# Tests the ability to overwrite processor functions + +import py_nymph as nymph +import py_nymph_validation as nv + +class WrappedProcessor(nymph.WrapProcessor): + pass + +print("\nMaking instace in python of class WrappedProcessor that inherits from nymph.WrapProcessor.\n") +p = WrappedProcessor() + + +ptb = nymph.KTProcessorToolbox('pt') + +print('Configuring') +ptb.AddProcessor('test-p-proc', 'tpp') +ptb.AddProcessor('bwp', p) +ptb.MakeConnection('tpp:the-signal', 'bwp:wrap-slot') + +ptb.PushBackToRunQueue('tpp') + + +print("\nBefore running: Overwrite WrapFunction that will be called from the slot function.\n") +p.WrapFunction = lambda x: print("Hey, I'm a python processor. And by the way: fThreshold is {}".format(p.fThreshold)) + + +print("Add a new variable to the processor.\n") +p.fThreshold = 6 +print('Property fThreshold from python processor is {}.\n'.format(p.fThreshold)) + + +print('Define new Configure method to set self.fThreshold.\n') +def Configure(self, a): + print('In Configure: Setting new fThreshold.\n') + self.fThreshold = a + +p.Configure = Configure +p.Configure(p, 7) + + + + +print('Running') + +ptb.Run() + +print('All done') \ No newline at end of file diff --git a/Cpp/Library/CMakeLists.txt b/Cpp/Library/CMakeLists.txt new file mode 100644 index 00000000..5294b540 --- /dev/null +++ b/Cpp/Library/CMakeLists.txt @@ -0,0 +1,91 @@ +# CMakeLists for Nymph/Cpp/Library +# Author: N.S. Oblath + +set( CONT_DIR Control ) +set( DATA_DIR Data ) +set( IMPL_DIR Implementation ) +#set( IO_DIR IO ) +set( PROC_DIR Processor ) +set( SVC_DIR Service ) +set( UTIL_DIR Utility ) + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR}/${CONT_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${DATA_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${IMPL_DIR} +# ${CMAKE_CURRENT_SOURCE_DIR}/${IO_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${PROC_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${SVC_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${UTIL_DIR} +) + +set( NYMPH_HEADERFILES + ${UTIL_DIR}/ConfigException.hh + ${UTIL_DIR}/Exception.hh + ${UTIL_DIR}/MemberVariable.hh + + ${SVC_DIR}/Service.hh + ${SVC_DIR}/ServiceToolbox.hh + + ${PROC_DIR}/HelloWorldProc.hh + ${PROC_DIR}/PrimaryProcessor.hh + ${PROC_DIR}/Processor.hh + ${PROC_DIR}/ProcessorRegistrar.hh + ${PROC_DIR}/ProcessorToolbox.hh + ${PROC_DIR}/QuitChain.hh + ${PROC_DIR}/Signal.hh + ${PROC_DIR}/SignalSlotBase.hh + ${PROC_DIR}/Slot.hh + + ${DATA_DIR}/Data.hh + ${DATA_DIR}/DataFrame.hh + ${DATA_DIR}/SignalData.hh + ${DATA_DIR}/SlotData.hh + + ${CONT_DIR}/ControlAccess.hh + ${CONT_DIR}/Controller.hh + ${CONT_DIR}/ReturnBuffer.hh + ${CONT_DIR}/SingleRunController.hh + + ${IMPL_DIR}/RunNymph.hh +) + +set( NYMPH_SOURCEFILES + ${UTIL_DIR}/ConfigException.cc + ${UTIL_DIR}/Exception.cc + + ${SVC_DIR}/Service.cc + ${SVC_DIR}/ServiceToolbox.cc + + ${PROC_DIR}/HelloWorldProc.cc + ${PROC_DIR}/PrimaryProcessor.cc + ${PROC_DIR}/Processor.cc + ${PROC_DIR}/ProcessorToolbox.cc + ${PROC_DIR}/SignalSlotBase.cc + + ${DATA_DIR}/Data.cc + ${DATA_DIR}/DataFrame.cc + + ${CONT_DIR}/ControlAccess.cc + ${CONT_DIR}/Controller.cc + ${CONT_DIR}/ReturnBuffer.cc + ${CONT_DIR}/SingleRunController.cc + + ${IMPL_DIR}/RunNymph.cc +) + + +################################################## + +pbuilder_library( + TARGET Nymph + SOURCES ${NYMPH_SOURCEFILES} + PUBLIC_EXTERNAL_LIBRARIES ${PUBLIC_EXT_LIBS} +) + +pbuilder_component_install_and_export( + COMPONENT Library + LIBTARGETS Nymph +) + +pbuilder_install_headers( ${NYMPH_HEADERFILES} ) diff --git a/Cpp/Library/Control/ControlAccess.cc b/Cpp/Library/Control/ControlAccess.cc new file mode 100644 index 00000000..23267354 --- /dev/null +++ b/Cpp/Library/Control/ControlAccess.cc @@ -0,0 +1,101 @@ +/* + * ControlAccess.cc + * + * Created on: Sept 15, 2019 + * Author: N.S. Oblath + */ + +#include "ControlAccess.hh" + +#include "logger.hh" + +LOGGER( contlog, "ControlAccess" ); + +namespace Nymph +{ + ControlAccess::ControlAccess() : + scarab::singleton< ControlAccess >(), + fControl( nullptr ) + { + + } + + ControlAccess::~ControlAccess() + {} + + bool ControlAccess::WaitToContinue() + { + if( fControl ) return fControl->WaitToContinue(); + THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + bool ControlAccess::WaitForBreakOrCanceled() + { + if( fControl ) return fControl->WaitForBreakOrCanceled(); + THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + void ControlAccess::WaitForEndOfRun() + { + if( fControl ) fControl->WaitForEndOfRun(); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + return; + } + + void ControlAccess::Continue() + { + if( fControl ) fControl->Continue(); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + return; + } + + void ControlAccess::Cancel( int code ) + { + if( fControl ) fControl->Cancel( code ); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + return; + } + + bool ControlAccess::IsCanceled() const + { + if( fControl ) return fControl->IsCanceled(); + THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + void ControlAccess::Break() + { + if( fControl ) fControl->Break(); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + return; + } + + bool ControlAccess::IsAtBreak() const + { + if( fControl ) return fControl->IsAtBreak(); + THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + bool ControlAccess::HasReturn() const + { + if( fControl ) return fControl->HasReturn(); + THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + void ControlAccess::ChainIsQuitting( const std::string& name, std::exception_ptr ePtr ) + { + if( fControl ) fControl->ChainIsQuitting( name, ePtr ); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + return; + } + + +/* + ReturnAccess::ReturnAccess() : + fReturn( new ReturnBuffer< int >() )//, + //fControl() + {} + + ReturnAccess::~ReturnAccess() + {} +*/ +} /* namespace Nymph */ diff --git a/Cpp/Library/Control/ControlAccess.hh b/Cpp/Library/Control/ControlAccess.hh new file mode 100644 index 00000000..a3c63872 --- /dev/null +++ b/Cpp/Library/Control/ControlAccess.hh @@ -0,0 +1,146 @@ +/* + * ControlAccess.hh + * + * Created on: Sept 14, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_CONTROLACCESS_HH_ +#define NYMPH_CONTROLACCESS_HH_ + +#include "Controller.hh" + +#include "Exception.hh" +#include "MemberVariable.hh" +#include "ReturnBuffer.hh" + +#include "singleton.hh" + +namespace Nymph +{ + + /*! + @class ControlAccess + @author N. S. Oblath + + @brief Global access to primary control functionality + + @details + This is a singleton class that allows access to the active Controller from anywhere in a Nymph-based application. + + Specific functionatity includes: + * Initiating a break and (optionally) returning parameters + * Continuing from a break + * Canceling operations + * Waiting for a break/continuation/cancelation + * Notifying the Controller that a Processor chain is quitting + + */ + class ControlAccess : public scarab::singleton< ControlAccess > + { + protected: + allow_singleton_access( ControlAccess ); + + ControlAccess(); + virtual ~ControlAccess(); + + public: + virtual bool WaitToContinue(); + + /// Returns when processing is completed or a breakpoint is reached + /// If the return is true, processing can continue after the break + /// If the return is false, processing has ended (either normally or due to an error) + bool WaitForBreakOrCanceled(); + + /// Use this to have a thread wait for the end of a run + void WaitForEndOfRun(); + + /// Instruct the Controller to continue after a breakpoint + void Continue(); + + /// Cancel all threads and end the run + void Cancel( int code = 0 ); + + /// Reports whether controls is canceled + bool IsCanceled() const; + + /// Initiate a break + void Break(); + + /// Initiate a break with a return + template< typename... XArgs > + std::tuple< XArgs&... >& BreakAndReturn( XArgs&... args ); + + /// Report whether control is at a breakpoint + bool IsAtBreak() const; + + /// Check whether the return buffer has been filled + bool HasReturn() const; + + /// Get the return buffer + template< typename... XArgs > + std::tuple< XArgs&... >& GetReturn(); + + /// Notify the control that a chain is quitting + void ChainIsQuitting( const std::string& name, std::exception_ptr ePtr = std::exception_ptr() ); + + MEMVAR( Controller*, Control ); + + }; + + template< class... XArgs > + std::tuple< XArgs&... >& ControlAccess::BreakAndReturn( XArgs&... args ) + { + if( fControl ) return fControl->BreakAndReturn( args... ); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + + template< class... XArgs > + std::tuple< XArgs&... >& ControlAccess::GetReturn() + { + if( fControl ) return fControl->GetReturn< XArgs... >(); + else THROW_EXCEPT_HERE( Exception() << "Control access does not have a valid controller pointer" ); + } + +/* + class ReturnAccess : public scarab::cancelable + { + public: + ReturnAccess(); + + virtual ~ReturnAccess(); + + template< typename... Args > + void SetReturn( Args&... arg ); + + template< typename... Args > + std::tuple< Args&... >& GetReturn(); + + protected: + std::shared_ptr< ReturnBufferBase > fReturn; + + public: + //MEM_VAR_SHARED_PTR( SharedControl, Control ); + }; + + typedef std::shared_ptr< ControlAccess > ControlAccessPtr; + + + template< typename... Args > + void ReturnAccess::SetReturn( Args&... args ) + { + fReturn = std::make_shared< ReturnBuffer< Args... > >( args... ); + return; + } + + template< typename... Args > + std::tuple< Args&... >& ReturnAccess::GetReturn() + { + std::shared_ptr< ReturnBuffer< Args... > > buffer( std::dynamic_pointer_cast< ReturnBuffer< Args... > >(fReturn) ); + if( buffer == nullptr ) throw std::exception(); + return buffer->GetReturn(); + } +*/ +} /* namespace Nymph */ + +#endif /* NYMPH_CONTROLACCESS_HH_ */ diff --git a/Cpp/Library/Control/Controller.cc b/Cpp/Library/Control/Controller.cc new file mode 100644 index 00000000..1f4bda06 --- /dev/null +++ b/Cpp/Library/Control/Controller.cc @@ -0,0 +1,157 @@ +/* + * Controller.cc + * + * Created on: Jan 13, 2022 + * Author: N.S. Oblath + */ + +#include "Controller.hh" + +#include "ControlAccess.hh" +#include "Exception.hh" +#include "QuitChain.hh" + +#include "logger.hh" + + +namespace Nymph +{ + LOGGER( contlog, "Controller" ); + + Controller::Controller() : + scarab::cancelable(), + fCycleTimeMS( 500 ), + fMutex(), + fCondVarContinue(), + fCondVarBreak(), + fBreakFlag( false ), + fReturnBuffer(), + fReturnMutex() + { + ControlAccess::get_instance()->SetControl( this ); + } + + Controller::~Controller() + {} + + void Controller::Configure( const scarab::param_node& node ) + { + SetCycleTimeMS( node.get_value( "cycle-time-ms", fCycleTimeMS ) ); + + return; + } + + bool Controller::WaitToContinue() + { + std::unique_lock< std::mutex > lock( fMutex ); + while( fBreakFlag && ! is_canceled() ) + { + fCondVarContinue.wait_for( lock, std::chrono::milliseconds(fCycleTimeMS) ); + } + return ! is_canceled(); // returns true if the thread should continue; false if it should end + } + + bool Controller::WaitForBreakOrCanceled() + { + std::unique_lock< std::mutex > lock( fMutex ); + while( ! fBreakFlag && ! is_canceled() ) + { + fCondVarBreak.wait_for( lock, std::chrono::milliseconds(fCycleTimeMS) ); + } + return is_canceled() ? false : fBreakFlag; // returns true if the thread should continue; false if it should end + } + + void Controller::WaitForEndOfRun() + { + LDEBUG( contlog, "Waiting for end-of-run" ); + while( WaitForBreakOrCanceled() ) + { + LDEBUG( contlog, "Reached breakpoint; waiting for continue" ); + if( ! WaitToContinue() ) break; + LDEBUG( contlog, "Processing resuming; waiting for end-of-run" ); + } + LDEBUG( contlog, "End-of-run reached" ); + + return; + } + + void Controller::Continue() + { + std::unique_lock< std::mutex > lock( fMutex ); + LDEBUG( contlog, "RESUME called" ); + fBreakFlag = false; + std::unique_lock< std::mutex > retLock( fReturnMutex ); + fReturnBuffer.reset(); + fCondVarContinue.notify_all(); + return; + } + + void Controller::Cancel( int code ) + { + this->cancel( code ); + return; + } + + bool Controller::IsCanceled() const + { + return this->is_canceled(); + } + + void Controller::Break() + { + while( IsAtBreak() && ! IsCanceled() ) + { + if( ! WaitToContinue() ) + { + THROW_EXCEPT_HERE( Exception() << "Canceled while waiting to initiate a breakpoint" ); + } + } + + std::unique_lock< std::mutex > lock( fMutex ); + fBreakFlag = true; + fCondVarBreak.notify_all(); + return; + } + + bool Controller::IsAtBreak() const + { + std::unique_lock< std::mutex > lock( fMutex ); + return fBreakFlag; + } + + void Controller::ChainIsQuitting( const std::string& name, std::exception_ptr ePtr ) + { + LDEBUG( contlog, "Chain <" << name << "> is quitting" ); + + if( ePtr ) + { + try + { + std::rethrow_exception( ePtr ); + } + catch( const QuitChain& e ) + { + // not necessarily an error, so don't set quitAfterThis to true + LINFO( contlog, "Chain <" << name << "> exited with QuitChain" ); + } + catch( const scarab::base_exception& e ) + { + // this is an error, so set quitAfterThis to true + LERROR( contlog, "Chain <" << name << "> exited with an exception" ); + PrintException( e ); + this->Cancel( RETURN_ERROR ); + } + } + + return; + } + + void Controller::do_cancellation( int code ) + { + std::unique_lock< std::mutex > lock( fMutex ); + LDEBUG( contlog, "CANCEL called" ); + fCondVarBreak.notify_all(); + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Control/Controller.hh b/Cpp/Library/Control/Controller.hh new file mode 100644 index 00000000..8bda216c --- /dev/null +++ b/Cpp/Library/Control/Controller.hh @@ -0,0 +1,133 @@ +/* + * Controller.hh + * + * Created on: Jan 13, 2022 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_CONTROLLER_HH_ +#define NYMPH_CONTROLLER_HH_ + +#include "MemberVariable.hh" +#include "ReturnBuffer.hh" + +#include "cancelable.hh" +#include "param.hh" + +#include +#include + +#include + +namespace Nymph +{ + + /*! + @class Controller + @author N. S. Oblath + + @brief Base class for Controller classes, with basic break/continue/cancel functionality + + @details + A Controller can: + + * Cancel operations: typically used by either a Processor chain's thread (usually via ControlAccess) or the client of the Controller, this will result in the full cancelation, from the top down, of all Nymph-based operations + * Initiate a break: used mostly by a Processor chain's thread where a breakpoint is set, a breakpoint can be initiated with control returned to the client; parameters (e.g. data) can be returned at the break to provide access to the client + * Waiting on a break/cancellation/continuation: the Controller can be used to cause a thread to wait on any of those conditions; cancellation will interupt all wait conditions + * Be notified of a quitting Processor chain: when a Processor chain quits, it notifies the Controller (via ControlAccess) that it is quitting; if there's an exception, that should be provided to properly forward error conditions + */ + class Controller : public scarab::cancelable + { + public: + Controller(); + virtual ~Controller(); + + public: + /// Configure the controller + void Configure( const scarab::param_node& node ); + + MEMVAR( unsigned, CycleTimeMS ); + + public: + /// Use this to make a thread wait on a break + /// If the return is true, processing should continue after the break + /// If the return is false, processing has ended (either normally or due to an error) + virtual bool WaitToContinue(); + + /// Use this to wait for a breakpoint to be reached or for cancellation + /// If the return is true, processing can continue after the break + /// If the return is false, processing has ended (either normally or due to an error) + virtual bool WaitForBreakOrCanceled(); + + /// Use this to have a thread wait for the end of a run + virtual void WaitForEndOfRun(); + + /// Instruct the Controller to continue after a breakpoint + virtual void Continue(); + + /// Cancel all threads and end the run + virtual void Cancel( int code = 0 ); + + /// Reports whether controls is canceled + virtual bool IsCanceled() const; + + /// Inititate a break + virtual void Break(); + + /// Initiate a break with a return + template< typename... XArgs > + std::tuple< XArgs&... >& BreakAndReturn( XArgs&... args ); + + /// Reports whether control is at a breakpoint + virtual bool IsAtBreak() const; + + /// Notify the control that a chain is quitting + virtual void ChainIsQuitting( const std::string& name, std::exception_ptr ePtr = std::exception_ptr() ); + + MEMVAR_REF_MUTABLE( std::mutex, Mutex ); + MEMVAR_REF_MUTABLE( std::condition_variable, CondVarContinue ); + MEMVAR_REF_MUTABLE( std::condition_variable, CondVarBreak ); + MEMVAR_NOSET( bool, BreakFlag ); + + protected: + void do_cancellation( int code ); + + public: + /// Checks whether the return buffer has been filled + bool HasReturn() const; + + /// Get the return buffer + template< typename... XArgs > + std::tuple< XArgs&... >& GetReturn(); + + protected: + std::unique_ptr< ReturnBufferBase > fReturnBuffer; + MEMVAR_REF_MUTABLE( std::mutex, ReturnMutex ); + + }; + + inline bool Controller::HasReturn() const + { + return fReturnBuffer.operator bool(); + } + + template< typename... XArgs > + std::tuple< XArgs&... >& Controller::BreakAndReturn( XArgs&... args ) + { + this->Break(); + std::unique_lock< std::mutex > lock( fReturnMutex ); + fReturnBuffer = std::make_unique< ReturnBuffer >( args... ); + return fReturnBuffer->GetReturn< XArgs... >(); + } + + template< class... XArgs > + std::tuple< XArgs&... >& Controller::GetReturn() + { + std::unique_lock< std::mutex > lock( fReturnMutex ); + if( ! fReturnBuffer ) THROW_EXCEPT_HERE( Exception() << "Return buffer is currently empty" ); + return fReturnBuffer->GetReturn< XArgs... >(); + } + +} /* namespace Nymph */ + +#endif /* NYMPH_CONTROLLER_HH_ */ diff --git a/Cpp/Library/Control/ReturnBuffer.cc b/Cpp/Library/Control/ReturnBuffer.cc new file mode 100644 index 00000000..7e0bf57f --- /dev/null +++ b/Cpp/Library/Control/ReturnBuffer.cc @@ -0,0 +1,19 @@ +/* + * ReturnBuffer.cc + * + * Created on: Oct 22, 2019 + * Author: N.S. Oblath + */ + +#include "ReturnBuffer.hh" + +namespace Nymph +{ + + ReturnBufferBase::ReturnBufferBase() + {} + + ReturnBufferBase::~ReturnBufferBase() + {} + +} diff --git a/Cpp/Library/Control/ReturnBuffer.hh b/Cpp/Library/Control/ReturnBuffer.hh new file mode 100644 index 00000000..3445d3f5 --- /dev/null +++ b/Cpp/Library/Control/ReturnBuffer.hh @@ -0,0 +1,120 @@ +/* + * ReturnBuffer.hh + * + * Created on: Oct 22, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_RETURNBUFFER_HH_ +#define NYMPH_RETURNBUFFER_HH_ + +#include "Exception.hh" + +#include + +namespace Nymph +{ + + /*! + @class ReturnBufferBase + @author N. S. Oblath + + @brief Base class for ReturnBuffers, providing access to the returned parameters + + @details + ReturnBuffers are a type-erasure mechanism for returning parameters at a breakpoint in Nymph operations. + + This base class provides the type erasure while grating access to the returned paramters via a templated GetREturn() function. + */ + class ReturnBufferBase + { + public: + ReturnBufferBase(); + virtual ~ReturnBufferBase(); + + /// Provides access to the returned parameters + /// Throws Nymph::Exception if the buffer is empty or if XArgs... does not match the stored contents + template< class... XArgs > + std::tuple< XArgs&... >& GetReturn(); + + /// Provides const access to the returned parameters + /// Throws Nymph::Exception if the buffer is empty or if XArgs... does not match the stored contents + template< class... XArgs > + const std::tuple< XArgs&... >& GetReturn() const; + }; + + /*! + @class ReturnBuffer + @author N. S. Oblath + + @brief Provides storage for returned parameters + + @details + Parameters are stored as a `std::tuple`. + */ + template< class... XArgs > + class ReturnBuffer : public ReturnBufferBase + { + public: + ReturnBuffer() = delete; + ReturnBuffer( XArgs&... retval ); + virtual ~ReturnBuffer(); + + /// Provides access to the returned parameters + /// Throws Nymph::Exception if the buffer is empty + std::tuple< XArgs&... >& GetReturn(); + + /// Provides const access to the returned parameters + /// Throws Nymph::Exception if the buffer is empty + const std::tuple< XArgs&... >& GetReturn() const; + + protected: + std::tuple< XArgs&... > fReturn; + }; + + + // ReturnBuffer + + template< class... XArgs > + ReturnBuffer< XArgs... >::ReturnBuffer( XArgs&... retval ) : + fReturn( retval... ) + {} + + template< class... XArgs > + ReturnBuffer< XArgs... >::~ReturnBuffer() + {} + + template< class... XArgs > + std::tuple< XArgs&... >& ReturnBuffer< XArgs... >::GetReturn() + { + return fReturn; + } + + template< class... XArgs > + const std::tuple< XArgs&... >& ReturnBuffer< XArgs... >::GetReturn() const + { + return fReturn; + } + + + // Templated functions from ReturnBufferBase + template< class... XArgs > + std::tuple< XArgs&... >& ReturnBufferBase::GetReturn() + { + ReturnBuffer< XArgs... >* typedBuffer = dynamic_cast< ReturnBuffer< XArgs... >* >(this); + if( ! typedBuffer ) THROW_EXCEPT_HERE( Exception() << "Invalid set of arguments for this return buffer" ); + return typedBuffer->GetReturn(); + } + + template< class... XArgs > + const std::tuple< XArgs&... >& ReturnBufferBase::GetReturn() const + { + ReturnBuffer< XArgs... >* typedBuffer = dynamic_cast< ReturnBuffer< XArgs... >* >(this); + if( ! typedBuffer ) THROW_EXCEPT_HERE( Exception() << "Invalid set of arguments for this return buffer" ); + return typedBuffer->GetReturn(); + } + + +} /* namespace Nymph */ + +#endif /* NYMPH_RETURNBUFFER_HH_ */ diff --git a/Cpp/Library/Control/SingleRunController.cc b/Cpp/Library/Control/SingleRunController.cc new file mode 100644 index 00000000..430b9948 --- /dev/null +++ b/Cpp/Library/Control/SingleRunController.cc @@ -0,0 +1,321 @@ +/* + * SingleRunController.cc + * + * Created on: Jan 13, 2022 + * Author: N.S. Oblath + */ + +#include "SingleRunController.hh" + +#include "ControlAccess.hh" +#include "PrimaryProcessor.hh" +#include "ProcessorToolbox.hh" +#include "QuitChain.hh" +#include "ServiceToolbox.hh" + +#include "logger.hh" +#include "param.hh" + +namespace Nymph +{ + LOGGER( contlog, "SingleRunController"); + + SingleRunController::SingleRunController( const ProcessorToolbox& procTB, const ServiceToolbox& svcTB, const std::string& name ) : + Controller(), + fProcTB( procTB ), + fSvcTB( svcTB ), + fRunQueue(), + fNActiveThreads( 0 ), + fDoRunThread(), + fChainThreads() + { + } + + SingleRunController::~SingleRunController() + { + } + + void SingleRunController::Configure( const scarab::param_node& node ) + { + LPROG( contlog, "Configuring run control" ); + + Controller::Configure( node ); + + // The run queue is an array of processor names, or groups of names, which will be run sequentially. + // If names are grouped (in another array), those in that group will be run in parallel. + if( ! node.has("run-queue") ) + { + LWARN( contlog, "Run queue was not specified" ); + } + else + { + ConfigureRunQueue( node["run-queue"].as_array() ); + } + + return; + } + + void SingleRunController::ConfigureRunQueue( const scarab::param_array& array ) + { + for( auto rqIt = array.begin(); rqIt != array.end(); ++rqIt ) + { + if( rqIt->is_value() ) + { + if( ! PushBackToRunQueue( (*rqIt)().as_string() ) ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to process run-queue entry: could not add processor to the queue" ); + } + } + else if( rqIt->is_array() ) + { + const scarab::param_array* rqNode = &( rqIt->as_array() ); + std::vector< std::string > names; + + for( scarab::param_array::const_iterator rqArrayIt = rqNode->begin(); rqArrayIt != rqNode->end(); ++rqArrayIt ) + { + if( ! rqArrayIt->is_value() ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Invalid run-queue array entry: not a value" ); + } + names.push_back( (*rqArrayIt)().as_string() ); + } + + if( ! PushBackToRunQueue(names) ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to process run-queue entry: could not add list of processors to the queue" ); + } + } + else + { + THROW_EXCEPT_HERE( ConfigException(array) << "Invalid run-queue entry: not a value or array" ); + } + } + + return; + } + + bool SingleRunController::PushBackToRunQueue( const std::string& name ) + { + ThreadSourceGroupT threadGroup; + + if( ! AddProcessorToThreadGroup( name, threadGroup ) ) + { + LERROR( contlog, "Unable to add processor <" << name << "> to thread group" ); + return false; + } + + fRunQueue.push_back( threadGroup ); + + LINFO( contlog, "Added processor <" << name << "> to the run queue" ); + return true; + } + + bool SingleRunController::PushBackToRunQueue( std::initializer_list< std::string > names ) + { + return PushBackToRunQueue( std::vector< std::string >(names) ); + } + + bool SingleRunController::PushBackToRunQueue( std::vector< std::string > names ) + { + ThreadSourceGroupT threadGroup; + + std::stringstream toPrint; + for( const std::string& name : names ) + { + if(! AddProcessorToThreadGroup( name, threadGroup ) ) + { + LERROR( contlog, "Unable to add processor <" << name << "> to thread group" ); + return false; + } + toPrint << name << ", "; // the extra comma at the end is removed below + } + + fRunQueue.push_back( threadGroup ); + std::string toPrintString = toPrint.str(); + toPrintString.resize( toPrintString.size() - 2 ); + LINFO( contlog, "Added processors <" << toPrintString << "> to the run queue" ); + return true; + } + + bool SingleRunController::AddProcessorToThreadGroup( const std::string& name, ThreadSourceGroupT& group ) + { + std::shared_ptr< Processor > procForRunQueue = fProcTB.GetProcessor( name ); + LDEBUG( contlog, "Attempting to add processor <" << name << "> to the run queue" ); + if( procForRunQueue == nullptr ) + { + LERROR( contlog, "Unable to find processor <" << name << "> requested for the run queue" ); + return false; + } + + PrimaryProcessor* primaryProc = dynamic_cast< PrimaryProcessor* >( procForRunQueue.get() ); + if( primaryProc == nullptr ) + { + LERROR( contlog, "Processor <" << name << "> is not a primary processor." ); + return false; + } + group.insert( ThreadSource(primaryProc, name) ); + return true; + } + + void SingleRunController::Run() + { + StartRun(); + + if( fDoRunThread.joinable() ) + { + WaitForEndOfRun(); + + JoinRunThread(); + } + + return; + } + + void SingleRunController::StartRun() + { + if( fDoRunThread.joinable() ) + { + LERROR( contlog, "It appears that a run has already been started" ); + return; + } + + if( ! fChainThreads.empty() ) + { + LERROR( contlog, "Chain threads map is not empty; cannot start new threads" ); + return; + } + + auto multiThreadRun = [&]() + { + try + { + LPROG( contlog, "Starting multi-threaded processing" ); + + for (auto rqIter = fRunQueue.begin(); rqIter != fRunQueue.end(); ++rqIter) + { + { // scope for mutex lock + std::unique_lock< std::mutex > lock( fMutex ); + + // iterate over primary processors in this group and launch threads + for( auto tgIter = rqIter->begin(); tgIter != rqIter->end(); ++tgIter ) + { + std::string procName( tgIter->fName ); + LINFO( contlog, "Starting processor <" << procName << ">" ); + + fChainThreads.emplace( + procName, + ThreadBundle( + tgIter->fProc, + std::thread(&PrimaryProcessor::operator(), tgIter->fProc), + std::exception_ptr() + ) + ); + ++fNActiveThreads; + }// end for loop over the thread group + } + + // wait here while things are still running + while( ! is_canceled() && fNActiveThreads > 0 ) + { + std::this_thread::sleep_for( std::chrono::milliseconds(fCycleTimeMS) ); + } + LDEBUG( contlog, "Past the wait for threads to stop.\nEither canceled (" << is_canceled() << ") or no active threads remain (# active threads: " << fNActiveThreads << ")" ); + + // ok, we're not running anymore. join all threads and move along. + + // join threads and check for exceptions + // break out of the run-queue iteration loop if there was an exception + bool quitAfterThis = is_canceled(); + for( auto threadIt = fChainThreads.begin(); threadIt != fChainThreads.end(); ++threadIt ) + { + LINFO( contlog, "Cleaning up thread for processor <" << threadIt->first << ">" ) + + std::thread thread( std::move(std::get<1>( threadIt->second )) ); + if( ! thread.joinable() ) + { + LDEBUG( contlog, "Thread for processor <" << threadIt->first << "> is not joinable" ); + } + else + { + LDEBUG( contlog, "Joining thread, ID = " << thread.get_id() ); + thread.join(); + } + + std::exception_ptr exPtr( std::move(std::get<2>( threadIt->second )) ); + if( exPtr ) + { + try + { + std::rethrow_exception( exPtr ); + } + catch( const QuitChain& e ) + { + // not necessarily an error, so don't set quitAfterThis to true + LINFO( contlog, "Thread had exited with QuitChain" ); + } + catch( const scarab::base_exception& e ) + { + // this is an error, so set quitAfterThis to true + LERROR( contlog, "Thread had exited with an exception" ); + PrintException( e ); + quitAfterThis = true; + } + } + else + { + // this thread did not have an exeption set, so it exited normally + LINFO( contlog, "Thread had exited normally" ) + } + + } + + fChainThreads.clear(); + + if( quitAfterThis ) + { + LWARN( contlog, "Exiting from run-queue loop under abnormal conditions" ); + break; + } + + } // end for loop over the run-queue + + // we have to cancel on success to make sure anything waiting on the control in some way finishes up + this->Cancel( RETURN_SUCCESS ); + + LPROG( contlog, "Processing is complete" ); + } + catch( scarab::base_exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + LERROR( contlog, "Caught scarab::base_exception thrown in a processor or in the multi-threaded run function" ); + LERROR( contlog, "Diagnostic Information:\n" ); + PrintException( e ); + ControlAccess::get_instance()->Cancel(); + return; + } + catch( std::exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + LERROR( contlog, "Caught std::exception thrown in a processor or in the multi-threaded run function: " << e.what() ); + ControlAccess::get_instance()->Cancel(); + return; + } + return; + }; // end multi-threaded run lambda + + fDoRunThread = std::thread( multiThreadRun ); + return; + } + + void SingleRunController::ChainIsQuitting( const std::string& name, std::exception_ptr ePtr ) + { + Controller::ChainIsQuitting( name, ePtr ); + + std::get<2>( fChainThreads.at(name) ) = std::move(ePtr); + + --fNActiveThreads; + + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Control/SingleRunController.hh b/Cpp/Library/Control/SingleRunController.hh new file mode 100644 index 00000000..b4177822 --- /dev/null +++ b/Cpp/Library/Control/SingleRunController.hh @@ -0,0 +1,163 @@ +/** + @file SingleRunController.hh + @brief Contains SingleRunController + @details + @author: N. S. Oblath + @date: Sep 27, 2012 + */ + +#ifndef NYMPH_SINGLERUNCONTROLLER_HH_ +#define NYMPH_SINGLERUNCONTROLLER_HH_ + +#include "Controller.hh" + +#include +#include +#include +#include +#include +#include + +namespace scarab +{ + class param_node; +} + +namespace Nymph +{ + class PrimaryProcessor; + class ProcessorToolbox; + class ServiceToolbox; + + /*! + @class SingleRunController + @author N. S. Oblath + + @brief Performs a single run as specified to the ProcessorToolbox + + @details + The SingleRunController performs a run of Katydid as defined by the run queue. + Processors are run in chains headed by a PrimaryProcessor. + The way that PrimaryProcessors are run are specified by the run queue. + PrimaryProcessors can be run serially, or in groups that run in parallel. + + The run queue can be defined in the configuration as follows: +
  • run-queue -- (array of strings and arrays of strings) define the queue of processors that will control the running of Nymph. + The elements of this array specify processors that are run sequentially. + If an element is itself an array, those processors listed in the sub-array will be run in parallel. +
      +
    • processor name -- add a processor to the run queue, or
    • +
    • array of processor names -- add a group of processors to the run queue.
    • +
    +
  • + */ + class SingleRunController : public Controller + { + public: + SingleRunController( const ProcessorToolbox& procTB, const ServiceToolbox& svcTB, const std::string& name = "single-run-controller" ); + virtual ~SingleRunController(); + + public: + /// Configure the controller + void Configure( const scarab::param_node& node ); + + public: + struct ThreadSource + { + PrimaryProcessor* fProc; + std::string fName; + //ControlAccessPtr fControlAccess; + ThreadSource( PrimaryProcessor* proc, const std::string& name ) : + fProc(proc), fName(name)//, fControlAccess( new ControlAccess() ) + {} + }; + struct CompareThreadSource + { + bool operator()( const ThreadSource& lhs, const ThreadSource& rhs ) const + { + return lhs.fProc < rhs.fProc; + } + }; + typedef std::set< ThreadSource, CompareThreadSource > ThreadSourceGroupT; + typedef std::deque< ThreadSourceGroupT > RunQueueT; + + /// Setup the run queue according to the `run-queue` configuration block + void ConfigureRunQueue( const scarab::param_array& node ); + + /// Push a single processor to the back of the run queue + bool PushBackToRunQueue( const std::string& name ); + + /// Push a set of processors to be run in parallel to the back of the run queue + bool PushBackToRunQueue( std::initializer_list< std::string > names ); + /// Push a set of processors to be run in parallel to the back of the run queue + bool PushBackToRunQueue( std::vector< std::string > names ); + + /// Remove the last item in the run queue, whether it's a single processor or a group of processors + void PopBackOfRunQueue(); + + /// Clear the run queue + void ClearRunQueue(); + + /// Const access to the run queue + const RunQueueT& RunQueue() const; + + protected: + const ProcessorToolbox& fProcTB; + const ServiceToolbox& fSvcTB; + + RunQueueT fRunQueue; + + bool AddProcessorToThreadGroup( const std::string& name, ThreadSourceGroupT& group ); + + public: + /// Process the run queue. + /// This will call Run() on all of the primary processors in the queue. + /// This is a blocking call that waits until the run is completed before returning. + void Run(); + + /// Asynchronously start a run + /// Starts run in a new thread and returns + void StartRun(); + + /// Wait for the run to complete; + void JoinRunThread(); + + /// Notify the control that a chain is quitting + virtual void ChainIsQuitting( const std::string& name, std::exception_ptr ePtr = std::exception_ptr() ); + + MEMVAR( unsigned, NActiveThreads ); + + protected: + std::thread fDoRunThread; + + typedef std::tuple< PrimaryProcessor*, std::thread, std::exception_ptr > ThreadBundle; + std::map< std::string, ThreadBundle > fChainThreads; + + }; + + inline void SingleRunController::PopBackOfRunQueue() + { + fRunQueue.pop_back(); + return; + } + + inline void SingleRunController::ClearRunQueue() + { + fRunQueue.clear(); + return; + } + + inline const SingleRunController::RunQueueT& SingleRunController::RunQueue() const + { + return fRunQueue; + } + + inline void SingleRunController::JoinRunThread() + { + fDoRunThread.join(); + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_SINGLERUNCONTROLLER_HH_ */ diff --git a/Cpp/Library/Data/Data.cc b/Cpp/Library/Data/Data.cc new file mode 100644 index 00000000..12be7202 --- /dev/null +++ b/Cpp/Library/Data/Data.cc @@ -0,0 +1,18 @@ +/* + * Data.cc + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + +#include "Data.hh" + +namespace Nymph +{ + Data::Data() + {} + + Data::~Data() + {} + +} /* namespace Nymph */ diff --git a/Cpp/Library/Data/Data.hh b/Cpp/Library/Data/Data.hh new file mode 100644 index 00000000..590deca0 --- /dev/null +++ b/Cpp/Library/Data/Data.hh @@ -0,0 +1,24 @@ +/* + * Data.hh + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + + +#ifndef NYMPH_DATA_HH_ +#define NYMPH_DATA_HH_ + +namespace Nymph +{ + + class Data + { + public: + Data(); + virtual ~Data(); + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_DATA_HH_ */ diff --git a/Cpp/Library/Data/DataFrame.cc b/Cpp/Library/Data/DataFrame.cc new file mode 100644 index 00000000..12240592 --- /dev/null +++ b/Cpp/Library/Data/DataFrame.cc @@ -0,0 +1,19 @@ +/* + * DataFrame.cc + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + +#include "DataFrame.hh" + +namespace Nymph +{ + DataFrame::DataFrame() : + fDataObjects() + {} + + DataFrame::~DataFrame() + {} + +} /* namespace Nymph */ diff --git a/Cpp/Library/Data/DataFrame.hh b/Cpp/Library/Data/DataFrame.hh new file mode 100644 index 00000000..755f2063 --- /dev/null +++ b/Cpp/Library/Data/DataFrame.hh @@ -0,0 +1,180 @@ +/* + * DataFrame.hh + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + + +#ifndef NYMPH_DATAFRAME_HH_ +#define NYMPH_DATAFRAME_HH_ + +#include "Data.hh" +#include "Exception.hh" + +#include "MemberVariable.hh" + +#include "typename.hh" + +#include +#include +#include + +namespace Nymph +{ + class DataFrameException : public scarab::typed_exception< DataFrameException > + { + public: + using scarab::typed_exception< DataFrameException >::typed_exception; + ~DataFrameException() = default; + }; + + // forward declare so we can define DataHandle here + class DataFrame; + + /// Pointer object to use for passing between Processors + typedef std::shared_ptr< DataFrame > DataHandle; + + /*! + @class DataFrame + + @author N. S. Oblath + + @brief Container for Data objects used during data processing + + @details + Individual Data objects are held in an unordered map, indexed by type. + + */ + class DataFrame + { + public: + DataFrame(); + DataFrame( const DataFrame& ) = delete; + virtual ~DataFrame(); + + DataFrame& operator=( const DataFrame& ) = delete; + + /// Returns true if the frame has no data objects + bool Empty() const; + + /// Returns true if object of type(s) XData exist in the frame; returns false otherwise; Has<>() returns true + template< typename... XData > + bool Has() const; + + /// Returns a reference to the object of type XData if it exists in the frame. + /// Creates and returns a reference to an object of type XData if it does not exist. + template< typename XData > + XData& Get(); + + /// Returns a reference to the object of type XData if it exists in the frame. + /// Throws DataFrameException if it does not exist. + template< typename XData > + const XData& Get() const; + + /// Inserts the provided object of type XData into the frame, taking ownership of the object. + /// If an object of this type exists, it is replaced. + template< typename XData > + void Set( XData* ptr ); + + /// Inserts the provided object of type XData into the frame, taking ownership of the object. + /// If an object of this type exists, it is replaced. + template< typename XData > + void Set( std::unique_ptr&& ptr ); + + /// Inserts a copy of the provided object of type XData into the frame. + /// If an object of this type exists, it is replaced. + template< typename XData > + void Set( const XData& obj ); + + /// Removes and destroys the object of type XData if it exists in the frame. + /// Does nothing if the object does not exist. + template< typename XData > + void Remove(); + + // typedef used to avoid problems with the comma in the MEMVAR macro + typedef std::unordered_map< std::type_index, std::unique_ptr > DataMap; + MEMVAR_REF( DataMap, DataObjects ); + + protected: + template< typename XData > + bool HasOneType() const; + }; + + + inline bool DataFrame::Empty() const + { + return fDataObjects.empty(); + } + + template< typename... XData > + bool DataFrame::Has() const + { + return ( HasOneType() && ... ); + } + + template< typename XData > + bool DataFrame::HasOneType() const + { + typedef std::remove_const_t< XData > XDataNoConst; + if( fDataObjects.count( typeid(XDataNoConst) ) == 0 ) return false; + return true; + } + + template< typename XData > + XData& DataFrame::Get() + { + typedef std::remove_const_t< XData > XDataNoConst; + if( ! Has< XDataNoConst >() ) + { + fDataObjects[ typeid(XDataNoConst) ].reset( new XDataNoConst() ); + } + return static_cast< XDataNoConst& >( *fDataObjects[typeid(XDataNoConst)] ); + } + + template< typename XData > + const XData& DataFrame::Get() const + { + typedef std::remove_const_t< XData > XDataNoConst; + if( Has< XDataNoConst >() ) + { + return static_cast< const XDataNoConst& >( *fDataObjects.at(typeid(XDataNoConst)) ); + } + THROW_EXCEPT_HERE( DataFrameException() << "Data type <" << scarab::type(XDataNoConst()) << "> is not present when const Get() was called" ); + } + + template< typename XData > + void DataFrame::Set( XData* ptr ) + { + typedef std::remove_const_t< XData > XDataNoConst; + fDataObjects[ typeid(XDataNoConst) ].reset( ptr ); // take ownership of ptr + return; + } + + template< typename XData > + void DataFrame::Set( std::unique_ptr< XData >&& ptr ) + { + typedef std::remove_const_t< XData > XDataNoConst; + fDataObjects[ typeid(XDataNoConst) ] = std::move(ptr); // take ownership of ptr + return; + } + + template< typename XData > + void DataFrame::Set( const XData& obj ) + { + typedef std::remove_const_t< XData > XDataNoConst; + fDataObjects[ typeid(XDataNoConst) ].reset( new XDataNoConst(obj) ); // make a copy of obj + return; + } + + template< typename XData > + void DataFrame::Remove() + { + typedef std::remove_const_t< XData > XDataNoConst; + fDataObjects.erase( typeid(XDataNoConst) ); + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_DATAFRAME_HH_ */ diff --git a/Cpp/Library/Data/DataManager.cc b/Cpp/Library/Data/DataManager.cc new file mode 100644 index 00000000..3fda7bc9 --- /dev/null +++ b/Cpp/Library/Data/DataManager.cc @@ -0,0 +1,18 @@ +/* + * DataManager.cc + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + +#include "DataManager.hh" + +namespace Nymph +{ + DataManager::DataManager() + {} + + DataManager::~DataManager() + {} + +} /* namespace Nymph */ diff --git a/Cpp/Library/Data/DataManager.hh b/Cpp/Library/Data/DataManager.hh new file mode 100644 index 00000000..3552738b --- /dev/null +++ b/Cpp/Library/Data/DataManager.hh @@ -0,0 +1,39 @@ +/* + * DataManager.hh + * + * Created on: Nov 5, 2019 + * Author: N.S. Oblath + */ + + +#ifndef NYMPH_DATAMANAGER_HH_ +#define NYMPH_DATAMANAGER_HH_ + +#include "MemberVariable.hh" + +#include +#include + +namespace Nymph +{ + struct Holder + {} + + template< typename XType > + struct TypedHolder + { + + } + + class DataManager + { + public: + DataManager(); + virtual ~DataManager(); + + MEMVAR_REF( std::map< std::type_index, std::list< Holder > >, DataStore ); + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_DATAMANAGER_HH_ */ diff --git a/Cpp/Library/Data/SignalData.hh b/Cpp/Library/Data/SignalData.hh new file mode 100644 index 00000000..1c75c444 --- /dev/null +++ b/Cpp/Library/Data/SignalData.hh @@ -0,0 +1,45 @@ +/* + * SignalData.hh + * + * Created on: Jan 25, 2022 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_SIGNALDATA_HH_ +#define NYMPH_SIGNALDATA_HH_ + +#include "Signal.hh" + +#include "DataFrame.hh" + +namespace Nymph +{ + /*! + @class SignalData + @author N. S. Oblath + + @brief Creates a signal that takes a DataFrame object as its argument. + + @details + The purpose of the signal is for passing DataFrame pointers between Processors. + + // TODO: remove this comment; perhaps move it to Slot or SlotData + If a DataSlot is being used to receive the DataFrame that this Signal is supposed to emit + then the Slot can be given a pointer to this Signal and the generic Slot function will automatically emit this Signal. + + Usage: + In your Processor's header add a member variable of type SignalData: + + SignalData fMySignal; + + Initialize the signal with the processor's 'this' pointer and the name of the signal: + + fMySignal( "my-signal", this ) + + That's it! + */ + typedef Signal< DataHandle > SignalData; + +} /* namespace Nymph */ + +#endif /* NYMPH_SIGNALDATA_HH_ */ diff --git a/Cpp/Library/Data/SlotData.hh b/Cpp/Library/Data/SlotData.hh new file mode 100644 index 00000000..c14073bd --- /dev/null +++ b/Cpp/Library/Data/SlotData.hh @@ -0,0 +1,154 @@ +/* + * SlotData.hh + * + * Created on: Jan 25, 2022 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_SLOTDATA_HH_ +#define NYMPH_SLOTDATA_HH_ + +#include "Slot.hh" +#include "SignalData.hh" + + +namespace Nymph +{ + template< class... XTypes > + struct TypeList {}; + + template< class... XTypes > + using In = TypeList< XTypes... >; + + template< class... XTypes > + using Out = TypeList< XTypes... >; + + // convenience macro: avoid needing to prepend In and Out with Nymph:: +#define USING_NYMPH_IN_OUT \ + template< class... XTypes > \ + using In = Nymph::In; \ + template< class... XTypes > \ + using Out = Nymph::Out; + + template< class XInList, class XOutList > + class SlotData; + + class Processor; + + /*! + @class SlotData + @author N. S. Oblath + + @brief Creates a slot that takes a DataHandle as the argument; the function that gets called should take 0 or more DataType&'s as its argument(s). + + @details + Usage: + This slot type includes the slot function with signature `void (DataHandle)`. + + Your processor (or, optionally, a different object) must have a member function with the signature bool ([input and output data-type references]). + The function that gets called can have both input data types (assumed to be const) and output data types (assumed to be non-const). The following + are all valid examples: + * void my_func(); // no arguments + * void my_func(DataType1&); // DataType1 is non-const + * void my_func(const DataType2&); // DataType2 is const + * void my_func(const DataType3& const DataType4&, DataType5&); // DataType3 and DataType4 are const; DataType5 is non-const + Note that const data types (i.e. inputs) should come before non-const types (i.e. outputs) in the function signature, + and the order of the data types in the `In<>` and `Out<>` type lists must match the order of the types in the function signature. + + The slot function checks that the provided DataFrame object (pointed to by the DataHandle) contains objects of all of the const data types, + creates (or retrieves) the objects of the non-const data types, and then calls the member function. + + In your Processor's header add a member variable of type SlotData< In<[input data types]>, Out<[output data types]> >. + The access specifier for the slot (e.g. protected, private, or public) does not affect the usability of the slot. + + Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. + Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. + + Also optionally, a signal to be emitted after the return of the member function can be specified as the last argument. + */ + template< template class XInList, template class XOutList, class... XInTypes, class... XOutTypes > + class SlotData< XInList, XOutList > : public Slot< DataHandle > + { + public: + template< class XFuncOwnerType > + using Signature = void (XFuncOwnerType::*)( XInTypes&..., XOutTypes&... ); + + public: + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XOwnerType > + SlotData( const std::string& name, XOwnerType* owner, Signature< XOwnerType > func, SignalData* signalPtr = nullptr ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XSlotOwnerType, class XFuncOwnerType > + SlotData( const std::string& name, XSlotOwnerType* proc, XFuncOwnerType* inst, Signature< XFuncOwnerType > func, SignalData* signalPtr = nullptr ); + + virtual ~SlotData(); + + void operator()( DataHandle handle ); + + protected: + //function_signature fFunc; + std::function< void (XInTypes&..., XOutTypes&... ) > fFunc; + + SignalData* fSignalPtr; + }; + + + //******************* + // Implementations + //******************* + + template< template class XInList, template class XOutList, class... XInTypes, class... XOutTypes > + template< class XOwnerType > + SlotData< XInList, XOutList >::SlotData(const std::string& name, XOwnerType* owner, SlotData< XInList, XOutList >::Signature< XOwnerType > func, SignalData* signalPtr ) : + Slot( name, owner, this, &SlotData::operator() ), + fFunc( [func, owner]( XInTypes&... inArgs, XOutTypes&... outArgs ){ (owner->*func)( inArgs..., outArgs... ); } ), + fSignalPtr( signalPtr ) + {} + + template< template class XInList, template class XOutList, class... XInTypes, class... XOutTypes > + template< class XSlotOwnerType, class XFuncOwnerType > + SlotData< XInList, XOutList >::SlotData(const std::string& name, XSlotOwnerType* owner, XFuncOwnerType* inst, SlotData< XInList, XOutList >::Signature< XFuncOwnerType > func, SignalData* signalPtr ) : + Slot( name, owner, this, &SlotData::operator() ), + fFunc( [func, inst]( XInTypes&... inArgs, XOutTypes&... outArgs ){ (inst->*func)( inArgs..., outArgs... ); } ), + fSignalPtr( signalPtr ) + {} + + template< template class XInList, template class XOutList, class... XInTypes, class... XOutTypes > + SlotData< XInList, XOutList >::~SlotData() + {} + + template< template class XInList, template class XOutList, class... XInTypes, class... XOutTypes > + void SlotData< XInList, XOutList >::operator()( DataHandle handle ) + { + // Standard data slot pattern: + + // Check to ensure that the required data type is present + if( ! handle->Has< XInTypes... >() ) + { + THROW_EXCEPT_HERE( Exception() << "Failed to find all of the necessary data types in slot <" << fName << ">. Aborting." ); + return; + } + + // Call the function + try + { + fFunc( handle->Get< XInTypes >()... , handle->Get< XOutTypes >()... ); + } + catch( scarab::base_exception& ) + { + THROW_NESTED_EXCEPT_HERE( Exception() << "Something went wrong in slot <" + fName + ">. Aborting." ); + return; + } + + // If there's a signal pointer, emit the signal + if( fSignalPtr ) + { + (*fSignalPtr)( handle ); + } + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_SLOTDATA_HH_ */ diff --git a/Cpp/Library/Implementation/RunNymph.cc b/Cpp/Library/Implementation/RunNymph.cc new file mode 100644 index 00000000..2134e727 --- /dev/null +++ b/Cpp/Library/Implementation/RunNymph.cc @@ -0,0 +1,123 @@ +/* + * RunNymph.cc + * + * Created on: Sep 13, 2016 + * Author: obla999 + */ + +#include "ProcessorToolbox.hh" +#include "ServiceToolbox.hh" +#include "SingleRunController.hh" + +#include "application.hh" +#include "logger.hh" +#include "param.hh" + +#include + +LOGGER( nlog, "RunNymph" ); + +namespace Nymph +{ + int RunNymph( scarab::param_node& config ) + { + try + { + LPROG( nlog, "Configuring processor toolbox" ); + + // Create the toolboxes and inject dependency + ProcessorToolbox procTB; + ServiceToolbox svcTB; + procTB.SetServiceToolbox( &svcTB ); + + // Configure toolboxes + svcTB.Configure( config); + procTB.Configure( config); + + // Create and configure the single-run controller. + LPROG( nlog, "Configuring controller" ); + SingleRunController controller( procTB, svcTB ); + if( config.has("controller") ) + { + controller.Configure( config["controller"].as_node() ); + } + + if( config.has( "dry-run" ) && config["dry-run"]().as_bool() ) + { + LPROG( nlog, "Dry run: no execution" ); + } + else + { + LPROG( nlog, "Executing run" ); + controller.Run(); + } + + LPROG( nlog, "That's all, folks!" ); + + return RETURN_SUCCESS; + } + catch( scarab::base_exception& e ) + { + LERROR( nlog, "Exception caught:" ); + PrintException( e ); + return RETURN_ERROR; + } + catch( std::exception& e ) + { + LERROR( nlog, "Exception caught:" ); + PrintException( e ); + return RETURN_ERROR; + } + } + + void AddRunNymphOptions( scarab::main_app& an_app ) + { + // options + an_app.add_config_flag< bool >( "--dry-run", "dry-run", "Load the config, setup processors, but do not execute the run" ); + } + + int ProcessorCheck( scarab::param_node& a_config ) + { + ProcessorToolbox tb; + + LWARN( nlog, a_config ); + + if( a_config.has( "list-procs" ) && a_config["list-procs"]().as_bool() ) + { + using factory_type = const scarab::factory< Processor, const std::string& >; + factory_type* t_factory = tb.ProcFactory(); + std::stringstream t_list_ss; + for( auto it = t_factory->begin(); it != t_factory->end(); ++it ) + { + t_list_ss << it->first << '\n'; + + } + LPROG( nlog, "Available processors:\n" << t_list_ss.str() ); + return RETURN_SUCCESS; + } + else + { + if( ! a_config.has( "proc-type" ) || a_config["proc-type"]().as_string().empty() ) + { + LERROR( nlog, "No processor type was provided to check" ); + return RETURN_ERROR; + } + std::string t_proc_type( a_config["proc-type"]().as_string() ); + bool t_proc_is_available = tb.CouldBuild( t_proc_type ); + if( ! t_proc_is_available ) + { + LWARN( nlog, "Processor <" << t_proc_type << "> is NOT registered with the processor toolbox" ); + return -1; + } + LPROG( nlog, "Processor <" << t_proc_type << "> is known to the processor toolbox" ); + return RETURN_SUCCESS; + } + } + + void AddProcessorCheckOptions( scarab::config_decorator* a_subcommand ) + { + // options + a_subcommand->add_config_option< std::string >( "proc-type", "proc-type", "Query Nymph to see if this processor type has been registered; returns 0 if present; -1 if not present" ); + a_subcommand->add_config_flag< bool >( "-l,--list-procs", "list-procs", "List available processors" ); + } +} diff --git a/Cpp/Library/Implementation/RunNymph.hh b/Cpp/Library/Implementation/RunNymph.hh new file mode 100644 index 00000000..9a6ad012 --- /dev/null +++ b/Cpp/Library/Implementation/RunNymph.hh @@ -0,0 +1,28 @@ +/* + * RunNymph.hh + * + * Created on: Sep 13, 2016 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_RUNNYMPH_HH_ +#define NYMPH_RUNNYMPH_HH_ + +namespace scarab +{ + class config_decorator; + class main_app; + class param_node; +} + +namespace Nymph +{ + int RunNymph( scarab::param_node& config ); + void AddRunNymphOptions( scarab::main_app& an_app ); + + int ProcessorCheck( scarab::param_node& config ); + void AddProcessorCheckOptions( scarab::config_decorator* an_app ); +} + + +#endif /* NYMPH_RUNNYMPH_HH_ */ diff --git a/Cpp/Library/Processor/HelloWorldProc.cc b/Cpp/Library/Processor/HelloWorldProc.cc new file mode 100644 index 00000000..bb8bb5d0 --- /dev/null +++ b/Cpp/Library/Processor/HelloWorldProc.cc @@ -0,0 +1,33 @@ +/* + * HelloWorldCpp.cc + * + * Created on: Sep 22, 2025 + * Author: N.S. Oblath + */ + +#include "HelloWorldProc.hh" + +#include "logger.hh" + +REGISTER_PROCESSOR( Nymph, HelloWorldCpp, "hello-world-cpp" ); + +LOGGER( hwlog, "hello-world-proc") + +namespace Nymph +{ + + HelloWorldCpp::HelloWorldCpp( const std::string& name ) : + Nymph::Processor( name ), + fHelloSig( "hello", this ), + fHelloSlot( "hello", this, &HelloWorldCpp::SayHello ) + {} + + HelloWorldCpp::~HelloWorldCpp() + {} + + void HelloWorldCpp::SayHello() + { + LPROG( hwlog, "Hello, world" ); + return; + } +} diff --git a/Cpp/Library/Processor/HelloWorldProc.hh b/Cpp/Library/Processor/HelloWorldProc.hh new file mode 100644 index 00000000..dbc097b8 --- /dev/null +++ b/Cpp/Library/Processor/HelloWorldProc.hh @@ -0,0 +1,35 @@ +/* + * HelloWorldProc.hh + * + * Created on: Sept 20, 2025 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_HELLOWORLDPROC_HH_ +#define NYMPH_HELLOWORLDPROC_HH_ + +#include "Processor.hh" +#include "Signal.hh" +#include "Slot.hh" + +namespace Nymph +{ + + class HelloWorldCpp : public Nymph::Processor + { + public: + HelloWorldCpp( const std::string& name = "hello-world-cpp" ); + + virtual ~HelloWorldCpp(); + + void Configure( const scarab::param_node& node ) {}; + + void SayHello(); + + MEMVAR_REF( Nymph::Signal<>, HelloSig ); + MEMVAR_REF( Nymph::Slot<>, HelloSlot ); + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_HELLOWORLDPROC_HH_ */ diff --git a/Cpp/Library/Processor/PrimaryProcessor.cc b/Cpp/Library/Processor/PrimaryProcessor.cc new file mode 100644 index 00000000..9f89d8d6 --- /dev/null +++ b/Cpp/Library/Processor/PrimaryProcessor.cc @@ -0,0 +1,53 @@ +/* + * PrimaryProcessor.cc + * + * Created on: Oct 10, 2012 + * Author: N.S. Oblath + */ + +#include "PrimaryProcessor.hh" + +#include "ControlAccess.hh" +#include "QuitChain.hh" + +#include "logger.hh" + +namespace Nymph +{ + LOGGER( proclog, "PrimaryProcessor" ); + + PrimaryProcessor::PrimaryProcessor( const std::string& name ) : + Processor( name )//, +// fExceptionPtr() + {} + + PrimaryProcessor::~PrimaryProcessor() + {} + + void PrimaryProcessor::operator()() + { + // go! + try + { + Run(); + } + catch( const QuitChain& e ) + { + LINFO( proclog, "Processor chain started by <" << fName << "> is quitting" ); + ControlAccess::get_instance()->ChainIsQuitting( fName, std::current_exception() ); + return; + } + catch( const std::exception& e ) + { + LERROR( proclog, "An error occurred during processor running: " << e.what() ); + ControlAccess::get_instance()->ChainIsQuitting( fName, std::current_exception() ); + return; + } + + LWARN( proclog, "Valid return" ); + ControlAccess::get_instance()->ChainIsQuitting( fName ); + + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Processor/PrimaryProcessor.hh b/Cpp/Library/Processor/PrimaryProcessor.hh new file mode 100644 index 00000000..0766eddd --- /dev/null +++ b/Cpp/Library/Processor/PrimaryProcessor.hh @@ -0,0 +1,81 @@ +/* + * PrimaryProcessor.hh + * + * Created on: Oct 10, 2012 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_PRIMARYPROCESSOR_HH_ +#define NYMPH_PRIMARYPROCESSOR_HH_ + +#include "Processor.hh" + + +namespace Nymph +{ + + /*! + @class PrimaryProcessor + @author N. S. Oblath + + @brief Base class for PrimaryProcessors. Each processor chain is headed by a PrimaryProcessor. + + @details + + Exceptions thrown during the run by any Processors in this PP's chain are caught by PrimaryProcessor::operator()(). + This class catches any exceptions inheriting from scarab::base_exception. + It also catches QuitChain for situations where the quitting has been requested from outside of the chain (e.g. cancelation from another thread). + + */ + class PrimaryProcessor : public Processor + { + public: + PrimaryProcessor( const std::string& name ); + virtual ~PrimaryProcessor(); + + public: + /// Callable function used by std::thread + void operator()();// ControlAccessPtr control ); // SharedControl is now accessed in Signal::operator(), so we don't have to pass it from signal to slot to signal, etc + + /// Starts the main action of the processor + virtual void Run() = 0; + +// MEMVAR_REF( std::exception_ptr, ExceptionPtr ); + + }; + + +/* + class PrimaryProcessor : public Processor + { + public: + PrimaryProcessor( std::initializer_list< std::string > signals, const std::string& name = "default-primary-processor-name" ); + virtual ~PrimaryProcessor(); + + public: + /// Callable function used by std::thread + void operator()( std::shared_ptr< KTThreadReference > ref, boost::condition_variable& startedCV, bool& startedFlag ); + + /// Starts the main action of the processor + virtual bool Run() = 0; + + std::shared_ptr< KTThreadReference > GetThreadRef(); + + MEMBERVARIABLE( bool, DoBreakpoint ); + + protected: + std::vector< std::string > fSignalsEmitted; + + std::shared_ptr< KTThreadReference > fThreadRef; + + }; + + inline std::shared_ptr< KTThreadReference > PrimaryProcessor::GetThreadRef() + { + return fThreadRef; + } +*/ + +} /* namespace Nymph */ + +#endif /* NYMPH_PRIMARYPROCESSOR_HH_ */ diff --git a/Cpp/Library/Processor/Processor.cc b/Cpp/Library/Processor/Processor.cc new file mode 100644 index 00000000..799a79be --- /dev/null +++ b/Cpp/Library/Processor/Processor.cc @@ -0,0 +1,161 @@ +/* + * Processor.cc + * + * Created on: Jan 5, 2012 + * Author: N.S. Oblath + */ + +#include "Processor.hh" + +#include "ServiceToolbox.hh" + +#include + +namespace Nymph +{ + //KTLOGGER(proclog, "Processor"); + + Processor::Processor( const std::string& name ) : + fName( name ), + fSignals(), + fSlots(), + fServiceToolbox( nullptr ) + { + } + + Processor::~Processor() + {} + + void Processor::RegisterSignal( std::string name, SignalBase* signal ) + { + LDEBUG( processorlog, "Registering signal <" << name << "> in processor <" << fName << ">" ); + fSignals.insert( SigMapVal(name, signal) ); + + // give the signal to any slots that are waiting for it + //auto range = fSlotsWaitingForSignals.equal_range( name ); + //for( auto rangeIt = range.first; rangeIt != range.second; ++rangeIt ) + //{ + // rangeIt->second->SignalsUsed().push_back( signal ); + //} + + return; + } + + void Processor::RegisterSlot( std::string name, SlotBase* slot ) + { + LDEBUG( processorlog, "Registering slot <" << name << "> in processor <" << fName << ">" ); + fSlots.insert( SlotMapVal(name, slot) ); + + /* + // take care of giving signal pointers to the slot, or saving them for later assignment + for( auto signalIt = signals.begin(); signalIt != signals.end(); ++signalIt ) + { + auto signalPtrIt = fSignals.find( *signalIt ); + if( signalPtrIt == fSignals.end() ) + { + // don't have that signal yet, so the slot will wait + fSlotsWaitingForSignals.insert( std::make_pair( *signalIt, slot ) ); + } + else + { + // have the signal; give it to the slot + slot->SignalsUsed().push_back( signalPtrIt->second ); + } + } + */ + + return; + } + + void Processor::ConnectASlot( const std::string& signalName, Processor& processor, const std::string& slotName, int groupNum ) + { + // get the signal and slot pointers + SigMapIt signalIt = fSignals.find(signalName); + if( signalIt == fSignals.end() ) + { + THROW_EXCEPT_HERE( SignalException() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the signal.\n" << + "You may have the signal name wrong." ); +// BOOST_THROW_EXCEPTION( SignalException() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the signal.\n" << +// "You may have the signal name wrong." << eom ); + } + + SlotMapIt slotIt = processor.fSlots.find(slotName); + if( slotIt == processor.fSlots.end() ) + { + THROW_EXCEPT_HERE( SlotException() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the slot." << + "You may have the slot name wrong." ); +// BOOST_THROW_EXCEPTION( SlotException() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the slot." << +// "You may have the slot name wrong." << eom ); + } + + try + { + // make the connection + ConnectSignalToSlot( signalIt->second, slotIt->second, groupNum ); + } + catch( SignalException& e ) + { + THROW_NESTED_EXCEPT_HERE( SignalException() << "Unable to connect signal <" << signalName << "> to slot <" << slotName << "> due to a problem with the signal." << + "\tYou may have the signal name wrong." ); + } + catch( SlotException& e ) + { + THROW_NESTED_EXCEPT_HERE( SlotException() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the slot." << + "\tYou may have the slot name wrong." ); + } + catch( ConnectionException& e ) + { + THROW_NESTED_EXCEPT_HERE( ConnectionException() << "Unable to connect signal <" << signalName << "> to slot <" << slotName << "> due to a problem making the connection." << + "\tCheck that the signatures of the signal and slot match exactly." ); + } + catch( std::exception& e ) + { + THROW_NESTED_EXCEPT_HERE( Exception() << "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> for an unknown reason." ); + } + + LDEBUG(processorlog, "Connected signal <" << this->Name() << ":" << signalName << "> to slot <" << processor.Name() << ":" << slotName << ">"); + + return; + } + + void Processor::ConnectSignalToSlot( SignalBase* signal, SlotBase* slot, int groupNum ) + { + if( ! signal ) + { + THROW_EXCEPT_HERE( SignalException() << "Signal pointer was NULL" ); +// BOOST_THROW_EXCEPTION( SignalException() << "Signal pointer was NULL" << eom ); + } + if( ! slot ) + { + THROW_EXCEPT_HERE( SlotException() << "Slot pointer was NULL" ); +// BOOST_THROW_EXCEPTION( SlotException() << "Slot pointer was NULL" << eom ); + } + + signal->Connect(slot, groupNum); + + return; + } + + bool Processor::GetDoBreakpoint( const std::string& signalName ) + { + SignalBase* signal = fSignals.at(signalName ); + if (signal != nullptr) + { + return signal->GetDoBreakpoint(); + } + THROW_EXCEPT_HERE( Exception() << "Signal <" << signalName << "> was not found" ); + return false; + } + + void Processor::SetDoBreakpoint( const std::string& signalName, bool flag ) + { + SignalBase* signal = fSignals.at(signalName ); + if( signal != nullptr ) + { + return signal->SetDoBreakpoint( flag ); + } + THROW_EXCEPT_HERE( Exception() << "Signal <" << signalName << "> was not found" ); + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Processor/Processor.hh b/Cpp/Library/Processor/Processor.hh new file mode 100644 index 00000000..284de804 --- /dev/null +++ b/Cpp/Library/Processor/Processor.hh @@ -0,0 +1,112 @@ +/** + @file Processor.hh + @brief Base class for processors + @author: N. S. Oblath + @date: Jan 5, 2012 + */ + +#ifndef NYMPH_PROCESSOR_HH_ +#define NYMPH_PROCESSOR_HH_ + +#include "ConfigException.hh" +#include "ProcessorRegistrar.hh" +#include "SignalSlotBase.hh" + +#include "factory.hh" +#include "logger.hh" +#include "param.hh" + +#include + +namespace Nymph +{ + LOGGER(processorlog, "Processor.hh"); + + class ServiceToolbox; + + struct ProcessorException : virtual public Exception {}; + + class Processor + { + protected: + typedef std::map< std::string, SignalBase* > SignalMap; + typedef SignalMap::iterator SigMapIt; + typedef SignalMap::value_type SigMapVal; + + typedef std::map< std::string, SlotBase* > SlotMap; + typedef SlotMap::iterator SlotMapIt; + typedef SlotMap::value_type SlotMapVal; + + public: + Processor( const std::string& name ); + virtual ~Processor(); + + template< class XDerivedProc > + static scarab::registrar< Nymph::Processor, XDerivedProc, const std::string& >* RegisterProcessor( const std::string& name ); + + public: + /// Configure the processor with a param_node + virtual void Configure( const scarab::param_node& node ) = 0; + + /// Utility function for connecting any signal to any slot + static void ConnectSignalToSlot( SignalBase* signal, SlotBase* slot, int groupNum=-1 ); + + /// Connect a signal in this processor to a slot in a different processor + void ConnectASlot( const std::string& signalName, Processor& processor, const std::string& slotName, int groupNum=-1 ); + /// Connect a signal in a different processor to a slot in this processor + void ConnectASignal( Processor& processor, const std::string& signalName, const std::string& slotName, int groupNum=-1 ); + + /// Register a signal object with this processor + void RegisterSignal( std::string name, SignalBase* signal ); + + /// Register a slot object with this processor + void RegisterSlot( std::string name, SlotBase* slot ); + + bool GetDoBreakpoint( const std::string& signalName ); + void SetDoBreakpoint( const std::string& signalName, bool flag ); + + MEMVAR_REF( std::string, Name ); + + MEMVAR_REF_CONST( SignalMap, Signals ); + MEMVAR_REF_CONST( SlotMap, Slots ); + + MEMVAR( ServiceToolbox*, ServiceToolbox ); + + // this is used only to hold pointers to slots waiting for signals + // the keys are the names of the signals being waited for, and the values are the slot pointers + //typedef std::multimap< std::string, SlotBase* > WaitingSlotMap; + //MEMVAR_REF_CONST( WaitingSlotMap, SlotsWaitingForSignals ); + + }; + + + inline void Processor::ConnectASignal(Processor& processor, const std::string& signalName, const std::string& slotName, int groupNum ) + { + processor.ConnectASlot( signalName, *this, slotName, groupNum ); + return; + } + + template< class XDerivedProc > + scarab::registrar< Processor, XDerivedProc, const std::string& >* Processor::RegisterProcessor( const std::string& name ) + { + return new scarab::registrar< Processor, XDerivedProc, const std::string& >( name ); + } + +#define REGISTER_PROCESSOR_NONAMESPACE(proc_class, proc_name) \ + static ::Nymph::ProcessorRegistrar< proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); + +#define REGISTER_PROCESSOR_NAMESPACE(proc_namespace, proc_class, proc_name) \ + static ::Nymph::ProcessorRegistrar< ::proc_namespace::proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); + +// Macro overloading trick from here: https://stackoverflow.com/a/11763277 +#define GET_MACRO(_1, _2, _3, NAME, ...) NAME +/// Processors defined in a namespace need to specify the namespace first: +/// [no namespace]: REGISTER_PROCESSOR( [class], [name in quotes] ) +/// [with namespace]: REGISTER_PROCESSOR( [namespace], [class], [name in quotes] ) +#define REGISTER_PROCESSOR(...) GET_MACRO(__VA_ARGS__, REGISTER_PROCESSOR_NAMESPACE, REGISTER_PROCESSOR_NONAMESPACE, )(__VA_ARGS__) + +} /* namespace Nymph */ + +//extern scarab::indexed_factory< std::string, Nymph::Processor, const std::string& > ex_shared_proc_factory; + +#endif /* NYMPH_PROCESSOR_HH_ */ diff --git a/Cpp/Library/Processor/ProcessorRegistrar.hh b/Cpp/Library/Processor/ProcessorRegistrar.hh new file mode 100644 index 00000000..2238e178 --- /dev/null +++ b/Cpp/Library/Processor/ProcessorRegistrar.hh @@ -0,0 +1,69 @@ +/** + @file ProcessorRegistrar.hh + @brief Contains ProcessorRegistrar + @details Customized indexed_factory registrar for Processors. + @author: N. S. Oblath + @date: Oct 3, 2025 + */ + +#ifndef NYMPH_PROCESSORREGISTRAR_HH_ +#define NYMPH_PROCESSORREGISTRAR_HH_ + +//#include "Processor.hh" +#include "ProcessorToolbox.hh" + +#include "indexed_factory.hh" + + +namespace Nymph +{ + class Processor; + + // registrar + + template< class XDerivedType, typename ... XArgs > + class ProcessorRegistrar : public scarab::base_registrar< Processor, XArgs... > + { + public: + ProcessorRegistrar( const std::string& a_index ); + virtual ~ProcessorRegistrar(); + + void register_class() const; + + Processor* create( XArgs ... args ) const; + + protected: + std::string f_index; + }; + + + template< class XDerivedType, typename ... XArgs > + ProcessorRegistrar< XDerivedType, XArgs... >::ProcessorRegistrar( const std::string& a_index ) : + scarab::base_registrar< Processor, XArgs... >(), + f_index( a_index ) + { + register_class(); + } + + template< class XDerivedType, typename ... XArgs > + ProcessorRegistrar< XDerivedType, XArgs... >::~ProcessorRegistrar() + { + ProcessorToolbox::GetProcFactory()->remove_class( f_index ); + } + + template< class XDerivedType, typename ... XArgs > + void ProcessorRegistrar< XDerivedType, XArgs... >::register_class() const + { + ProcessorToolbox::GetProcFactory()->register_class( f_index, this ); + return; + } + + template< class XDerivedType, typename ... XArgs > + Processor* ProcessorRegistrar< XDerivedType, XArgs... >::create( XArgs... args ) const + { + return dynamic_cast< Processor* >( new XDerivedType( args... ) ); + } + +} + +#endif /* NYMPH_PROCESSORREGISTRAR_HH_ */ diff --git a/Cpp/Library/Processor/ProcessorToolbox.cc b/Cpp/Library/Processor/ProcessorToolbox.cc new file mode 100644 index 00000000..e4d553b2 --- /dev/null +++ b/Cpp/Library/Processor/ProcessorToolbox.cc @@ -0,0 +1,413 @@ +/* + * ProcessorToolbox.cc + * + * Created on: Sep 27, 2012 + * Author: N.S. Oblath + */ + +#include "ProcessorToolbox.hh" + +#include "PrimaryProcessor.hh" +#include "ServiceToolbox.hh" + +#include "factory.hh" +#include "logger.hh" +#include "param_codec.hh" + +#include + +using std::string; + +namespace Nymph +{ + LOGGER(proclog, "ProcessorToolbox"); + + ProcessorToolbox::ProcessorToolbox( const std::string& name ) : + fProcFactory( scarab::factory< Processor, const std::string& >::get_instance() ), + fProcMap(), + fServiceToolbox( nullptr ) + { + } + + ProcessorToolbox::~ProcessorToolbox() + {} + + scarab::factory< Processor, const std::string& >* ProcessorToolbox::GetProcFactory() + { + return scarab::factory< Processor, const std::string& >::get_instance(); + } + + void ProcessorToolbox::Configure( const scarab::param_node& node ) + { + LPROG( proclog, "Configuring processor toolbox" ); + + // Deal with "processor" blocks + if( ! node.has("processors") ) + { + LWARN( proclog, "No processors were specified" ); + } + else + { + ConfigureProcessors( node["processors"].as_array() ); + } + + // Then deal with connections" + if( ! node.has("connections") ) + { + LWARN( proclog, "No connections were specified" ); + } + else + { + ConfigureConnections( node["connections"].as_array() ); + } + + return; + } + + + void ProcessorToolbox::ConfigureProcessors( const scarab::param_array& array ) + { + for( auto procIt = array.begin(); procIt != array.end(); ++procIt ) + { + if( ! procIt->is_node() ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Invalid processor entry (not a node): " << *procIt ); + } + const scarab::param_node& procNode = procIt->as_node(); + + if( ! procNode.has("type") ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to create processor: no processor type given" ); + } + string procType = procNode["type"]().as_string(); + + string procName; + if( ! procNode.has("name") ) + { + LINFO(proclog, "No name given for processor of type <" << procType << ">; using type as name."); + procName = procType; + } + else + { + procName = procNode["name"]().as_string(); + } + + std::shared_ptr< Processor > newProc ( fProcFactory->create(procType, procName) ); + if( newProc == nullptr ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to create processor of type <" << procType << ">" ); + } + newProc->SetServiceToolbox( fServiceToolbox ); + + LDEBUG( proclog, "Attempting to configure processor <" << procName << ">" ); + try + { + newProc->Configure(procNode); + } + catch( scarab::base_exception& e ) + { + THROW_NESTED_EXCEPT_HERE( Exception() << "An error occurred while configuring processor <" << procName << ">" ); + } + + if( ! AddProcessor( procName, newProc ) ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to add processor <" << procName << ">" ); + } + } + + return; + } + + + void ProcessorToolbox::ConfigureConnections( const scarab::param_array& array ) + { + for( auto connIt = array.begin(); connIt != array.end(); ++connIt ) + { + if( ! connIt->is_node() ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Invalid connection entry: not a node" << *connIt ); + } + const scarab::param_node& connNode = connIt->as_node(); + + if( ! connNode.has("signal") || ! connNode.has("slot") ) + { + std::string sigSlotMessage( "signal = " ); + if (connNode.has("signal")) + { + sigSlotMessage += connNode["signal"]().as_string(); + } + else + { + sigSlotMessage += "MISSING"; + } + + sigSlotMessage += "\nslot = "; + if (connNode.has("slot")) + { + sigSlotMessage += connNode["slot"]().as_string(); + } + else + { + sigSlotMessage += "MISSING"; + } + THROW_EXCEPT_HERE( ConfigException(array) << "Signal/Slot connection information is incomplete!\n" << sigSlotMessage ); + } + + bool connReturn = false; + if( connNode.has("order") ) + { + connReturn = MakeConnection( connNode["signal"]().as_string(), connNode["slot"]().as_string(), connNode["order"]().as_int() ); + } + else + { + connReturn = MakeConnection( connNode["signal"]().as_string(), connNode["slot"]().as_string() ); + } + if( ! connReturn ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to make connection <" << connNode["signal"]() << "> --> <" << connNode["slot"]() << ">" ); + } + + if( connNode.has("breakpoint") ) + { + if (! SetBreakpoint( connNode["slot"]().as_string() ) ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to set breakpoint on <" << connNode["slot"]() ); + } + } + + LINFO( proclog, "Signal <" << connNode["signal"]() << "> connected to slot <" << connNode["slot"]() << ">" ); + } + + return; + } + + std::shared_ptr< Processor > ProcessorToolbox::GetProcessor( const std::string& procName ) + { + ProcMapIt it = fProcMap.find( procName ); + if( it == fProcMap.end() ) + { + LWARN( proclog, "Processor <" << procName << "> was not found." ); + return nullptr; + } + return it->second.fProc; + } + + const std::shared_ptr< Processor > ProcessorToolbox::GetProcessor( const std::string& procName ) const + { + ProcMapCIt it = fProcMap.find( procName ); + if (it == fProcMap.end()) + { + LWARN( proclog, "Processor <" << procName << "> was not found." ); + return nullptr; + } + return it->second.fProc; + } + + bool ProcessorToolbox::AddProcessor( const std::string& procName, std::shared_ptr< Processor > proc ) + { + ProcMapIt it = fProcMap.find( procName); + if( it == fProcMap.end() ) + { + ProcessorInfo pInfo; + pInfo.fProc = proc; + fProcMap.insert( ProcMapValue(procName, pInfo) ); + LDEBUG( proclog, "Added processor <" << procName << "> (a.k.a. " << proc->Name() << ")" ); + return true; + } + LWARN( proclog, "Processor <" << procName << "> already exists; new processor was not added." ); + return false; + } + + bool ProcessorToolbox::AddProcessor( const std::string& procType, const std::string& procName ) + { + ProcMapIt it = fProcMap.find( procName ); + if( it == fProcMap.end() ) + { + std::shared_ptr< Processor > newProc ( fProcFactory->create(procType, procType) ); + if( newProc == nullptr ) + { + LERROR( proclog, "Unable to create processor of type <" << procType << ">" ); + return false; + } + if( ! AddProcessor(procName, newProc) ) + { + LERROR( proclog, "Unable to add processor <" << procName << ">" ); + return false; + } + return true; + } + LWARN( proclog, "Processor <" << procName << "> already exists; new processor was not added." ); + return false; + } + + bool ProcessorToolbox::RemoveProcessor( const std::string& procName ) + { + std::shared_ptr< Processor > procToRemove = ReleaseProcessor( procName ); + if( procToRemove == nullptr ) + { + return false; + } + LDEBUG( proclog, "Processor <" << procName << "> deleted." ); + return true; + } + + std::shared_ptr< Processor > ProcessorToolbox::ReleaseProcessor( const std::string& procName ) + { + ProcMapIt it = fProcMap.find( procName ); + if( it == fProcMap.end() ) + { + LWARN( proclog, "Processor <" << procName << "> was not found." ); + return nullptr; + } + std::shared_ptr< Processor > procToRelease = it->second.fProc; + fProcMap.erase( it ); + return procToRelease; + } + + void ProcessorToolbox::ClearProcessors() + { + fProcMap.clear(); + return; + } + + + bool ProcessorToolbox::MakeConnection( const std::string& signal, const std::string& slot, int order ) + { + string signalProcName, signalName; + if( ! ParseSignalSlotName( signal, signalProcName, signalName ) ) + { + LERROR( proclog, "Unable to parse signal name: <" << signal << ">" ); + return false; + } + + string slotProcName, slotName; + if( ! ParseSignalSlotName( slot, slotProcName, slotName ) ) + { + LERROR( proclog, "Unable to parse slot name: <" << slot << ">" ); + return false; + } + + return MakeConnection( signalProcName, signalName, slotProcName, slotName, order ); + } + + bool ProcessorToolbox::MakeConnection( const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order ) + { + std::shared_ptr< Processor > signalProc = GetProcessor( signalProcName ); + if( signalProc == nullptr ) + { + LERROR(proclog, "Processor named <" << signalProcName << "> was not found!"); + return false; + } + + std::shared_ptr< Processor > slotProc = GetProcessor( slotProcName ); + if( slotProc == nullptr ) + { + LERROR( proclog, "Processor named <" << slotProcName << "> was not found!" ); + return false; + } + + try + { + if( order != std::numeric_limits< int >::min() ) + { + signalProc->ConnectASlot( signalName, *slotProc.get(), slotName, order ); + } + else + { + signalProc->ConnectASlot(signalName, *slotProc.get(), slotName); + } + } + catch( scarab::base_exception& e ) + { + LERROR( proclog, "An error occurred while connecting signals and slots:\n" + << "\tSignal " << signalName << " from processor " << signalProcName << " (a.k.a. " << signalProc->Name() << ")" << '\n' + << "\tSlot " << slotName << " from processor " << slotProcName << " (a.k.a. " << slotProc->Name() << ")" << '\n' ); + PrintException( e ); + return false; + } + + return true; + } + + bool ProcessorToolbox::SetBreakpoint( const std::string& signal ) + { + string signalProcName, signalName; + if(! ParseSignalSlotName( signal, signalProcName, signalName ) ) + { + LERROR(proclog, "Unable to parse signal name: <" << signal << ">"); + return false; + } + + return SetBreakpoint( signalProcName, signalName ); + } + + bool ProcessorToolbox::SetBreakpoint( const std::string& signalProcName, const std::string& signalName ) + { + std::shared_ptr< Processor > signalProc = GetProcessor( signalProcName ); + if( signalProc == nullptr ) + { + LERROR( proclog, "Processor named <" << signalProcName << "> was not found!" ); + return false; + } + + try + { + signalProc->SetDoBreakpoint( signalName, true ); + return true; + } + catch( scarab::base_exception& e ) + { + LERROR( proclog, "Unable to set breakpoint: " ); + PrintException( e ); + return false; + } + } + + bool ProcessorToolbox::RemoveBreakpoint( const std::string& signal ) + { + string signalProcName, signalName; + if( ! ParseSignalSlotName( signal, signalProcName, signalName ) ) + { + LERROR( proclog, "Unable to parse signal name: <" << signal << ">" ); + return false; + } + + return RemoveBreakpoint( signalProcName, signalName ); + } + + bool ProcessorToolbox::RemoveBreakpoint( const std::string& signalProcName, const std::string& signalName ) + { + std::shared_ptr< Processor > signalProc = GetProcessor( signalProcName ); + if( signalProc == nullptr ) + { + LERROR(proclog, "Processor named <" << signalProcName << "> was not found!"); + return false; + } + + try + { + signalProc->SetDoBreakpoint( signalName, false ); + return true; + } + catch( scarab::base_exception& e ) + { + LERROR( proclog, "Unable to set breakpoint: " ); + PrintException( e ); + return false; + } + } + + bool ProcessorToolbox::ParseSignalSlotName( const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot ) const + { + size_t sepPos = toParse.find_first_of( fSigSlotNameSep ); + if( sepPos == string::npos ) + { + LERROR( proclog, "Unable to find separator between processor and signal/slot name in <" << toParse << ">" ); + return false; + } + nameOfProc = toParse.substr( 0, sepPos ); + nameOfSigSlot = toParse.substr( sepPos + 1 ); + return true; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Processor/ProcessorToolbox.hh b/Cpp/Library/Processor/ProcessorToolbox.hh new file mode 100644 index 00000000..7e7fb99c --- /dev/null +++ b/Cpp/Library/Processor/ProcessorToolbox.hh @@ -0,0 +1,195 @@ +/** + @file ProcessorToolbox.hh + @brief Contains ProcessorToolbox + @details Manages processors requested by the user at run time. + @author: N. S. Oblath + @date: Sep 27, 2012 + */ + +#ifndef NYMPH_PROCESSORTOOLBOX_HH_ +#define NYMPH_PROCESSORTOOLBOX_HH_ + +#include "ControlAccess.hh" +#include "MemberVariable.hh" + +#include "factory.hh" +#include "param.hh" + +#include +#include +#include + + +namespace Nymph +{ + class PrimaryProcessor; + class Processor; + class ServiceToolbox; + + /*! + @class ProcessorToolbox + @author N. S. Oblath + + @brief Manages processors requested by the user at run time. + + @details + ProcessorToolbox allows the user to setup an application at runtime. + + The user chooses the processors to be used, how they're linked with signals and slots, and what is used to "run" + the program via a configuration file. + + While this does result in longer configuration files, it drastically simplifies the space of executables that are needed. + + The following order should be used for configuring the processor toolbox: +
      +
    1. Create processors
    2. +
    3. Connect signals and slots
    4. +
    5. Create the run queue
    6. +
    + + Available (nested) configuration values: +
  • processors (array of objects) -- create a processor; each object in the array should consist of: +
      +
    • type -- string specifying the processor type (matches the string given to the Registrar, which should be specified before the class implementation in each processor's .cc file).
    • +
    • name -- string giving the individual processor a name so that multiple processors of the same type can be created.
    • +
    +
  • +
  • connection (array of objects) -- connect a signal to a slot; each object should consist of: +
      +
    • signal -- proc-name:signal-name; name (i.e. the name given in the array of processors above) of the processor, and the signal that will be emitted.
    • +
    • slot -- proc-name:slot-name
    • ; name of the processor with the slot that will receive the signal. +
    • group-order -- (optional) integer specifying the order in which slots should be called. +
    +
  • + */ + class ProcessorToolbox + { + protected: + typedef std::unique_lock< std::mutex > unique_lock; + + public: + ProcessorToolbox( const std::string& name = "processor-toolbox" ); + virtual ~ProcessorToolbox(); + + // This was implemented to avoid the duplicate factory problem when accessing the factory from the Pybindings + static scarab::factory< Processor, const std::string& >* GetProcFactory(); + + protected: + scarab::factory< Processor, const std::string& >* fProcFactory; // singleton; not owned by ProcessorToolbox + + + public: + /// Configure the toolbox: create the processors; connnect signals and slots; and setup the run queue. + void Configure( const scarab::param_node& node ); + + protected: + struct ProcessorInfo + { + std::shared_ptr< Processor > fProc; + }; + typedef std::map< std::string, ProcessorInfo > ProcessorMap; + typedef ProcessorMap::iterator ProcMapIt; + typedef ProcessorMap::const_iterator ProcMapCIt; + typedef ProcessorMap::value_type ProcMapValue; + + public: + /// Create processors and configure each according to the `processors` configuration block + void ConfigureProcessors( const scarab::param_array& node ); + + /// Get a pointer to a processor in the toolbox + std::shared_ptr< Processor > GetProcessor( const std::string& procName ); + /// Get a pointer to a processor in the toolbox + const std::shared_ptr< Processor > GetProcessor( const std::string& procName ) const; + + /// Add a processor to the toolbox + /// Toolbox takes ownership of the processor + bool AddProcessor( const std::string& procName, std::shared_ptr< Processor > proc ); + bool AddProcessor( const std::string& procType, const std::string& procName ); + + /// Check whether a processor already has a processor with a given name + bool HasProcessor( const std::string& procName ) const; + /// Check whether a processor could build a processor of a given type (i.e. does the factory know about this processor?) + bool CouldBuild( const std::string& procType ) const; + + /// Remove a processor from the toolbox + bool RemoveProcessor( const std::string& procName ); + + /// Remove a processor from the toolbox and return it to the user + /// Ownership is passed to the user + std::shared_ptr< Processor > ReleaseProcessor( const std::string& procName ); + + /// Remove all processors from the toolbox + /// Also clears the run queue + void ClearProcessors(); + + const scarab::factory< Processor, const std::string& >* ProcFactory() const; + + protected: + ProcessorMap fProcMap; + + + public: + // for the MakeConnection overloading, extra overloading is used instead of default parameters so that the python interface works + + /// Make connections between processors according to the `connections` configuration block + void ConfigureConnections( const scarab::param_array& node ); + + /// Make a connection between the signal from one processor and the slot from another processor + /// Both processors should already have been added to the Toolbox + /// Signal and slot strings should be formatted as: [processor name]:[signal/slot name] + bool MakeConnection(const std::string& signal, const std::string& slot, int order); + bool MakeConnection(const std::string& signal, const std::string& slot); + + /// Make a connection between the signal from one processor and the slot from another processor + /// Both processors should already have been added to the Toolbox + bool MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order); + bool MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName); + + /// Set a breakpoint on a signal + /// Slot string should be formatted as: [processor name]:[signal name] + bool SetBreakpoint( const std::string& signal ); + /// Set a breakpoint on a signal + bool SetBreakpoint( const std::string& signalProcName, const std::string& signalName ); + + /// Remove a breakpoint from a signal + /// Slot string should be formatted as: [processor name]:[signal name] + bool RemoveBreakpoint( const std::string& signal ); + /// Remove a breakpoint from a signal + bool RemoveBreakpoint( const std::string& signalProcName, const std::string& signalName ); + + protected: + bool ParseSignalSlotName( const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot ) const; + static const char fSigSlotNameSep = ':'; + + public: + MEMVAR(ServiceToolbox*, ServiceToolbox); + + }; + + inline bool ProcessorToolbox::HasProcessor( const std::string& procName ) const + { + return fProcMap.count( procName ) == 1; + } + + inline bool ProcessorToolbox::CouldBuild( const std::string& procType ) const + { + return fProcFactory->has_class( procType ); + } + + inline bool ProcessorToolbox::MakeConnection(const std::string& signal, const std::string& slot) + { + return MakeConnection(signal, slot, std::numeric_limits< int >::min()); + } + + inline bool ProcessorToolbox::MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName) + { + return MakeConnection(signalProcName, signalName, slotProcName, slotName, std::numeric_limits< int >::min()); + } + + inline const scarab::factory< Processor, const std::string& >* ProcessorToolbox::ProcFactory() const + { + return fProcFactory; + } + +} /* namespace Nymph */ +#endif /* NYMPH_PROCESSORTOOLBOX_HH_ */ diff --git a/Cpp/Library/Processor/QuitChain.hh b/Cpp/Library/Processor/QuitChain.hh new file mode 100644 index 00000000..fff9b138 --- /dev/null +++ b/Cpp/Library/Processor/QuitChain.hh @@ -0,0 +1,35 @@ +/* + * QuitChain.hh + * + * Created on: Oct 22, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_QUITCHAIN_HH_ +#define NYMPH_QUITCHAIN_HH_ + +#include "base_exception.hh" + +namespace Nymph +{ + /*! + @class QuitChain + @author N. S. Oblath + + @brief Thrown from a Processor chain to communicate within the chain that the chain + should stop running from the top (i.e. from the Primary Processor) + + */ + class QuitChain : public scarab::typed_exception< QuitChain > + { + public: + using scarab::typed_exception< QuitChain >::typed_exception; + ~QuitChain() = default; + }; + + #define QUIT_CHAIN throw ::Nymph::QuitChain()(__FILE__, __LINE__) + //THROW_EXCEPT_HERE( ::Nymph::QuitChain() ) + +} /* namespace Nymph */ + +#endif /* NYMPH_QUITCHAIN_HH_ */ diff --git a/Cpp/Library/Processor/Signal.hh b/Cpp/Library/Processor/Signal.hh new file mode 100644 index 00000000..c8e304f7 --- /dev/null +++ b/Cpp/Library/Processor/Signal.hh @@ -0,0 +1,152 @@ +/* + * Signal.hh + * + * Created on: Jan 15, 2013 + * Author: N.S. Oblath + * + * Based on this implementation: https://schneegans.github.io/tutorials/2015/09/20/signal-slot + */ + +#ifndef NYMPH_SIGNAL_HH_ +#define NYMPH_SIGNAL_HH_ + +#include "SignalSlotBase.hh" + +#include "ControlAccess.hh" +#include "QuitChain.hh" + +#include "logger.hh" + + +namespace Nymph +{ + LOGGER( signallog, "Signal" ); + + template< typename... XArgs > + class Slot; + + /*! + @class Signal + @author N. S. Oblath + + @brief A signal object may call multiple slots with the same signature. You can connect functions to the Signal + that will be called when the `emit()` method on the Signal object is invoked. Any argument passed to `emit()` + will be passed to the given functions. + + @details + An owned slot will register itself with the owner. The owner must have a `RegisterSignal()` function that can be called. + + */ + template< typename... XArgs > + class Signal : public SignalBase + { + public: + using signature = void( XArgs... ); + + public: + /// Unowned signal + Signal( const std::string& name ); + /// Owned signal + template< typename XOwner > + Signal( const std::string& name, XOwner* owner ); + Signal( const Signal& ) = delete; + Signal( Signal&& ) = delete; + virtual ~Signal(); + + virtual void Connect( SlotBase* slot, int group = -1 ); + + // calls all connected functions + void Emit( XArgs... args ); + void operator()( XArgs... args ); + }; + + + //******************* + // Implementations + //******************* + + template< typename... XArgs > + Signal< XArgs... >::Signal( const std::string& name ) : + SignalBase( name ) + {} + + template< typename... XArgs > + template< typename XOwner > + inline Signal< XArgs... >::Signal( const std::string& name, XOwner* owner ) : + SignalBase( name, owner ) + {} + + template< typename... XArgs > + Signal< XArgs... >::~Signal() + {} + + template< typename... XArgs > + void Signal< XArgs... >::Connect( SlotBase* slot, int group ) + { + if( fConnections.count( slot ) != 0 ) + { + LWARN( signallog, "Signal <" << fName << "> is already connected to slot <" << slot->Name() << ">" ); + return; + } + + // ensure that the slot is of the correct type + if( ! slot->MatchesTo( this ) ) + { + THROW_EXCEPT_HERE( ConnectionException() << "Trying to connect signal <" << fName << "> to slot <" << slot->Name() << ">, but cannot make the connection:\n" << + "\tUnable to cast from SlotBase to this signal's derived type.\n" << + "\tArgument types do not match" ); + } + + AddConnection( slot, group ); + + return; + } + + // calls all connected functions + template< typename... XArgs > + inline void Signal< XArgs... >::Emit( XArgs... args ) + { + (*this)( args... ); + return; + } + + template< typename... XArgs > + inline void Signal< XArgs... >::operator()( XArgs... args ) + { + ControlAccess* control = ControlAccess::get_instance(); + + // Check for whether we need to quit from external input: + // - if we're canceled, then quit the thread + // - if we're at a break, then wait to continue; + // once we continue, if we need to quit, then do so + if( control->IsCanceled() || (control->IsAtBreak() && ! control->WaitToContinue()) ) + { + QUIT_CHAIN; // throws QuitThread; should be caught by PrimaryProcessor::operator() + } + + // Check for whether this signal emission has a breakpoint + if( fDoBreakpoint ) + { + // do the break + LDEBUG( signallog, "Doing breakpoint; will now break and return" ); + control->BreakAndReturn( args... ); + //control->Break(); + // wait to continue; once we continue, if we need to quit, then do so + LDEBUG( signallog, "Will now wait to continue" ); + if( ! control->WaitToContinue() ) + { + QUIT_CHAIN; // throws QuitThread; should be caught by PrimaryProcessor::operator() + } + } + + // Emit signal by calling all connected slots + for( auto connection : fConnections ) + { + static_cast< Slot< XArgs... >* >(connection)->operator()( args... ); + } + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_SIGNAL_HH_ */ diff --git a/Cpp/Library/Processor/SignalSlotBase.cc b/Cpp/Library/Processor/SignalSlotBase.cc new file mode 100644 index 00000000..37ba9d43 --- /dev/null +++ b/Cpp/Library/Processor/SignalSlotBase.cc @@ -0,0 +1,53 @@ +/* + * SignalSlotBase.cc + * + * Created on: Sept 7, 2019 + * Author: N.S. Oblath + */ + +#include "SignalSlotBase.hh" + +namespace Nymph +{ + SlotBase::SlotBase( const std::string& name ) : + fName( name ), + fConnections() + {} + + SlotBase::~SlotBase() + { + DisconnectAll(); + } + + void SlotBase::DisconnectAll() + { + while( ! fConnections.empty() ) + { + Disconnect( *fConnections.begin() ); + } + return; + } + + SignalBase::SignalBase( const std::string& name ) : + fName( name ), + fConnections(), + fDoBreakpoint( false ) + {} + + SignalBase::~SignalBase() + { + DisconnectAll(); + } + + // disconnects all previously connected functions + void SignalBase::DisconnectAll() + { + while( ! fConnections.empty() ) + { + Disconnect( *fConnections.begin() ); + } + return; + } + +} + diff --git a/Cpp/Library/Processor/SignalSlotBase.hh b/Cpp/Library/Processor/SignalSlotBase.hh new file mode 100644 index 00000000..77c0ee07 --- /dev/null +++ b/Cpp/Library/Processor/SignalSlotBase.hh @@ -0,0 +1,157 @@ +/* + * SignalSlotBase.hh + * + * Created on: Feb 25, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_SIGNALSLOTBASE_HH_ +#define NYMPH_SIGNALSLOTBASE_HH_ + +//#include "ControlAccess.hh" +#include "Exception.hh" +#include "MemberVariable.hh" + +#include +#include +#include +#include + +namespace Nymph +{ + + class SignalException : public scarab::typed_exception< SignalException > + { + public: + using scarab::typed_exception< SignalException >::typed_exception; + ~SignalException() = default; + }; + + class SlotException : public scarab::typed_exception< SlotException > + { + public: + using scarab::typed_exception< SlotException >::typed_exception; + ~SlotException() = default; + }; + + class ConnectionException : public scarab::typed_exception< ConnectionException > + { + public: + using scarab::typed_exception< ConnectionException >::typed_exception; + ~ConnectionException() = default; + }; + + + class SignalBase; + + /*! + @class SlotBase + @author N. S. Oblath + + @brief Provides the basic slot interface, including connect/disconnect, and storage of connections. + + */ + class SlotBase + { + public: + SlotBase( const std::string& name ); + template< typename XOwner > + SlotBase( const std::string& name, XOwner* owner ); + virtual ~SlotBase(); + + virtual void ConnectTo( SignalBase* signal, int group = -1 ) = 0; + virtual bool MatchesTo( SignalBase* signal) = 0; + + void Disconnect( SignalBase* signal); + void DisconnectAll(); + + MEMVAR_REF( std::string, Name ); + + typedef std::set< SignalBase* > SignalConnections; // to get around the problem of having a comma inside a macro function argument + MEMVAR_REF_MUTABLE( SignalConnections, Connections ); + + protected: + friend class SignalBase; + virtual void AddConnection( SignalBase* signal ); + }; + + + /*! + @class SignalBase + @author N. S. Oblath + + @brief Provides the basic signal interface, including connect/disconnect, storage of connections, and the option to perform a breakpoint. + + */ + class SignalBase + { + public: + SignalBase( const std::string& name ); + template< typename XOwner > + SignalBase( const std::string& name, XOwner* owner ); + virtual ~SignalBase(); + + virtual void Connect( SlotBase* slot, int group = -1 ) = 0; + + void Disconnect( SlotBase* slot ); + void DisconnectAll(); + + MEMVAR_REF( std::string, Name ); + + typedef std::set< SlotBase* > SlotConnections; // to get around the problem of having a comma inside a macro function argument + MEMVAR_REF_MUTABLE_CONST( SlotConnections, Connections ); + + MEMVAR( bool, DoBreakpoint ); + + protected: + friend class SlotBase; + virtual void AddConnection( SlotBase* slot, int group ); + }; + + template< typename XOwner > + SlotBase::SlotBase( const std::string& name, XOwner* owner ) : + fName( name ), + fConnections() + { + owner->RegisterSlot( name, this ); + } + + inline void SlotBase::AddConnection( SignalBase* signal ) + { + fConnections.insert( signal ); + return; + } + + inline void SlotBase::Disconnect( SignalBase* signal ) + { + signal->Disconnect( this ); + return; + } + + template< typename XOwner > + SignalBase::SignalBase( const std::string& name, XOwner* owner ) : + fName( name ), + fConnections(), + fDoBreakpoint( false ) + { + owner->RegisterSignal( name, this ); + } + + inline void SignalBase::AddConnection( SlotBase* slot, int group ) + { + fConnections.insert( slot ); + slot->AddConnection( this ); + return; + } + + // disconnects a previously connected function + inline void SignalBase::Disconnect( SlotBase* slot ) + { + slot->fConnections.erase( this ); + fConnections.erase( slot ); + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_SIGNALSLOTBASE_HH_ */ diff --git a/Cpp/Library/Processor/Slot.hh b/Cpp/Library/Processor/Slot.hh new file mode 100644 index 00000000..6005dcdf --- /dev/null +++ b/Cpp/Library/Processor/Slot.hh @@ -0,0 +1,159 @@ +/* + * Slot.hh + * + * Created on: Jan 13, 2013 + * Author: N.S. Oblath + * + * Based on this implementation: https://schneegans.github.io/tutorials/2015/09/20/signal-slot + */ + +#ifndef NYMPH_SLOT_HH_ +#define NYMPH_SLOT_HH_ + +#include "SignalSlotBase.hh" + +//#include "Exception.hh" + +#include "logger.hh" + +//#include +#include + +namespace Nymph +{ + LOGGER(slotlog, "Slot"); + + template< typename... XArgs > + class Signal; + + // Type XOwner is the class that owns the Slot object + /*! + @class Slot + @author N. S. Oblath + + @brief A Slot is connected to and called by a Signal with the same signature. Slots call a particular function when they're triggered (usually by a signal). + + @details + Usage: + The different constructors allow different relationships between the owner of the slot (if there is one) and the owner of the function to be called (if there is one) + The function should have the signature void (Args). + */ + template< typename... XArgs > + class Slot : public SlotBase + { + public: + using signature = void( XArgs... ); + + public: + /// Unowned slot + Slot( const std::string& name, const std::function< signature >& func ); + /// Owned slot, non-const member function + template< typename XOwner > + Slot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) ); + /// Owned slot, const member function + template< typename XOwner > + Slot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) const ); + /// Owned slot, generic function + template< typename XOwner > + Slot( const std::string& name, XOwner* owner, const std::function< signature >& func ); + /// Owned slot, non-const member fuction of another class + template< typename XOwner, typename XFuncClass > + Slot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) ); + /// Owned slot, const member fuction of another class + template< typename XOwner, typename XFuncClass > + Slot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) const ); + Slot( const Slot& ) = delete; + Slot( Slot&& ) = delete; + virtual ~Slot(); + + void ConnectTo( SignalBase* signal, int group = -1 ) override; + bool MatchesTo( SignalBase* signal) override; + + /// execute fFunction + void operator()( XArgs... args ); + + MEMVAR_REF( std::function< signature >, Function ); + + }; + + //******************* + // Implementations + //******************* + + // Slot + + template< typename... XArgs > + Slot< XArgs... >::Slot( const std::string& name, const std::function< signature >& func ) : + SlotBase( name ), + fFunction( func ) + {} + + template< typename... XArgs > + template< typename XOwner > + Slot< XArgs... >::Slot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) ) : + SlotBase( name, owner ), + fFunction( [func, owner]( XArgs... args ){ return (owner->*func)(args...);} ) + {} + + template< typename... XArgs > + template< typename XOwner > + Slot< XArgs... >::Slot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) const ) : + SlotBase( name, owner ), + fFunction( [func, owner]( XArgs... args ){ return (owner->*func)(args...);} ) + {} + + template< typename... XArgs > + template< typename XOwner > + Slot< XArgs... >::Slot( const std::string& name, XOwner* owner, const std::function< signature >& func ) : + SlotBase( name, owner ), + fFunction( func ) + {} + + template< typename... XArgs > + template< typename XOwner, typename XFuncClass > + Slot< XArgs... >::Slot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) ) : + SlotBase( name, owner ), + fFunction( [func, inst]( XArgs... args ){ return (inst->*func)(args...);} ) + {} + + template< typename... XArgs > + template< typename XOwner, typename XFuncClass > + Slot< XArgs... >::Slot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) const ) : + SlotBase( name, owner ), + fFunction( [func, inst]( XArgs... args ){ return (inst->*func)(args...);} ) + {} + + template< typename... XArgs > + Slot< XArgs... >::~Slot() + {} + + template< typename... XArgs > + void Slot< XArgs... >::ConnectTo( SignalBase* signal, int group ) + { + if( fConnections.count( signal ) != 0 ) + { + LWARN( slotlog, "Slot <" << fName << "> is already has connection to signal <" << signal->Name() << ">" ); + return; + } + + signal->Connect( this, group ); + + return; + } + + template< typename... XArgs > + bool Slot< XArgs... >::MatchesTo( SignalBase* signal ) + { + return dynamic_cast< Signal< XArgs... >* >( signal ); + } + + template< typename... XArgs > + inline void Slot< XArgs... >::operator()( XArgs... args ) + { + fFunction( args... ); + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_SLOT_HH_ */ diff --git a/Cpp/Library/Service/Service.cc b/Cpp/Library/Service/Service.cc new file mode 100644 index 00000000..286d72ee --- /dev/null +++ b/Cpp/Library/Service/Service.cc @@ -0,0 +1,22 @@ +/* + * Service.cc + * + * Created on: Dec 28, 2023 + * Author: N.S. Oblath + */ + +#include "Service.hh" + +namespace Nymph +{ + //KTLOGGER(servicelog, "Service"); + + Service::Service( const std::string& name ) : + fName( name ) + { + } + + Service::~Service() + {} + +} /* namespace Nymph */ diff --git a/Cpp/Library/Service/Service.hh b/Cpp/Library/Service/Service.hh new file mode 100644 index 00000000..424c11d7 --- /dev/null +++ b/Cpp/Library/Service/Service.hh @@ -0,0 +1,61 @@ +/** + @file Service.hh + @brief Base class for processors + @author: N. S. Oblath + @date: Dec 28, 2023 + */ + +#ifndef NYMPH_SERVICE_HH_ +#define NYMPH_SERVICE_HH_ + +#include "Exception.hh" +#include "MemberVariable.hh" + +#include "factory.hh" +#include "param.hh" + +#include + +namespace Nymph +{ + struct ServiceException : virtual public Exception {}; + + class Service + { + public: + Service( const std::string& name ); + virtual ~Service(); + + template< class XDerivedProc > + static scarab::registrar< Nymph::Service, XDerivedProc, const std::string& >* RegisterService( const std::string& name ); + + public: + /// Configure the processor with a param_node + virtual void Configure( const scarab::param_node& node ) = 0; + + MEMVAR_REF( std::string, Name ); + }; + + + template< class XDerivedProc > + scarab::registrar< Service, XDerivedProc, const std::string& >* Service::RegisterService( const std::string& name ) + { + return new scarab::registrar< Service, XDerivedProc, const std::string& >( name ); + } + +#define REGISTER_SERVICE_NONAMESPACE(proc_class, proc_name) \ + static ::scarab::registrar< ::Nymph::Service, proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); + +#define REGISTER_SERVICE_NAMESPACE(proc_namespace, proc_class, proc_name) \ + static ::scarab::registrar< ::Nymph::Service, ::proc_namespace::proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); + +// Macro overloading trick from here: https://stackoverflow.com/a/11763277 +#define GET_MACRO(_1, _2, _3, NAME, ...) NAME +/// Services defined in a namespace need to specify the namespace first: +/// [no namespace]: REGISTER_PROCESSOR( [class], [name in quotes] ) +/// [with namespace]: REGISTER_PROCESSOR( [namespace], [class], [name in quotes] ) +#define REGISTER_SERVICE(...) GET_MACRO(__VA_ARGS__, REGISTER_SERVICE_NAMESPACE, REGISTER_SERVICE_NONAMESPACE, )(__VA_ARGS__) + +} /* namespace Nymph */ + +#endif /* NYMPH_SERVICE_HH_ */ diff --git a/Cpp/Library/Service/ServiceToolbox.cc b/Cpp/Library/Service/ServiceToolbox.cc new file mode 100644 index 00000000..be9c05c2 --- /dev/null +++ b/Cpp/Library/Service/ServiceToolbox.cc @@ -0,0 +1,187 @@ +/* + * ServiceToolbox.cc + * + * Created on: Dec 28, 2023 + * Author: N.S. Oblath + */ + +#include "ServiceToolbox.hh" + +#include "ConfigException.hh" +#include "Service.hh" + +#include "factory.hh" +#include "logger.hh" +#include "param_codec.hh" + +#include + +using std::string; + +namespace Nymph +{ + LOGGER(servicelog, "ServiceToolbox"); + + ServiceToolbox::ServiceToolbox( const std::string& name ) : + fServiceFactory( scarab::factory< Service, const std::string& >::get_instance() ), + fServiceMap() + { + } + + ServiceToolbox::~ServiceToolbox() + {} + + void ServiceToolbox::Configure( const scarab::param_node& node ) + { + LPROG( servicelog, "Configuring service toolbox" ); + + // Deal with "service" blocks + if( ! node.has("services") ) + { + LWARN( servicelog, "No services were specified" ); + } + else + { + ConfigureServices( node["services"].as_array() ); + } + + return; + } + + + void ServiceToolbox::ConfigureServices( const scarab::param_array& array ) + { + for( auto serviceIt = array.begin(); serviceIt != array.end(); ++serviceIt ) + { + if( ! serviceIt->is_node() ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Invalid service entry (not a node): " << *serviceIt ); + } + const scarab::param_node& serviceNode = serviceIt->as_node(); + + if( ! serviceNode.has("type") ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to create service: no service type given" ); + } + string serviceType = serviceNode["type"]().as_string(); + + string serviceName; + if( ! serviceNode.has("name") ) + { + LINFO(servicelog, "No name given for service of type <" << serviceType << ">; using type as name."); + serviceName = serviceType; + } + else + { + serviceName = serviceNode["name"]().as_string(); + } + + std::shared_ptr< Service > newProc ( fServiceFactory->create(serviceType, serviceName) ); + if( newProc == nullptr ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to create service of type <" << serviceType << ">" ); + } + + LDEBUG( servicelog, "Attempting to configure service <" << serviceName << ">" ); + try + { + newProc->Configure(serviceNode); + } + catch( scarab::base_exception& e ) + { + THROW_NESTED_EXCEPT_HERE( Exception() << "An error occurred while configuring service <" << serviceName << ">" ); + } + + if( ! AddService( serviceName, newProc ) ) + { + THROW_EXCEPT_HERE( ConfigException(array) << "Unable to add service <" << serviceName << ">" ); + } + } + + return; + } + + std::shared_ptr< Service > ServiceToolbox::GetService( const std::string& serviceName ) + { + if( auto it = fServiceMap.find( serviceName ); it != fServiceMap.end() ) + { + return it->second.fService; + } + LWARN( servicelog, "Service <" << serviceName << "> was not found." ); + return nullptr; + } + + const std::shared_ptr< Service > ServiceToolbox::GetService( const std::string& serviceName ) const + { + if( auto it = fServiceMap.find( serviceName ); it != fServiceMap.end() ) + { + return it->second.fService; + } + LWARN( servicelog, "Service <" << serviceName << "> was not found." ); + return nullptr; + } + + bool ServiceToolbox::AddService( const std::string& serviceName, std::shared_ptr< Service > service ) + { + if( auto it = fServiceMap.find( serviceName); it == fServiceMap.end() ) + { + ServiceInfo pInfo; + pInfo.fService = service; + fServiceMap.insert( ServiceMap::value_type(serviceName, pInfo) ); + LDEBUG( servicelog, "Added service <" << serviceName << "> (a.k.a. " << service->Name() << ")" ); + return true; + } + LWARN( servicelog, "Service <" << serviceName << "> already exists; new service was not added." ); + return false; + } + + bool ServiceToolbox::AddService( const std::string& serviceType, const std::string& serviceName ) + { + if( auto it = fServiceMap.find( serviceName ); it == fServiceMap.end() ) + { + std::shared_ptr< Service > newProc ( fServiceFactory->create(serviceType, serviceType) ); + if( newProc == nullptr ) + { + LERROR( servicelog, "Unable to create service of type <" << serviceType << ">" ); + return false; + } + if( ! AddService(serviceName, newProc) ) + { + LERROR( servicelog, "Unable to add service <" << serviceName << ">" ); + return false; + } + return true; + } + LWARN( servicelog, "Service <" << serviceName << "> already exists; new service was not added." ); + return false; + } + + bool ServiceToolbox::RemoveService( const std::string& serviceName ) + { + if( auto serviceToRemove = ReleaseService( serviceName ); serviceToRemove != nullptr ) + { + LDEBUG( servicelog, "Service <" << serviceName << "> deleted." ); + return true; + } + return false; + } + + std::shared_ptr< Service > ServiceToolbox::ReleaseService( const std::string& serviceName ) + { + if( auto it = fServiceMap.find( serviceName ); it != fServiceMap.end() ) + { + std::shared_ptr< Service > serviceToRelease = it->second.fService; + fServiceMap.erase( it ); + return serviceToRelease; + } + LWARN( servicelog, "Service <" << serviceName << "> was not found." ); + return nullptr; + } + + void ServiceToolbox::ClearServices() + { + fServiceMap.clear(); + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Service/ServiceToolbox.hh b/Cpp/Library/Service/ServiceToolbox.hh new file mode 100644 index 00000000..29b0c927 --- /dev/null +++ b/Cpp/Library/Service/ServiceToolbox.hh @@ -0,0 +1,114 @@ +/** + @file ServiceToolbox.hh + @brief Contains ServiceToolbox + @details Manages svcessors requested by the user at run time. + @author: N. S. Oblath + @date: Dec 28, 2023 + */ + +#ifndef NYMPH_SERVICETOOLBOX_HH_ +#define NYMPH_SERVICETOOLBOX_HH_ + +#include "factory.hh" +#include "param.hh" + +#include +#include +#include + + +namespace Nymph +{ + class Service; + + /*! + @class ServiceToolbox + @author N. S. Oblath + + @brief Manages services requested by the user at run time. + + @details + ServiceToolbox allows the user to setup an application at runtime. + + The user chooses the services to be used via a configuration file. + + Available (nested) configuration values: +
  • services (array of objects) -- create a service; each object in the array should consist of: +
      +
    • type -- string specifying the svcessor type (matches the string given to the Registrar, which should be specified before the class implementation in each service's .cc file).
    • +
    • name -- string giving the individual svcessor a name so that multiple services of the same type can be created.
    • +
    +
  • + */ + class ServiceToolbox + { + protected: + typedef std::unique_lock< std::mutex > unique_lock; + + public: + ServiceToolbox( const std::string& name = "service-toolbox" ); + virtual ~ServiceToolbox(); + + protected: + scarab::factory< Service, const std::string& >* fServiceFactory; // singleton; not owned by ServiceToolbox + + + public: + /// Configure the toolbox: create the services and configure; connnect signals and slots; and setup the run queue. + void Configure( const scarab::param_node& node ); + + protected: + struct ServiceInfo + { + std::shared_ptr< Service > fService; + }; + typedef std::map< std::string, ServiceInfo > ServiceMap; + + public: + /// Create services and configure each according to the `services` configuration block + void ConfigureServices( const scarab::param_array& node ); + + /// Get a pointer to a service in the toolbox + std::shared_ptr< Service > GetService( const std::string& svcName ); + /// Get a pointer to a service in the toolbox + const std::shared_ptr< Service > GetService( const std::string& svcName ) const; + + /// Add a service to the toolbox + /// Toolbox takes ownership of the service + bool AddService( const std::string& svcName, std::shared_ptr< Service > svc ); + bool AddService( const std::string& svcType, const std::string& svcName ); + + /// Check whether a service already has a serice with a given name + bool HasService( const std::string& svcName ) const; + /// Check whether a service could build a service of a given type (i.e. does the factory know about this service?) + bool CouldBuild( const std::string& svcType ) const; + + /// Remove a service from the toolbox + bool RemoveService( const std::string& svcName ); + + /// Remove a service from the toolbox and return it to the user + /// Ownership is passed to the user + std::shared_ptr< Service > ReleaseService( const std::string& svcName ); + + /// Remove all svcessors from the toolbox + /// Also clears the run queue + void ClearServices(); + + protected: + ServiceMap fServiceMap; + + }; + + inline bool ServiceToolbox::HasService( const std::string& svcName ) const + { + return fServiceMap.count( svcName ) == 1; + } + + inline bool ServiceToolbox::CouldBuild( const std::string& svcType ) const + { + return fServiceFactory->has_class( svcType ); + } + + +} /* namespace Nymph */ +#endif /* NYMPH_SERVICETOOLBOX_HH_ */ diff --git a/Cpp/Library/Utility/ConfigException.cc b/Cpp/Library/Utility/ConfigException.cc new file mode 100644 index 00000000..ce760db6 --- /dev/null +++ b/Cpp/Library/Utility/ConfigException.cc @@ -0,0 +1,69 @@ +/* + * ConfigException.cc + * + * Created on: Jun 13, 2022 + * Author: N.S. Oblath + */ + +#include "ConfigException.hh" + +namespace Nymph +{ + //KTLOGGER(proclog, "Processor"); + + ConfigException::ConfigException() noexcept : + scarab::typed_exception< ConfigException >(), + fConfig(), + fConfigStr() + {} + + ConfigException::ConfigException( const std::string& a_filename, int a_line ) noexcept : + scarab::typed_exception< ConfigException >( a_filename, a_line ), + fConfig(), + fConfigStr() + {} + + ConfigException::ConfigException( const scarab::param& config ) noexcept : + scarab::typed_exception< ConfigException >(), + fConfig(), + fConfigStr() + { + try + { + fConfig = config.clone(); + } + catch( ... ) + {} + } + + ConfigException::ConfigException( const ConfigException& orig ) noexcept : + scarab::typed_exception< ConfigException >( orig ), + fConfig(), + fConfigStr() + { + try + { + if( orig.fConfig ) fConfig = orig.fConfig->clone(); + } + catch( ... ) + {} + + } + + ConfigException::~ConfigException() noexcept + {} + + const char* ConfigException::what() const noexcept + { + try + { + fConfigStr = f_what + '\n' + fConfig->to_string(); + return fConfigStr.c_str(); + } + catch( ... ) + { + return f_what.c_str(); + } + } + +} /* namespace Nymph */ diff --git a/Cpp/Library/Utility/ConfigException.hh b/Cpp/Library/Utility/ConfigException.hh new file mode 100644 index 00000000..ce4d0595 --- /dev/null +++ b/Cpp/Library/Utility/ConfigException.hh @@ -0,0 +1,50 @@ +/** + @file ConfigException.hh + @brief Exceptions related to configurations + @author: N. S. Oblath + @date: Jan 5, 2012 + */ + +#ifndef NYMPH_CONFIG_EXCEPTION_HH_ +#define NYMPH_CONFIG_EXCEPTION_HH_ + +#include "Exception.hh" + +#include "MemberVariable.hh" + +#include "param.hh" + +namespace Nymph +{ + /*! + @class ConfigException + @author N. S. Oblath + + @brief An exception class for use with errors that occur during configuration. + + @details + + The param_node of the configuration in question can be attached to the exception using the constructor + or the function Config(). + + */ + class ConfigException : public scarab::typed_exception< ConfigException > + { + public: + ConfigException() noexcept; + ConfigException( const std::string& a_filename, int a_line ) noexcept; + ConfigException( const scarab::param& config ) noexcept; + ConfigException( const ConfigException& orig ) noexcept; + virtual ~ConfigException() noexcept; + + virtual const char* what() const noexcept; + + MEMVAR_REF( scarab::param_ptr_t, Config ); + + protected: + mutable std::string fConfigStr; + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_CONFIG_EXCEPTION_HH_ */ diff --git a/Cpp/Library/Utility/Exception.cc b/Cpp/Library/Utility/Exception.cc new file mode 100644 index 00000000..57e911f2 --- /dev/null +++ b/Cpp/Library/Utility/Exception.cc @@ -0,0 +1,55 @@ +/* + * Exception.cc + * + * Created on: Feb 25, 2014 + * Author: nsoblath + */ + +#include "Exception.hh" + +#include "logger.hh" + +LOGGER( exlog, "Exception" ); + +namespace Nymph +{ + void PrintException( const scarab::base_exception& e, unsigned count ) + { + std::string prefix = std::to_string(count) + ": "; + LINFO( exlog, prefix << "Thrown at: " << e.where() ); + LINFO( exlog, prefix << e.what() ); + try + { + std::rethrow_if_nested( e ); + } + catch(const scarab::base_exception& eNext) + { + PrintException( eNext, ++count ); + } + catch(const std::exception& eNext) + { + PrintException( eNext, ++count ); + } + return; + } + + void PrintException( const std::exception& e, unsigned count ) + { + std::string prefix = std::to_string(count) + ": "; + LINFO( exlog, prefix << e.what() ); + try + { + std::rethrow_if_nested( e ); + } + catch(const scarab::base_exception& eNext) + { + PrintException( eNext, ++count ); + } + catch(const std::exception& eNext) + { + PrintException( eNext, ++count ); + } + return; + } + +} diff --git a/Cpp/Library/Utility/Exception.hh b/Cpp/Library/Utility/Exception.hh new file mode 100644 index 00000000..f2b16d2e --- /dev/null +++ b/Cpp/Library/Utility/Exception.hh @@ -0,0 +1,45 @@ +/* + * Exception.hh + * + * Created on: Feb 25, 2014 + * Author: nsoblath + */ + + +#ifndef NYMPH_EXCEPTION_HH_ +#define NYMPH_EXCEPTION_HH_ + +#include "base_exception.hh" + + +namespace Nymph +{ + /*! + @class Exception + @author N. S. Oblath + + @brief Basic exception class for use in Nymph and derived packages + + */ + class Exception : public scarab::typed_exception< Exception > + { + public: + using scarab::typed_exception< Exception >::typed_exception; + virtual ~Exception() noexcept = default; + }; + +#define CREATE_EXCEPT_HERE( anException ) anException( __FILE__, __LINE__ ) +#define EXCEPT_HERE( anException ) (anException)( __FILE__, __LINE__ ) + +#define THROW_EXCEPT_HERE( anException ) throw EXCEPT_HERE( anException ) +#define THROW_NESTED_EXCEPT_HERE( anException ) std::throw_with_nested( EXCEPT_HERE( anException ) ) + + /// Recursive print function for potentially-nested exceptions (for scarab::base_exception) + void PrintException( const scarab::base_exception& e, unsigned count = 0 ); + + /// Recursive print function for potentially-nested exceptions (for std::exception) + void PrintException( const std::exception& e, unsigned count = 0 ); + +} + +#endif /* NYMPH_EXCEPTION_HH_ */ diff --git a/Cpp/Library/Utility/MemberVariable.hh b/Cpp/Library/Utility/MemberVariable.hh new file mode 100644 index 00000000..b48e3ef8 --- /dev/null +++ b/Cpp/Library/Utility/MemberVariable.hh @@ -0,0 +1,93 @@ +/* + * MemberVariable.hh + * + * Created on: Aug 5, 2014 + * Author: nsoblath + */ + +#ifndef NYMPH_MEMBERVARIABLE_HH_ +#define NYMPH_MEMBERVARIABLE_HH_ + +/** + * Macros for class member variables + * + * In all cases remember to initialize the variables! + * + * For "normal" variables + * Defines accessors [type GetMyVar() const], [void SetMyVar( type )], and member variable [type fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMVAR + * - MEMVAR_NOSET + * - MEMVAR_STATIC + * - MEMVAR_STATIC_NOSET + * + * For variables accessed by reference + * Defines accessors [const type& MyVar() const], [type& MyVar()], and member variable [type fMyVar] + * The non-const function is not available if the _CONST macros are used + * - MEMVAR_REF + * - MEMVAR_REF_CONST + * - MEMVAR_REF_STATIC + * - MEMVAR_REF_STATIC_CONST + * + * For pointer variables + * Defines accessors [type* GetMyVar() const], [void SetMyVar( type* )], and member variable [type* fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMVAR_PTR + * - MEMVAR_PTR_NOSET + * - MEMVAR_PTR_STATIC + * - MEMVAR_PTR_STATIC_NOSET + * + * For shared_ptr's + * Defines accessors [const std::shared_ptr< type > MyVar() const], [std::shared_ptr< type > MyVar()], and member variable [std::shared_ptr< type > fMyVar] + * The non-const function is not available if the _CONST macros are used + * - MEMVAR_SHARED_PTR + * - MEMVAR_SHARED_PTR_CONST + * - MEMVAR_SHARED_PTR_STATIC + * - MEMVAR_SHARED_PTR_STATIC_CONST + * + * For atomic variables + * Defines accessors [type GetMyVar() const], [void SetMyVar( type )], and member variable [std::atomic< type > fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMVAR_ATOMIC + * - MEMVAR_ATOMIC_NOSET + * - MEMVAR_ATOMIC_STATIC + * - MEMVAR_ATOMIC_STATIC_NOSET + * + */ + +#include "_camel_case_member_variables.hh" + +#define MEMVAR camel_case_mv_accessible +#define MEMVAR_NOSET camel_case_mv_accessible_noset +#define MEMVAR_STATIC camel_case_mv_accessible_static +#define MEMVAR_STATIC_NOSET camel_case_mv_accessible_static_noset +#define MEMVAR_MUTABLE camel_case_mv_accessible_mutable +#define MEMVAR_MUTABLE_NOSET camel_case_mv_accessible_mutable_noset + +#define MEMVAR_REF camel_case_mv_referrable +#define MEMVAR_REF_CONST camel_case_mv_referrable_const +#define MEMVAR_REF_STATIC camel_case_mv_referrable_static +#define MEMVAR_REF_MUTABLE camel_case_mv_referrable_mutable +#define MEMVAR_REF_MUTABLE_CONST camel_case_mv_referrable_mutable + +#define MEMVAR_PTR camel_case_mv_assignable +#define MEMVAR_PTR_NOSET camel_case_mv_assignable_noset +#define MEMVAR_PTR_STATIC camel_case_mv_assignable_static +#define MEMVAR_PTR_STATIC_NOSET camel_case_mv_assignable_static_noset +#define MEMVAR_PTR_MUTABLE camel_case_mv_assignable_mutable +#define MEMVAR_PTR_MUTABLE_NOSET camel_case_mv_assignable_mutable_noset + +#define MEMVAR_SHARED_PTR camel_case_mv_shared_ptr +#define MEMVAR_SHARED_PTR_CONST camel_case_mv_shared_ptr_const +#define MEMVAR_SHARED_PTR_STATIC camel_case_mv_shared_ptr_static +#define MEMVAR_SHARED_PTR_MUTABLE camel_case_mv_shared_ptr_mutable +#define MEMVAR_SHARED_PTR_MUTABLE_CONST camel_case_mv_shared_ptr_mutable + +#define MEMVAR_ATOMIC camel_case_mv_atomic +#define MEMVAR_ATOMIC_NOSET camel_case_mv_atomic_noset +#define MEMVAR_ATOMIC_STATIC camel_case_mv_atomic_static +#define MEMVAR_ATOMIC_STATIC_NOSET camel_case_mv_atomic_static_noset +#define MEMVAR_ATOMIC_MUTABLE camel_case_mv_atomic_mutable +#define MEMVAR_ATOMIC_MUTABLE_NOSET camel_case_mv_atomic_mutable_noset + +#endif /* NYMPH_MEMBERVARIABLE_HH_ */ diff --git a/Cpp/Library_v1/AddLibPythonPath.sh.in b/Cpp/Library_v1/AddLibPythonPath.sh.in new file mode 100644 index 00000000..68aff4b8 --- /dev/null +++ b/Cpp/Library_v1/AddLibPythonPath.sh.in @@ -0,0 +1,3 @@ +# Adds the lib install directory to the python path + +export PYTHONPATH=@LIB_INSTALL_DIR@:$PYTHONPATH diff --git a/Library/Application/KTApplication.cc b/Cpp/Library_v1/Application/KTApplication.cc similarity index 93% rename from Library/Application/KTApplication.cc rename to Cpp/Library_v1/Application/KTApplication.cc index 0e49697c..4bc4e01e 100644 --- a/Library/Application/KTApplication.cc +++ b/Cpp/Library_v1/Application/KTApplication.cc @@ -67,11 +67,11 @@ namespace Nymph scarab::param_ptr_t configFromFile = translator.read_file( configFilePath.native() ); if( configFromFile == NULL ) { - throw KTException() << "[KTApplication] error parsing config file"; + BOOST_THROW_EXCEPTION( KTException() << "[KTApplication] error parsing config file" << eom ); } if( ! configFromFile->is_node() ) { - throw KTException() << "[KTApplication] configuration file must consist of an object/node"; + BOOST_THROW_EXCEPTION( KTException() << "[KTApplication] configuration file must consist of an object/node" << eom ); } fConfigurator->Merge( configFromFile->as_node() ); } @@ -83,7 +83,7 @@ namespace Nymph scarab::param_ptr_t configFromJSON = inputJSON.read_string( clJSON ); if( ! configFromJSON->is_node() ) { - throw KTException() << "[KTApplication] command line json must be an object"; + BOOST_THROW_EXCEPTION( KTException() << "[KTApplication] command line json must be an object" << eom ); } fConfigurator->Merge( configFromJSON->as_node() ); } diff --git a/Library/Application/KTApplication.hh b/Cpp/Library_v1/Application/KTApplication.hh similarity index 100% rename from Library/Application/KTApplication.hh rename to Cpp/Library_v1/Application/KTApplication.hh diff --git a/Library/Application/KTCommandLineHandler.cc b/Cpp/Library_v1/Application/KTCommandLineHandler.cc similarity index 96% rename from Library/Application/KTCommandLineHandler.cc rename to Cpp/Library_v1/Application/KTCommandLineHandler.cc index ad668ab3..5023633b 100644 --- a/Library/Application/KTCommandLineHandler.cc +++ b/Cpp/Library_v1/Application/KTCommandLineHandler.cc @@ -26,10 +26,6 @@ namespace Nymph { KTLOGGER(utillog, "KTCommandLineHandler"); - CommandLineHandlerException::CommandLineHandlerException (std::string const& why) - : std::logic_error(why) - {} - KTCommandLineHandler::KTCommandLineHandler() : fExecutableName("NONE"), fPackageString(STRINGIFY_2(PACKAGE_STRING)), @@ -279,12 +275,22 @@ namespace Nymph string nodeName(t_full_name.substr(t_node_start_pos, t_node_sep_pos)); if (parentNode.has(nodeName)) { +<<<<<<< HEAD:Library_v1/Application/KTCommandLineHandler.cc + parentNode = parentNode[nodeName]().as_node(); + } + else + { + scarab::param_node* newChildNode = new scarab::param_node(); + parentNode.add(nodeName, newChildNode); + parentNode = *newChildNode; +======= parentNode = parentNode[nodeName].as_node(); } else { parentNode.add(nodeName, scarab::param_ptr_t( new scarab::param_node())); parentNode = parentNode[nodeName].as_node(); +>>>>>>> develop:Library/Application/KTCommandLineHandler.cc } t_node_start_pos = t_node_sep_pos + 1; t_node_sep_pos = t_full_name.find_first_of(fNodeSeparator, t_node_start_pos); @@ -388,7 +394,7 @@ namespace Nymph { KTERROR(utillog, "Exception caught while performing initial CL parsing:\n" << '\t' << e.what()); - throw std::logic_error(e.what()); + BOOST_THROW_EXCEPTION( KTException() << e.what() << eom ); } // Save the remaining command-line options for later parsing (after the full option list has been populated) fCommandLineParseLater = po::collect_unrecognized(tParsedOpts.options, po::include_positional); diff --git a/Library/Application/KTCommandLineHandler.hh b/Cpp/Library_v1/Application/KTCommandLineHandler.hh similarity index 97% rename from Library/Application/KTCommandLineHandler.hh rename to Cpp/Library_v1/Application/KTCommandLineHandler.hh index ed76317d..6aca4b92 100644 --- a/Library/Application/KTCommandLineHandler.hh +++ b/Cpp/Library_v1/Application/KTCommandLineHandler.hh @@ -9,6 +9,7 @@ #ifndef KTCOMMANDLINEHANDLER_H_ #define KTCOMMANDLINEHANDLER_H_ +#include "KTException.hh" #include "KTLogger.hh" #include "param.hh" @@ -19,7 +20,6 @@ namespace po = boost::program_options; #include #include -#include #include #include @@ -27,13 +27,6 @@ namespace Nymph { KTLOGGER(utillog_clh, "KTCommandLineHandler.hh"); - class CommandLineHandlerException : public std::logic_error - { - public: - CommandLineHandlerException(std::string const& why); - }; - - /*! @class KTCommandLineHandler @author N. S. Oblath @@ -287,7 +280,7 @@ namespace Nymph { KTWARN(utillog_clh, "Command line option <" << aCLOption << "> was not set!\n" "Next time check whether it's set before calling this function."); - throw CommandLineHandlerException("Command line option " + aCLOption + " was not set!"); + BOOST_THROW_EXCEPTION( KTException() << "Command line option " + aCLOption + " was not set!" << eom ); } return fCommandLineVarMap[aCLOption].as< XReturnType >(); } diff --git a/Library/Application/KTCommandLineOption.hh b/Cpp/Library_v1/Application/KTCommandLineOption.hh similarity index 100% rename from Library/Application/KTCommandLineOption.hh rename to Cpp/Library_v1/Application/KTCommandLineOption.hh diff --git a/Library/Application/KTConfigurator.cc b/Cpp/Library_v1/Application/KTConfigurator.cc similarity index 73% rename from Library/Application/KTConfigurator.cc rename to Cpp/Library_v1/Application/KTConfigurator.cc index ed21ba2d..4207a392 100644 --- a/Library/Application/KTConfigurator.cc +++ b/Cpp/Library_v1/Application/KTConfigurator.cc @@ -23,6 +23,10 @@ namespace Nymph KTConfigurator::~KTConfigurator() { +<<<<<<< HEAD:Library_v1/Application/KTConfigurator.cc + delete fMasterConfig; +======= +>>>>>>> develop:Library/Application/KTConfigurator.cc } } /* namespace Nymph */ diff --git a/Library/Application/KTConfigurator.hh b/Cpp/Library_v1/Application/KTConfigurator.hh similarity index 58% rename from Library/Application/KTConfigurator.hh rename to Cpp/Library_v1/Application/KTConfigurator.hh index feaf28d6..05b9f766 100644 --- a/Library/Application/KTConfigurator.hh +++ b/Cpp/Library_v1/Application/KTConfigurator.hh @@ -48,12 +48,50 @@ namespace Nymph template< typename XReturnType > XReturnType KTConfigurator::Get( const std::string& aName ) const { +<<<<<<< HEAD:Library_v1/Application/KTConfigurator.hh + try + { + fParamBuffer = &const_cast< scarab::param& >( (*fMasterConfig)[aName] ); + if( fParamBuffer->is_value() ) + { + return (*fParamBuffer)().as< XReturnType >(); + } + } + catch( std::exception& e ) + {} + BOOST_THROW_EXCEPTION( KTException() << "configurator does not have a value for <" << aName << ">" << eom ); +======= return fMasterConfig[ aName ]().as< XReturnType >(); +>>>>>>> develop:Library/Application/KTConfigurator.hh } template< typename XReturnType > XReturnType KTConfigurator::Get( const std::string& aName, XReturnType aDefault ) const { +<<<<<<< HEAD:Library_v1/Application/KTConfigurator.hh + try + { + fParamBuffer = &const_cast< scarab::param& >( (*fMasterConfig)[aName] ); + if( fParamBuffer->is_value() ) + { + return (*fParamBuffer)().as< XReturnType >(); + } + } + catch( std::exception& e ) + {} + return aDefault; + } + + inline void KTConfigurator::Merge(const scarab::param_node& aNode) + { + fMasterConfig->merge(aNode); + return; + } + + inline scarab::param_node& KTConfigurator::Config() + { + return *fMasterConfig; +======= return fMasterConfig.get_value< XReturnType >( aName, aDefault ); } @@ -71,8 +109,15 @@ namespace Nymph inline const scarab::param_node& KTConfigurator::Config() const { return fMasterConfig; +>>>>>>> develop:Library/Application/KTConfigurator.hh } + inline const scarab::param_node& KTConfigurator::Config() const + { + return *fMasterConfig; + } + + } /* namespace Nymph */ #endif /* KTCONFIGURATOR_HH_ */ diff --git a/Library/Application/KTDataQueueProcessor.cc b/Cpp/Library_v1/Application/KTDataQueueProcessor.cc similarity index 62% rename from Library/Application/KTDataQueueProcessor.cc rename to Cpp/Library_v1/Application/KTDataQueueProcessor.cc index 000ed41b..4354dfd3 100644 --- a/Library/Application/KTDataQueueProcessor.cc +++ b/Cpp/Library_v1/Application/KTDataQueueProcessor.cc @@ -17,7 +17,9 @@ namespace Nymph KTDataQueueProcessorTemplate< KTDataQueueProcessor >(name), fDataSignal("data", this) { - RegisterSlot("data", this, &KTDataQueueProcessor::QueueData); + SetFuncPtr(&KTDataQueueProcessor::EmitDataSignal); + fQueueDataSW = RegisterSlot("data", this, &KTDataQueueProcessor::QueueData, {}); + fSignalsEmitted.push_back("data"); //RegisterSlot("data-list", this, &KTDataQueueProcessor::QueueDataList); } @@ -31,18 +33,19 @@ namespace Nymph return true; } - void KTDataQueueProcessor::EmitDataSignal(KTDataPtr data) + void KTDataQueueProcessor::EmitDataSignal(KTDataHandle data) { fDataSignal(data); return; } - void KTDataQueueProcessor::QueueData(KTDataPtr& data) + void KTDataQueueProcessor::QueueData(KTDataHandle& data) { - return DoQueueData(data, &KTDataQueueProcessor::EmitDataSignal); + fQueueDataSW->GetThreadRef()->Break(data, fQueueDataSW->GetDoBreakpoint()); + return DoQueueData(data); } /* - void KTDataQueueProcessor::QueueDataList(list< KTDataPtr >* dataList) + void KTDataQueueProcessor::QueueDataList(list< KTDataHandle >* dataList) { return DoQueueDataList(dataList, &KTDataQueueProcessor::EmitDataSignal); } diff --git a/Library/Application/KTDataQueueProcessor.hh b/Cpp/Library_v1/Application/KTDataQueueProcessor.hh similarity index 83% rename from Library/Application/KTDataQueueProcessor.hh rename to Cpp/Library_v1/Application/KTDataQueueProcessor.hh index eec27991..8ce93cf8 100644 --- a/Library/Application/KTDataQueueProcessor.hh +++ b/Cpp/Library_v1/Application/KTDataQueueProcessor.hh @@ -11,7 +11,7 @@ #include "KTPrimaryProcessor.hh" #include "KTConcurrentQueue.hh" -#include "KTData.hh" +#include "KTCoreData.hh" #include "KTLogger.hh" #include "KTSlot.hh" @@ -46,10 +46,12 @@ namespace Nymph class KTDataQueueProcessorTemplate : public KTPrimaryProcessor { public: + typedef void (XProcessorType::*FuncPtrType)(KTDataHandle); + struct DataAndFunc { - KTDataPtr fData; - void (XProcessorType::*fFuncPtr)(KTDataPtr); + KTDataHandle fData; + FuncPtrType fFuncPtr; }; typedef KTConcurrentQueue< DataAndFunc > Queue; @@ -76,14 +78,14 @@ namespace Nymph protected: Status fStatus; - //************************************** - // Derived Processor function pointer - //************************************** + //********************************************** + // Derived Processor function pointer (optional) + //********************************************** public: - void SetFuncPtr(void (XProcessorType::*ptr)(KTDataPtr)); + void SetFuncPtr(FuncPtrType ptr); protected: - void (XProcessorType::*fFuncPtr)(KTDataPtr); + FuncPtrType fFuncPtr; //********* @@ -109,13 +111,17 @@ namespace Nymph // Queueing functions for slots //********* protected: - /// Queue an data object + /// Queue an data object with a provided function + /// Assumes ownership of the data; original shared pointer will be nullified + void DoQueueData(KTDataHandle& data, FuncPtrType func); + + /// Queue an data object with fFuncPtr /// Assumes ownership of the data; original shared pointer will be nullified - void DoQueueData(KTDataPtr& data, void (XProcessorType::*func)(KTDataPtr)); + void DoQueueData(KTDataHandle& data); /// Queue a list of data objects /// Assumes ownership of all data objects and the list; original shared pointers will be nullified - //void DoQueueDataList(std::list< KTDataPtr& >* dataList, void (XProcessorType::*fFuncPtr)(KTDataPtr)); + //void DoQueueDataList(std::list< KTDataHandle& >* dataList, void (XProcessorType::*fFuncPtr)(KTDataHandle)); //********* // Slots @@ -129,7 +135,7 @@ namespace Nymph // Signals //********* protected: - KTSignalOneArg< void > fQueueDoneSignal; + KTSignalDone fQueueDoneSignal; }; @@ -145,16 +151,18 @@ namespace Nymph @brief Generic data queue for asynchronous processing @details + Asynchronously emits a signal for each data object it receives. + Allows the user to start an asynchronous chain of processors. Configuration name: "data-queue" Available configuration values: Slots: - - "data": void (KTDataPtr) -- Queue a data object for asynchronous processing; use signal "data" + - "data": void (KTDataHandle) -- Queue a data object for asynchronous processing; use signal "data" Signals: - - "data": void (KTDataPtr) -- Emitted for each data object in the queue + - "data": void (KTDataHandle) -- Emitted for each data object in the queue - "queue-done": void () -- Emitted when queue is emptied (inherited from KTDataQueueProcessorTemplate) */ class KTDataQueueProcessor : public KTDataQueueProcessorTemplate< KTDataQueueProcessor > @@ -166,7 +174,7 @@ namespace Nymph bool ConfigureSubClass(const scarab::param_node& node); public: - void EmitDataSignal(KTDataPtr data); + void EmitDataSignal(KTDataHandle data); //*************** // Signals @@ -181,11 +189,12 @@ namespace Nymph public: /// Queue an data object; will emit data signal /// Assumes ownership of the data; original shared pointer will be nullified - void QueueData(KTDataPtr& data); + void QueueData(KTDataHandle& data); + KTSlotWrapper* fQueueDataSW; /// Queue a list of data objects; will emit data signal /// Assumes ownership of all data objects and the list; original shared pointers will be nullified - //void QueueDataList(std::list< KTDataPtr >* dataList); + //void QueueDataList(std::list< KTDataHandle >* dataList); }; @@ -197,16 +206,16 @@ namespace Nymph template< class XProcessorType > KTDataQueueProcessorTemplate< XProcessorType >::KTDataQueueProcessorTemplate(const std::string& name) : - KTPrimaryProcessor(name), + KTPrimaryProcessor({"queue-done"}, name), fStatus(kStopped), fFuncPtr(NULL), fQueue(), fPopFromQueue(&KTConcurrentQueue< DataAndFunc >::wait_and_pop), fQueueDoneSignal("queue-done", this) { - RegisterSlot("use-timed-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToTimedPop); - RegisterSlot("use-untimed-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToUntimedPop); - RegisterSlot("use-single-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToSinglePop); + RegisterSlot("use-timed-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToTimedPop, {}); + RegisterSlot("use-untimed-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToUntimedPop, {}); + RegisterSlot("use-single-pop", this, &KTDataQueueProcessorTemplate< XProcessorType >::SwitchToSinglePop, {}); } template< class XProcessorType > @@ -242,7 +251,7 @@ namespace Nymph } template< class XProcessorType > - void KTDataQueueProcessorTemplate< XProcessorType >::SetFuncPtr(void (XProcessorType::*ptr)(KTDataPtr)) + void KTDataQueueProcessorTemplate< XProcessorType >::SetFuncPtr(FuncPtrType ptr) { fFuncPtr = ptr; return; @@ -294,7 +303,7 @@ namespace Nymph template< class XProcessorType > - void KTDataQueueProcessorTemplate< XProcessorType >::DoQueueData(KTDataPtr& data, void (XProcessorType::*func)(KTDataPtr)) + void KTDataQueueProcessorTemplate< XProcessorType >::DoQueueData(KTDataHandle& data, FuncPtrType func) { KTDEBUG(eqplog, "Queueing data"); DataAndFunc daf; @@ -304,11 +313,18 @@ namespace Nymph fQueue.push(daf); return; } + + template< class XProcessorType > + void KTDataQueueProcessorTemplate< XProcessorType >::DoQueueData(KTDataHandle& data) + { + DoQueueData(data, fFuncPtr); + return; + } /* template< class XProcessorType > - void KTDataQueueProcessorTemplate< XProcessorType >::DoQueueDataList(std::list< KTDataPtr& >* dataList, void (XProcessorType::*func)(KTDataPtr)) + void KTDataQueueProcessorTemplate< XProcessorType >::DoQueueDataList(std::list< KTDataHandle& >* dataList, void (XProcessorType::*func)(KTDataHandle)) { - typedef std::list< KTDataPtr > DataList; + typedef std::list< KTDataHandle > DataList; KTDEBUG(eqplog, "Queueing data objects"); DataAndFunc daf; diff --git a/Library/Application/KTFilenameParsers.cc b/Cpp/Library_v1/Application/KTFilenameParsers.cc similarity index 100% rename from Library/Application/KTFilenameParsers.cc rename to Cpp/Library_v1/Application/KTFilenameParsers.cc diff --git a/Library/Application/KTFilenameParsers.hh b/Cpp/Library_v1/Application/KTFilenameParsers.hh similarity index 100% rename from Library/Application/KTFilenameParsers.hh rename to Cpp/Library_v1/Application/KTFilenameParsers.hh diff --git a/Cpp/Library_v1/Application/KTPrintDataStructure.cc b/Cpp/Library_v1/Application/KTPrintDataStructure.cc new file mode 100644 index 00000000..c05e5186 --- /dev/null +++ b/Cpp/Library_v1/Application/KTPrintDataStructure.cc @@ -0,0 +1,121 @@ +/* + * KTPrintDataStructure.cc + * + * Created on: Sept 12, 2014 + * Author: N.S. Oblath + */ + +#include "KTPrintDataStructure.hh" + +#include "KTLogger.hh" + +#include + +using boost::shared_ptr; + +namespace Nymph +{ + KTLOGGER(datalog, "KTPrintDataStructure"); + + // Register the processor + KT_REGISTER_PROCESSOR(KTPrintDataStructure, "print-data-structure"); + + KTPrintDataStructure::KTPrintDataStructure(const std::string& name) : + KTProcessor(name), + fDataSignal("data", this), + fDataStructSlot("print-data", this, &KTPrintDataStructure::PrintDataStructure, {"data"}), + fCutStructSlot("print-cuts", this, &KTPrintDataStructure::PrintCutStructure, {"data"}), + fDataAndCutStructSlot("print-data-and-cuts", this, &KTPrintDataStructure::PrintDataAndCutStructure, {"data"}) + { + } + + KTPrintDataStructure::~KTPrintDataStructure() + { + } + + bool KTPrintDataStructure::Configure(const scarab::param_node&) + { + return true; + } + + void KTPrintDataStructure::PrintDataStructure(KTDataHandle dataHandle) + { + DoPrintDataStructure(dataHandle); + + KTSlotWrapper* slotWrap = fDataStructSlot.GetSlotWrapper(); + slotWrap->GetThreadRef()->Break( dataHandle, slotWrap->GetDoBreakpoint() ); + + fDataSignal(dataHandle); + + return; + } + + void KTPrintDataStructure::PrintCutStructure(KTDataHandle dataHandle) + { + DoPrintCutStructure(dataHandle); + + KTSlotWrapper* slotWrap = fCutStructSlot.GetSlotWrapper(); + fCutStructSlot.GetSlotWrapper()->GetThreadRef()->Break( dataHandle, slotWrap->GetDoBreakpoint() ); + + fDataSignal(dataHandle); + + return; + } + + + void KTPrintDataStructure::PrintDataAndCutStructure(KTDataHandle dataHandle) + { + DoPrintDataStructure(dataHandle); + DoPrintCutStructure(dataHandle); + + KTSlotWrapper* slotWrap = fDataAndCutStructSlot.GetSlotWrapper(); + fDataAndCutStructSlot.GetSlotWrapper()->GetThreadRef()->Break( dataHandle, slotWrap->GetDoBreakpoint() ); + + fDataSignal(dataHandle); + + return; + } + + void KTPrintDataStructure::DoPrintDataStructure(KTDataHandle dataHandle) + { + std::stringstream printbuf; + + printbuf << "\nData Structure:\n"; + printbuf << "\t- " << dataHandle->Name() << '\n'; + KTDEBUG(datalog, "Found data type " << dataHandle->Name()); + KTExtensibleCore< KTDataRider >::BasePtrType nextData = dataHandle->Next(); + while (nextData != NULL) + { + printbuf << "\t- " << nextData->Name() << '\n'; + KTDEBUG(datalog, "Found data type " << nextData->Name()); + nextData = nextData->Next(); + } + + KTINFO(datalog, printbuf.str()); + + return; + } + + void KTPrintDataStructure::DoPrintCutStructure(KTDataHandle dataHandle) + { + std::stringstream printbuf; + + KTCutStatus& cutStatus = dataHandle->CutStatus(); + printbuf << "\n" << cutStatus; + + const KTCutStatus::CutResults_t cutResults = cutStatus.CutResults(); + printbuf << "Cut Structure:\n"; + unsigned nCuts = cutResults.size(); + for (unsigned iCut = 0; iCut < nCuts; ++iCut) + { + if (! cutResults[iCut].fAssigned) continue; + printbuf << "\t" << iCut << " -- " << cutResults[iCut].fName << " -- is cut: " << cutResults[iCut].fState << '\n'; + KTDEBUG(datalog, "Found cut type " << cutResults[iCut].fName << " at mask position " << iCut); + } + + KTINFO(datalog, printbuf.str()); + + return; + } + +} /* namespace Nymph */ diff --git a/Library/Application/KTPrintDataStructure.hh b/Cpp/Library_v1/Application/KTPrintDataStructure.hh similarity index 58% rename from Library/Application/KTPrintDataStructure.hh rename to Cpp/Library_v1/Application/KTPrintDataStructure.hh index b47b411a..10c171c5 100644 --- a/Library/Application/KTPrintDataStructure.hh +++ b/Cpp/Library_v1/Application/KTPrintDataStructure.hh @@ -35,12 +35,12 @@ namespace Nymph Available configuration values: none Slots: - - "print-data": void (KTDataPtr) -- Prints the structure of the data object; Does not modify the data or cuts; Emits signal "data" - - "print-cuts": void (KTDataPtr) -- Prints the structure of the data's cuts; Does not modify the data or cuts; Emits signal "data" - - "print-data-and-cuts": void (KTDataPtr) -- Prints the structure of the data object and its cuts; Does not modify the data or cuts; Emits signal "data" + - "print-data": void (KTDataHandle) -- Prints the structure of the data object; Does not modify the data or cuts; Emits signal "data" + - "print-cuts": void (KTDataHandle) -- Prints the structure of the data's cuts; Does not modify the data or cuts; Emits signal "data" + - "print-data-and-cuts": void (KTDataHandle) -- Prints the structure of the data object and its cuts; Does not modify the data or cuts; Emits signal "data" Signals: - - "data": void (KTDataPtr) -- Emitted after structure information is printed + - "data": void (KTDataHandle) -- Emitted after structure information is printed */ class KTPrintDataStructure : public KTProcessor @@ -52,13 +52,13 @@ namespace Nymph bool Configure(const scarab::param_node& node); public: - void PrintDataStructure(KTDataPtr dataPtr); - void PrintCutStructure(KTDataPtr dataPtr); - void PrintDataAndCutStructure(KTDataPtr dataPtr); + void PrintDataStructure(KTDataHandle dataHandle); + void PrintCutStructure(KTDataHandle dataHandle); + void PrintDataAndCutStructure(KTDataHandle dataHandle); private: - void DoPrintDataStructure(KTDataPtr dataPtr); - void DoPrintCutStructure(KTDataPtr dataPtr); + void DoPrintDataStructure(KTDataHandle dataHandle); + void DoPrintCutStructure(KTDataHandle dataHandle); //*************** // Signals @@ -72,9 +72,9 @@ namespace Nymph //*************** private: - KTSlotOneArg< void (KTDataPtr) > fDataStructSlot; - KTSlotOneArg< void (KTDataPtr) > fCutStructSlot; - KTSlotOneArg< void (KTDataPtr) > fDataAndCutStructSlot; + KTSlot< KTDataHandle > fDataStructSlot; + KTSlot< KTDataHandle > fCutStructSlot; + KTSlot< KTDataHandle > fDataAndCutStructSlot; }; } diff --git a/Cpp/Library_v1/Application/KTProcessorToolbox.cc b/Cpp/Library_v1/Application/KTProcessorToolbox.cc new file mode 100644 index 00000000..d483f1d8 --- /dev/null +++ b/Cpp/Library_v1/Application/KTProcessorToolbox.cc @@ -0,0 +1,986 @@ +/* + * KTProcessorToolbox.cc + * + * Created on: Sep 27, 2012 + * Author: nsoblath + */ + +#include "KTProcessorToolbox.hh" + +#include "KTException.hh" +#include "KTLogger.hh" +#include "KTPrimaryProcessor.hh" + +#include "factory.hh" +#include "param_codec.hh" + +#include +#include + +using std::deque; +using std::set; +using std::string; +using std::vector; + +namespace Nymph +{ + KTLOGGER(proclog, "KTProcessorToolbox"); + + KTProcessorToolbox::KTProcessorToolbox(const std::string& name) : + KTConfigurable(name), + fProcFactory(scarab::factory< KTProcessor, const std::string& >::get_instance()), + fRunSingleThreaded( false ), + fRunQueue(), + fProcMap(), + fThreadReferences(), + fContinueCV(), + fDoContinue( false ), + fBreakContMutex(), + fDoRunThread( nullptr ), + fDoRunPromise(), + fDoRunBreakFlag( false ) + { + } + + KTProcessorToolbox::~KTProcessorToolbox() + { + CancelThreads(); + + JoinRunThread(); + + ClearProcessors(); + } + + bool KTProcessorToolbox::Configure(const scarab::param_node& node) + { + KTPROG(proclog, "Configuring . . ."); + + SetRunSingleThreaded( node.get_value( "single-threaded", fRunSingleThreaded ) ); + + // Deal with "processor" blocks + if (! node.has("processors")) + { + KTWARN(proclog, "No processors were specified"); + } + else + { + const scarab::param_array& procArray = node["processors"].as_array(); + for( scarab::param_array::const_iterator procIt = procArray.begin(); procIt != procArray.end(); ++procIt ) + { + if( ! procIt->is_node() ) + { + KTERROR( proclog, "Invalid processor entry: not a node" ); + return false; + } + const scarab::param_node& procNode = procIt->as_node(); + + if (! procNode.has("type")) + { + KTERROR(proclog, "Unable to create processor: no processor type given"); + return false; + } + string procType = procNode["type"]().as_string(); + + string procName; + if (! procNode.has("name")) + { + KTINFO(proclog, "No name given for processor of type <" << procType << ">; using type as name."); + procName = procType; + } + else + { + procName = procNode["name"]().as_string(); + } + std::shared_ptr< KTProcessor > newProc ( fProcFactory->create(procType, procType)); + if (newProc == NULL) + { + KTERROR(proclog, "Unable to create processor of type <" << procType << ">"); + return false; + } + + if (! AddProcessor(procName, newProc)) + { + KTERROR(proclog, "Unable to add processor <" << procName << ">"); + //delete newProc; //not required for smart pointers + return false; + } + } + } + + + // Then deal with connections" + if (! node.has("connections")) + { + KTWARN(proclog, "No connections were specified"); + } + else + { + const scarab::param_array& connArray = node["connections"].as_array(); + for( scarab::param_array::const_iterator connIt = connArray.begin(); connIt != connArray.end(); ++connIt ) + { + if( ! connIt->is_node() ) + { + KTERROR( proclog, "Invalid connection entry: not a node" ); + return false; + } + const scarab::param_node& connNode = connIt->as_node(); + + if ( ! connNode.has("signal") || ! connNode.has("slot") ) + { + KTERROR(proclog, "Signal/Slot connection information is incomplete!"); + if (connNode.has("signal")) + { + KTWARN(proclog, "signal = " << connNode["signal"]()); + } + else + { + KTERROR(proclog, "signal = MISSING"); + } + + if (connNode.has("slot")) + { + KTWARN(proclog, "slot = " << connNode["slot"]()); + } + else + { + KTERROR(proclog, "slot = MISSING"); + } + return false; + } + + bool connReturn = false; + if (connNode.has("order")) + { + connReturn = MakeConnection(connNode["signal"]().as_string(), connNode["slot"]().as_string(), connNode["order"]().as_int()); + } + else + { + connReturn = MakeConnection(connNode["signal"]().as_string(), connNode["slot"]().as_string()); + } + if (! connReturn) + { + KTERROR(proclog, "Unable to make connection <" << connNode["signal"]() << "> --> <" << connNode["slot"]() << ">"); + return false; + } + + if (connNode.has("breakpoint")) + { + if (! SetBreakpoint(connNode["slot"]().as_string())) + { + KTERROR(proclog, "Unable to set breakpoint on <" << connNode["slot"]()); + return false; + } + } + + KTINFO(proclog, "Signal <" << connNode["signal"]() << "> connected to slot <" << connNode["slot"]() << ">"); + } + } + + + // Finally, deal with processor-run specifications + // The run queue is an array of processor names, or groups of names, which will be run sequentially. + // If names are grouped (in another array), those in that group will be run in parallel. + // In single threaded mode all threads will be run sequentially in the order they were specified. + if (! node.has("run-queue")) + { + KTWARN(proclog, "Run queue was not specified"); + } + else + { + const scarab::param_array& rqArray = node["run-queue"].as_array(); + for (scarab::param_array::const_iterator rqIt = rqArray.begin(); rqIt != rqArray.end(); ++rqIt) + { + if (rqIt->is_value()) + { + if (! PushBackToRunQueue((*rqIt)().as_string())) + { + KTERROR(proclog, "Unable to process run-queue entry: could not add processor to the queue"); + return false; + } + } + else if (rqIt->is_array()) + { + const scarab::param_array* rqNode = &( rqIt->as_array() ); + std::vector< std::string > names; + + for (scarab::param_array::const_iterator rqArrayIt = rqNode->begin(); rqArrayIt != rqNode->end(); ++rqArrayIt) + { + if (! rqArrayIt->is_value()) + { + KTERROR(proclog, "Invalid run-queue array entry: not a value"); + return false; + } + names.push_back((*rqArrayIt)().as_string()); + } + + if (! PushBackToRunQueue(names)) + { + KTERROR(proclog, "Unable to process run-queue entry: could not add list of processors to the queue"); + return false; + } + } + else + { + KTERROR(proclog, "Invalid run-queue entry: not a value or array"); + return false; + } + } + } + + return true; + } + + bool KTProcessorToolbox::ConfigureProcessors(const scarab::param_node& node) + { + for (ProcMapIt iter = fProcMap.begin(); iter != fProcMap.end(); iter++) + { + KTDEBUG(proclog, "Attempting to configure processor <" << iter->first << ">"); + string procName = iter->first; + string nameUsed(procName); + if (! node.has(nameUsed)) + { + nameUsed = iter->second.fProc->GetConfigName(); + if (! node.has(nameUsed)) + { + KTWARN(proclog, "Did not find a parameter node <" << procName << "> or <" << nameUsed << ">\n" + "\tProcessor <" << procName << "> was not configured."); + continue; + } + } + const scarab::param_node& subNode = node[nameUsed].as_node(); + if (! iter->second.fProc->Configure(subNode)) + { + KTERROR(proclog, "An error occurred while configuring processor <" << procName << "> with parameter node <" << nameUsed << ">"); + return false; + } + } + return true; + } + + bool KTProcessorToolbox::ConfigureProcessors(const std::string& config) + { + scarab::param_translator translator; + scarab::param_node optNode; + optNode.add( "encoding", new scarab::param_value( "json" ) ); + return ConfigureProcessors( translator.read_string( config, optNode )->as_node() ); + } + + std::shared_ptr< KTProcessor > KTProcessorToolbox::GetProcessor(const std::string& procName) + { + ProcMapIt it = fProcMap.find(procName); + if (it == fProcMap.end()) + { + KTWARN(proclog, "Processor <" << procName << "> was not found."); + return NULL; + } + return it->second.fProc; + } + + const std::shared_ptr< KTProcessor > KTProcessorToolbox::GetProcessor(const std::string& procName) const + { + ProcMapCIt it = fProcMap.find(procName); + if (it == fProcMap.end()) + { + KTWARN(proclog, "Processor <" << procName << "> was not found."); + return NULL; + } + return it->second.fProc; + } + + bool KTProcessorToolbox::AddProcessor(const std::string& procName, std::shared_ptr< KTProcessor > proc) + { + ProcMapIt it = fProcMap.find(procName); + if (it == fProcMap.end()) + { + ProcessorInfo pInfo; + pInfo.fProc = proc; + fProcMap.insert(ProcMapValue(procName, pInfo)); + KTDEBUG(proclog, "Added processor <" << procName << "> (a.k.a. " << proc->GetConfigName() << ")"); + return true; + } + KTWARN(proclog, "Processor <" << procName << "> already exists; new processor was not added."); + return false; + } + + bool KTProcessorToolbox::AddProcessor(const std::string& procType, const std::string& procName) + { + ProcMapIt it = fProcMap.find(procName); + if (it == fProcMap.end()) + { + std::shared_ptr< KTProcessor > newProc ( fProcFactory->create(procType, procType)); + if (newProc == NULL) + { + KTERROR(proclog, "Unable to create processor of type <" << procType << ">"); + return false; + } + if (! AddProcessor(procName, newProc)) + { + KTERROR(proclog, "Unable to add processor <" << procName << ">"); + //delete newProc; + return false; + } + return true; + } + KTWARN(proclog, "Processor <" << procName << "> already exists; new processor was not added."); + return false; + } + + bool KTProcessorToolbox::RemoveProcessor(const std::string& procName) + { + std::shared_ptr< KTProcessor > procToRemove = ReleaseProcessor(procName); + if (procToRemove == NULL) + { + return false; + } + //delete procToRemove; + KTDEBUG(proclog, "Processor <" << procName << "> deleted."); + return true; + } + + std::shared_ptr< KTProcessor > KTProcessorToolbox::ReleaseProcessor(const std::string& procName) + { + ProcMapIt it = fProcMap.find(procName); + if (it == fProcMap.end()) + { + KTWARN(proclog, "Processor <" << procName << "> was not found."); + return NULL; + } + std::shared_ptr< KTProcessor > procToRelease = it->second.fProc; + fProcMap.erase(it); + return procToRelease; + } + + void KTProcessorToolbox::ClearProcessors() + { + /*for (ProcMapIt it = fProcMap.begin(); it != fProcMap.end(); it++) + { + delete it->second.fProc; + }*/ //not required for smart pointers + fProcMap.clear(); + fRunQueue.clear(); + return; + } + + + bool KTProcessorToolbox::MakeConnection(const std::string& signal, const std::string& slot, int order) + { + string signalProcName, signalName; + if (! ParseSignalSlotName(signal, signalProcName, signalName)) + { + KTERROR(proclog, "Unable to parse signal name: <" << signal << ">"); + return false; + } + + string slotProcName, slotName; + if (! ParseSignalSlotName(slot, slotProcName, slotName)) + { + KTERROR(proclog, "Unable to parse slot name: <" << slot << ">"); + return false; + } + + return MakeConnection(signalProcName, signalName, slotProcName, slotName, order); + } + + bool KTProcessorToolbox::MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order) + { + std::shared_ptr< KTProcessor > signalProc = GetProcessor(signalProcName); + if (signalProc == NULL) + { + KTERROR(proclog, "Processor named <" << signalProcName << "> was not found!"); + return false; + } + + std::shared_ptr< KTProcessor > slotProc = GetProcessor(slotProcName); + if (slotProc == NULL) + { + KTERROR(proclog, "Processor named <" << slotProcName << "> was not found!"); + return false; + } + + try + { + if (order != std::numeric_limits< int >::min()) + { + signalProc->ConnectASlot(signalName, slotProc.get(), slotName, order); + } + else + { + signalProc->ConnectASlot(signalName, slotProc.get(), slotName); + } + } + catch (boost::exception& e) + { + KTERROR(proclog, "An error occurred while connecting signals and slots:\n" + << "\tSignal " << signalName << " from processor " << signalProcName << " (a.k.a. " << signalProc->GetConfigName() << ")" << '\n' + << "\tSlot " << slotName << " from processor " << slotProcName << " (a.k.a. " << slotProc->GetConfigName() << ")" << '\n' + << '\t' << diagnostic_information( e )); + return false; + } + + return true; + } + + bool KTProcessorToolbox::SetBreakpoint(const std::string& slot) + { + string slotProcName, slotName; + if (! ParseSignalSlotName(slot, slotProcName, slotName)) + { + KTERROR(proclog, "Unable to parse slot name: <" << slot << ">"); + return false; + } + + return SetBreakpoint(slotProcName, slotName); + } + + bool KTProcessorToolbox::SetBreakpoint(const std::string& slotProcName, const std::string& slotName) + { + std::shared_ptr< KTProcessor > slotProc = GetProcessor(slotProcName); + if (slotProc == NULL) + { + KTERROR(proclog, "Processor named <" << slotProcName << "> was not found!"); + return false; + } + + try + { + slotProc->SetDoBreakpoint(slotName, true); + return true; + } + catch (boost::exception& e) + { + KTERROR(proclog, "Unable to set breakpoint: " << diagnostic_information( e )); + return false; + } + } + + bool KTProcessorToolbox::RemoveBreakpoint(const std::string& slot) + { + string slotProcName, slotName; + if (! ParseSignalSlotName(slot, slotProcName, slotName)) + { + KTERROR(proclog, "Unable to parse slot name: <" << slot << ">"); + return false; + } + + return RemoveBreakpoint(slotProcName, slotName); + } + + bool KTProcessorToolbox::RemoveBreakpoint(const std::string& slotProcName, const std::string& slotName) + { + std::shared_ptr< KTProcessor > slotProc = GetProcessor(slotProcName); + if (slotProc == NULL) + { + KTERROR(proclog, "Processor named <" << slotProcName << "> was not found!"); + return false; + } + + try + { + slotProc->SetDoBreakpoint(slotName, false); + return true; + } + catch (boost::exception& e) + { + KTERROR(proclog, "Unable to set breakpoint: " << diagnostic_information( e )); + return false; + } + } + + bool KTProcessorToolbox::ParseSignalSlotName(const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot) const + { + size_t sepPos = toParse.find_first_of(fSigSlotNameSep); + if (sepPos == string::npos) + { + KTERROR(proclog, "Unable to find separator between processor and signal/slot name in <" << toParse << ">"); + return false; + } + nameOfProc = toParse.substr(0, sepPos); + nameOfSigSlot = toParse.substr(sepPos + 1); + return true; + } + + + bool KTProcessorToolbox::PushBackToRunQueue(const std::string& name) + { + ThreadGroup threadGroup; + + if (! AddProcessorToThreadGroup( name, threadGroup)) + { + KTERROR(proclog, "Unable to add processor <" << name << "> to thread group"); + return false; + } + + fRunQueue.push_back(threadGroup); + + KTINFO(proclog, "Added processor <" << name << "> to the run queue"); + return true; + } + + bool KTProcessorToolbox::PushBackToRunQueue(std::initializer_list< std::string > names) + { + return PushBackToRunQueue(std::vector< std::string >(names)); + } + + bool KTProcessorToolbox::PushBackToRunQueue(std::vector< std::string > names) + { + ThreadGroup threadGroup; + + std::stringstream toPrint; + for (const std::string& name : names) + { + if (! AddProcessorToThreadGroup(name, threadGroup)) + { + KTERROR(proclog, "Unable to add processor <" << name << "> to thread group"); + return false; + } + toPrint << name << ", "; // the extra comma at the end is removed below + } + + fRunQueue.push_back(threadGroup); + std::string toPrintString = toPrint.str(); + toPrintString.resize(toPrintString.size()-2); + KTINFO(proclog, "Added processors <" << toPrintString << "> to the run queue"); + return true; + } + + bool KTProcessorToolbox::AddProcessorToThreadGroup(const std::string& name, ThreadGroup& group) + { + std::shared_ptr< KTProcessor > procForRunQueue = GetProcessor(name); + KTDEBUG(proclog, "Attempting to add processor <" << name << "> to the run queue"); + if (procForRunQueue == NULL) + { + KTERROR(proclog, "Unable to find processor <" << name << "> requested for the run queue"); + return false; + } + + KTPrimaryProcessor* primaryProc = dynamic_cast< KTPrimaryProcessor* >(procForRunQueue.get()); + if (primaryProc == NULL) + { + KTERROR(proclog, "Processor <" << name << "> is not a primary processor."); + return false; + } + //group.insert(primaryProc); + group.insert(Thread(primaryProc, name)); + return true; + } + + void KTProcessorToolbox::AsyncRun() + { + if( fDoRunThread != nullptr ) + { + KTERROR( proclog, "It appears that a run has already been started" ); + return; + } + + fDoRunPromise = boost::promise< void >(); + fDoRunFuture = fDoRunPromise.get_future().share(); + + bool willRunSingleThreaded = fRunSingleThreaded; +#ifdef SINGLETHREADED + willRunSingleThreaded = true; +#endif + + if( willRunSingleThreaded ) + { + StartSingleThreadedRun(); + } + else + { + StartMultiThreadedRun(); + } + + return; + } + + void KTProcessorToolbox::StartSingleThreadedRun() + { + auto singleThreadRun = [&]() + { + try + { + KTPROG( proclog, "Starting single-threaded processing" ); + + for (RunQueue::iterator rqIter = fRunQueue.begin(); rqIter != fRunQueue.end(); ++rqIter) + { + for (ThreadGroup::iterator tgIter = rqIter->begin(); tgIter != rqIter->end(); ++tgIter) + { + boost_unique_lock breakContLock( fBreakContMutex ); + + std::string procName( tgIter->fName ); + KTINFO( proclog, "Starting processor <" << procName << ">" ); + + std::shared_ptr< KTThreadReference > thisThreadRef = std::make_shared< KTThreadReference >(); + thisThreadRef->Name() = procName; + + thisThreadRef->SetInitiateBreakFunc( [&](){ this->InitiateBreak(); } ); + thisThreadRef->SetWaitForContinueFunc( [&]( boost_unique_lock& lock ){ this->WaitForContinue( lock ); } ); + + fThreadReferences.push_back( thisThreadRef ); + + boost::condition_variable threadStartedCV; + boost::mutex threadStartedMutex; + bool threadStartedFlag = false; + + boost_unique_lock threadStartedLock( threadStartedMutex ); + boost::thread thread( [&](){ tgIter->fProc->operator()( thisThreadRef, threadStartedCV, threadStartedFlag ); } ); + KTDEBUG( proclog, "Thread ID is <" << thread.get_id() << ">; waiting for thread start" ); + while( ! threadStartedFlag ) + { + threadStartedCV.wait( threadStartedLock ); + } + KTDEBUG( proclog, "Thread has started" ); + + KTDEBUG( proclog, "Thread ID is <" << thread.get_id() << ">" ); + + bool stillRunning = true; // determines behavior that depends on whether a return from the thread was temporary or from the thread completing + do + { + boost::future_status status; + do + { + status = fThreadReferences.back()->GetDataPtrRetFuture().wait_for( boost::chrono::milliseconds(500) ); + } while (status != boost::future_status::ready); + + stillRunning = false; + if( fThreadReferences.back()->GetBreakFlag() ) + { + KTDEBUG( proclog, "Breakpoint reached (seen first in thread <" << procName << ">; may not be where breakpoint is set)" ); + + WaitForContinue( breakContLock ); + + KTDEBUG( proclog, "Breakpoint finished" ); + stillRunning = true; + } + else + { + try + { + fThreadReferences.back()->GetReturnValue(); + KTINFO( proclog, "Thread <" << procName << "> has finished" ); + fThreadReferences.pop_back(); + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct procTB_STRFinishing >( "There was an error while finishing thread <" + procName + "> and retrieving its results" ); + throw; + } + stillRunning = false; + } + } while( stillRunning ); + + KTINFO( proclog, "Processor <" << procName << "> has finished" ); + + thread.join(); + fThreadReferences.clear(); + } // end for loop over the thread group + } // end for loop over the run-queue + + KTPROG( proclog, "Processing is complete (single-threaded)" ); + } + catch( std::exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + KTERROR( proclog, "Caught std::exception thrown in a processor or in the single-threaded run function: " << e.what() ); + KTWARN( proclog, "Setting boost::exception of do-run-promise in StartSingleThreadedRun" ); + fDoRunPromise.set_exception( boost::current_exception() ); + return; + } + catch( boost::exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + KTERROR( proclog, "Caught boost::exception thrown in a processor or in the single-threaded run function" ); + KTWARN( proclog, "Setting boost::exception of do-run-promise in StartSingleThreadedRun" ); + fDoRunPromise.set_exception( boost::current_exception() ); + return; + } + fDoRunPromise.set_value(); + return; + }; // end single-threaded run lambda + + fDoRunThread = new boost::thread( singleThreadRun ); + return; + } + void KTProcessorToolbox::StartMultiThreadedRun() + { + auto multiThreadRun = [&]() + { + try + { + KTPROG( proclog, "Starting multi-threaded processing" ); + + for (RunQueue::iterator rqIter = fRunQueue.begin(); rqIter != fRunQueue.end(); ++rqIter) + { + boost::thread_group threads; + + { // scope for threadFuturesLock + boost_unique_lock breakContLock( fBreakContMutex ); + //boost_unique_lock threadFuturesLock( fThreadReferencesMutex ); + + for (ThreadGroup::iterator tgIter = rqIter->begin(); tgIter != rqIter->end(); ++tgIter) + { + std::string procName( tgIter->fName ); + KTINFO( proclog, "Starting processor <" << procName << ">" ); + + std::shared_ptr< KTThreadReference > thisThreadRef = std::make_shared< KTThreadReference >(); + thisThreadRef->Name() = procName; + + thisThreadRef->SetInitiateBreakFunc( [&](){ this->InitiateBreak(); } ); + thisThreadRef->SetWaitForContinueFunc( [&]( boost_unique_lock& lock ){ this->WaitForContinue( lock ); } ); + fThreadReferences.push_back( thisThreadRef ); + + boost::condition_variable threadStartedCV; + boost::mutex threadStartedMutex; + bool threadStartedFlag = false; + + boost_unique_lock threadStartedLock( threadStartedMutex ); + boost::thread* thisThread = new boost::thread( [&](){ tgIter->fProc->operator()( thisThreadRef, threadStartedCV, threadStartedFlag ); } ); + KTDEBUG( proclog, "Thread ID is <" << thisThread->get_id() << ">; waiting for thread start" ); + while( ! threadStartedFlag ) + { + threadStartedCV.wait( threadStartedLock ); + } + KTDEBUG( proclog, "Thread has started" ); + + threads.add_thread( thisThread ); + + }// end for loop over the thread group + } // end scope for threadFuturesLock + + bool stillRunning = true; // determines behavior that depends on whether a return from the thread was temporary or from the thread completing + do + { + auto finishedFuturePtr = boost::wait_for_any( KTThreadRefFutureIterator(fThreadReferences.begin()), KTThreadRefFutureIterator(fThreadReferences.end()) ); + boost_unique_lock breakContLock( fBreakContMutex ); + + size_t iThread = finishedFuturePtr - KTThreadRefFutureIterator(fThreadReferences.begin()); + auto finishedReferencePtr = fThreadReferences.begin() + iThread; + std::string threadName( (*finishedReferencePtr)->Name() ); + KTDEBUG( proclog, "Thread <" << iThread << " (" << threadName << ") > has stopped for some reason" ); + + stillRunning = false; + if( fDoRunBreakFlag ) + { + // a breakpoint has been reached + KTDEBUG( proclog, "Breakpoint reached (seen first in thread <" << threadName << ">; may not be where breakpoint is set)" ); + + // wait here for the user to continue + WaitForContinue( breakContLock ); + + KTDEBUG( proclog, "Breakpoint finished" ); + stillRunning = true; + //continueSignal = fMasterContSignal; // refresh the local copy of the shared future + } + else + { + // we're finished a thread; get its results + try + { + KTINFO( proclog, "Thread <" << threadName << "> has finished" ); + finishedFuturePtr->get(); + fThreadReferences.erase( finishedReferencePtr ); + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct procTB_MTRFinishing >( "There was an error while finishing thread <" + threadName + "> and retrieving its results" ); + throw; + } + + if( fThreadReferences.empty() ) stillRunning = false; + else stillRunning = true; + } + } while( stillRunning ); + + KTDEBUG( proclog, "Joining threads" ); + threads.join_all(); + boost_unique_lock breakContLock( fBreakContMutex ); + fThreadReferences.clear(); + } // end for loop over the run-queue + + KTPROG( proclog, "Processing is complete (multi-threaded)" ); + } + catch( std::exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + KTERROR( proclog, "Caught std::exception thrown in a processor or in the multi-threaded run function: " << e.what() ); + KTWARN( proclog, "Setting boost::exception of do-run-promise in StartMultiThreadedRun" ); + fDoRunPromise.set_exception( boost::current_exception() ); + return; + } + catch( boost::exception& e ) + { + // exceptions thrown in this function or from within processors will end up here + KTERROR( proclog, "Caught boost::exception thrown in a processor or in the multi-threaded run function" ); + KTWARN( proclog, "Setting boost::exception of do-run-promise in StartMultiThreadedRun" ); + fDoRunPromise.set_exception( boost::current_exception() ); + return; + } + KTWARN( proclog, "Setting value of do-run-promise in StartMultiThreadedRun" ); + fDoRunPromise.set_value(); + return; + }; // end multi-threaded run lambda + + fDoRunThread = new boost::thread( multiThreadRun ); + return; + } + + + bool KTProcessorToolbox::WaitForBreak() + { + boost_unique_lock breakContLock( fBreakContMutex ); + + if( fDoRunBreakFlag ) + { + KTWARN( proclog, "Can't wait for a break; Already at a breakpoint" ); + return true; + } + + boost::shared_future< void > doRunFuture = fDoRunFuture; + if( ! doRunFuture.valid() ) + { + BOOST_THROW_EXCEPTION( KTException() << "Cannot wait for a break in the current state (the \"DoRun\" future does not have a valid shared state)" << eom ); + } + + breakContLock.unlock(); + + // block here until there's a break + doRunFuture.wait(); + + if( fDoRunBreakFlag ) + { + KTDEBUG( proclog, "Breakpoint reached (the wait is over)" ); + return true; + } + + try + { + doRunFuture.get(); + KTDEBUG( proclog, "End-of-run reached (the wait-for-break is over)" ); + return false; + } + catch( boost::exception& e ) + { + KTERROR( proclog, "An error occurred during processing: " << boost::diagnostic_information( e ) ); + return false; + } + } + + void KTProcessorToolbox::WaitForEndOfRun() + { + // WaitForBreak() and WaitForContinue() throw boost::exception for problems with the future or promise objects + + KTDEBUG( proclog, "Waiting for end-of-run" ); + while( WaitForBreak() ) + { + KTDEBUG( proclog, "Reached breakpoint; waiting for continue" ); + boost::mutex localMutex; + boost_unique_lock localLock( localMutex ); + WaitForContinue( localLock ); + KTDEBUG( proclog, "Processing resuming; waiting for end-of-run" ); + } + KTDEBUG( proclog, "End-of-run reached" ); + + return; + } + + void KTProcessorToolbox::Continue() + { + if( ! fDoRunBreakFlag ) + { + KTWARN( proclog, "Not currently at a breakpoint" ); + return; + } + + boost_unique_lock breakContLock( fBreakContMutex ); + + // reset all break flags + fDoRunBreakFlag = false; + std::vector< boost_unique_lock > trLocks; + for( auto trIt = fThreadReferences.begin(); trIt != fThreadReferences.end(); ++trIt ) + { + trLocks.emplace_back( (*trIt)->Mutex() ); + (*trIt)->SetBreakFlag( false ); + (*trIt)->RefreshDataPtrRet(); + } + + // reset the do-run promise and future + fDoRunPromise = boost::promise< void >(); + fDoRunFuture = fDoRunPromise.get_future().share(); + + KTINFO( proclog, "Continuing from breakpoint" ); + // signal everything to resume + fDoContinue = true; + fContinueCV.notify_all(); + + return; + } + + KTDataHandle KTProcessorToolbox::GetData( const std::string& threadName ) + { + //boost_unique_lock threadFuturesLock( fThreadReferencesMutex ); + boost_unique_lock breakContLock( fBreakContMutex ); + + auto trIt = fThreadReferences.begin(); + for( ; trIt != fThreadReferences.end(); ++trIt ) + { + if( (*trIt)->Name() == threadName ) break; + } + if( trIt == fThreadReferences.end() ) + { + KTWARN( proclog, "Did not find thread <" << threadName << ">" ); + BOOST_THROW_EXCEPTION( KTException() << "Did not find thread <" << threadName << ">" << eom ); + } + + boost_unique_lock lock( (*trIt)->Mutex() ); + return (*trIt)->GetReturnValue(); + } + + void KTProcessorToolbox::InitiateBreak() + { + boost_unique_lock breakContLock( fBreakContMutex ); + if( fDoRunBreakFlag ) return; + + // set the continue flag to false so that things will wait for the continue + fDoContinue = false; + + // set all break flags + // master break flag + fDoRunBreakFlag = true; + // thread break flags + for( auto trIt = fThreadReferences.begin(); trIt != fThreadReferences.end(); ++trIt ) + { + boost_unique_lock lock( (*trIt)->Mutex() ); + (*trIt)->SetBreakFlag( true ); + } + + // use the do-run promise to signal the break + KTWARN( proclog, "Setting value of do-run-promise" ); + fDoRunPromise.set_value(); + + return; + } + + void KTProcessorToolbox::CancelThreads() + { + boost_unique_lock breakContLock( fBreakContMutex ); + for( auto trIt = fThreadReferences.begin(); trIt != fThreadReferences.end(); ++trIt ) + { + boost_unique_lock lock( (*trIt)->Mutex() ); + (*trIt)->SetCanceled( true ); + } + return; + } + + bool KTProcessorToolbox::Run() + { + // can throw boost::exception + + AsyncRun(); + + WaitForEndOfRun(); + + JoinRunThread(); + + return true; + } + +} /* namespace Nymph */ diff --git a/Library/Application/KTProcessorToolbox.hh b/Cpp/Library_v1/Application/KTProcessorToolbox.hh similarity index 54% rename from Library/Application/KTProcessorToolbox.hh rename to Cpp/Library_v1/Application/KTProcessorToolbox.hh index 8ac331e4..e64c7947 100644 --- a/Library/Application/KTProcessorToolbox.hh +++ b/Cpp/Library_v1/Application/KTProcessorToolbox.hh @@ -10,11 +10,29 @@ #define KTPROCESSORTOOLBOX_HH_ #include "KTConfigurable.hh" +<<<<<<< HEAD:Library_v1/Application/KTProcessorToolbox.hh +#include "KTMemberVariable.hh" +#include "KTThreadReference.hh" + +#include "factory.hh" +======= +>>>>>>> develop:Library/Application/KTProcessorToolbox.hh + +#define BOOST_THREAD_PROVIDES_FUTURE +#include +#include +#include + +#include +#include +#include #include #include #include #include +#include + namespace Nymph { @@ -44,6 +62,7 @@ namespace Nymph Available (nested) configuration values:
      +
    • run-single-threaded (bool) -- specify whether to run in single-threaded mode (will be ignored if the application has been compiled with the SINGLETHREADED flag set)
    • processors (array of objects) -- create a processor; each object in the array should consist of:
      • type -- string specifying the processor type (matches the string given to the Registrar, which should be specified before the class implementation in each processor's .cc file).
      • @@ -70,6 +89,9 @@ namespace Nymph */ class KTProcessorToolbox : public KTConfigurable { + private: + typedef boost::unique_lock< boost::mutex > boost_unique_lock; + public: KTProcessorToolbox(const std::string& name = "processor-toolbox"); virtual ~KTProcessorToolbox(); @@ -83,17 +105,12 @@ namespace Nymph /// Configure processors from a json-encoding of their configurations bool ConfigureProcessors(const std::string& config); - - public: - /// Process the run queue. - /// This will call Run() on all of the processors in the queue. - bool Run(); - + MEMBERVARIABLE( bool, RunSingleThreaded ); private: struct ProcessorInfo { - KTProcessor* fProc; + std::shared_ptr< KTProcessor > fProc; }; typedef std::map< std::string, ProcessorInfo > ProcessorMap; typedef ProcessorMap::iterator ProcMapIt; @@ -102,13 +119,13 @@ namespace Nymph public: /// Get a pointer to a processor in the toolbox - KTProcessor* GetProcessor(const std::string& procName); + std::shared_ptr< KTProcessor > GetProcessor(const std::string& procName); /// Get a pointer to a processor in the toolbox - const KTProcessor* GetProcessor(const std::string& procName) const; + const std::shared_ptr< KTProcessor > GetProcessor(const std::string& procName) const; /// Add a processor to the toolbox /// Toolbox takes ownership of the processor - bool AddProcessor(const std::string& procName, KTProcessor* proc); + bool AddProcessor(const std::string& procName, std::shared_ptr< KTProcessor > proc); bool AddProcessor(const std::string& procType, const std::string& procName); /// Remove a processor from the toolbox @@ -116,7 +133,7 @@ namespace Nymph /// Remove a processor from the toolbox and return it to the user /// Ownership is passed to the user - KTProcessor* ReleaseProcessor(const std::string& procName); + std::shared_ptr< KTProcessor > ReleaseProcessor(const std::string& procName); /// Remove all processors from the toolbox /// Also clears the run queue @@ -127,14 +144,31 @@ namespace Nymph public: + // for the MakeConnection overloading, extra overloading is used instead of default parameters so that the python interface works + /// Make a connection between the signal from one processor and the slot from another processor /// Both processors should already have been added to the Toolbox /// Signal and slot strings should be formatted as: [processor name]:[signal/slot name] - bool MakeConnection(const std::string& signal, const std::string& slot, int order = std::numeric_limits< int >::min()); + bool MakeConnection(const std::string& signal, const std::string& slot, int order); + bool MakeConnection(const std::string& signal, const std::string& slot) {return MakeConnection(signal, slot, std::numeric_limits< int >::min());} /// Make a connection between the signal from one processor and the slot from another processor /// Both processors should already have been added to the Toolbox - bool MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order = std::numeric_limits< int >::min()); + bool MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order); + bool MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName) + {return MakeConnection(signalProcName, signalName, slotProcName, slotName, std::numeric_limits< int >::min());} + + /// Set a breakpoint on a slot + /// Slot string should be formatted as: [processor name]:[slot name] + bool SetBreakpoint(const std::string& slot); + /// Set a breakpoint on a slot + bool SetBreakpoint(const std::string& slotProcName, const std::string& slotName); + + /// Remove a breakpoint from a slot + /// Slot string should be formatted as: [processor name]:[slot name] + bool RemoveBreakpoint(const std::string& slot); + /// Remove a breakpoint from a slot + bool RemoveBreakpoint(const std::string& slotProcName, const std::string& slotName); private: bool ParseSignalSlotName(const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot) const; @@ -164,12 +198,11 @@ namespace Nymph Thread(KTPrimaryProcessor* proc, const std::string& name) : fProc(proc), fName(name) {} }; - //typedef std::set< KTPrimaryProcessor* > ThreadGroup; struct CompareThread { bool operator() (const Thread& lhs, const Thread& rhs) const { - return lhs.fName < rhs.fName; + return lhs.fProc < rhs.fProc; } }; typedef std::set< Thread, CompareThread > ThreadGroup; @@ -178,8 +211,89 @@ namespace Nymph RunQueue fRunQueue; + public: + /// Process the run queue. + /// This will call Run() on all of the processors in the queue. + bool Run(); + + void AsyncRun(); + + void WaitForContinue( boost_unique_lock& lock ); + + /// Returns when processing is completed or a breakpoint is reached + /// Throws a boost::exception if there's an error with the future object in use + /// If the return is true, processing can continue after the break + /// If the return is false, processing has ended (either normally or due to an error) + bool WaitForBreak(); + + void WaitForEndOfRun(); + + void Continue(); + + void CancelThreads(); + + void JoinRunThread(); + + KTDataHandle GetData( const std::string& threadName ); + + private: + friend class KTThreadReference; + + typedef boost::shared_future< KTDataHandle > Future; + + void StartSingleThreadedRun(); + void StartMultiThreadedRun(); + + // called from KTThreadReference::Break + void InitiateBreak(); + + std::vector< std::shared_ptr< KTThreadReference > > fThreadReferences; + + boost::condition_variable fContinueCV; + bool fDoContinue; + boost::mutex fBreakContMutex; + + boost::thread* fDoRunThread; + boost::promise< void > fDoRunPromise; + boost::shared_future< void > fDoRunFuture; + bool fDoRunBreakFlag; + + }; + + template< class Value, class IIterator > + class KTThreadRefFutureIter : public boost::iterator_adaptor< KTThreadRefFutureIter< Value, IIterator >, IIterator, Value, boost::random_access_traversal_tag > + { + private: + // used for the conversion constructor below + struct enabler {}; + + public: + KTThreadRefFutureIter() : + KTThreadRefFutureIter::iterator_adaptor_() + {} + KTThreadRefFutureIter( const IIterator& other ) : + KTThreadRefFutureIter::iterator_adaptor_( other ) + {} + + // converts from Iterator to ConstIterator, but the enable_if business prevents converting from ConstIterator to Iterator + template< class OtherValue, class OtherIIterator > + KTThreadRefFutureIter( const KTThreadRefFutureIter< OtherValue, OtherIIterator > & other, typename boost::enable_if< boost::is_convertible< OtherValue, Value >, enabler >::type = enabler() ) : + KTThreadRefFutureIter::iterator_adaptor_( other.base ) + {} + + private: + friend class boost::iterator_core_access; + + Value& dereference() const + { + return (*(this->base_reference()))->GetDataPtrRetFuture(); + } }; + typedef KTThreadRefFutureIter< boost::unique_future< KTDataHandle >, std::vector< std::shared_ptr< KTThreadReference > >::iterator > KTThreadRefFutureIterator; + typedef KTThreadRefFutureIter< const boost::unique_future< KTDataHandle >, std::vector< std::shared_ptr< KTThreadReference > >::const_iterator > KTThreadRefFutureConstIterator; + + inline void KTProcessorToolbox::PopBackOfRunQueue() { fRunQueue.pop_back(); @@ -192,5 +306,26 @@ namespace Nymph return; } +<<<<<<< HEAD:Library_v1/Application/KTProcessorToolbox.hh + inline void KTProcessorToolbox::WaitForContinue( boost_unique_lock& lock ) + { + //fMasterContSignal.wait(); + while( ! fDoContinue ) + { + fContinueCV.wait( lock ); + } + return; + } + + inline void KTProcessorToolbox::JoinRunThread() + { + if( fDoRunThread == nullptr ) return; + fDoRunThread->join(); + delete fDoRunThread; + fDoRunThread = nullptr; + } + +======= +>>>>>>> develop:Library/Application/KTProcessorToolbox.hh } /* namespace Nymph */ #endif /* KTPROCESSORTOOLBOX_HH_ */ diff --git a/Cpp/Library_v1/Application/KTProcessorToolboxPybind.hh b/Cpp/Library_v1/Application/KTProcessorToolboxPybind.hh new file mode 100644 index 00000000..a0dc85d7 --- /dev/null +++ b/Cpp/Library_v1/Application/KTProcessorToolboxPybind.hh @@ -0,0 +1,100 @@ +/* + * KTProcessorToolboxPybind.hh + * + * Created on: Jan 29, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTPROCESSORTOOLBOXPYBIND_HH_ +#define NYMPH_KTPROCESSORTOOLBOXPYBIND_HH_ + +#include "KTProcessorToolbox.hh" + +#include "pybind11/pybind11.h" +#include "pybind11/stl.h" + +namespace Nymph +{ + void ExportKTProcessorToolbox( pybind11::module& mod ) + { + pybind11::class_< KTProcessorToolbox >( mod, "KTProcessorToolbox" ) + .def( pybind11::init< const std::string & >() ) + + // members related to configuration + .def( "ConfigureProcessors", (bool (KTProcessorToolbox::*)(const std::string&)) &KTProcessorToolbox::ConfigureProcessors, + "Configure processors from a json dictionary. Top-level keys are processor names, values are dictionaries with their configurations" ) + + // processor access + // GetProcessor skipped because raw pointers are not supported by pybind11 + .def( "AddProcessor", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&)) &KTProcessorToolbox::AddProcessor, + "Create a processor by name and add it to the toolbox" ) + .def("AddProcessor", (bool (KTProcessorToolbox::*)(const std::string&, std::shared_ptr< KTProcessor > proc)) &KTProcessorToolbox::AddProcessor, + "Create a processor and add it to the toolbox" ) + .def( "RemoveProcessor", &KTProcessorToolbox::RemoveProcessor, + "Remove a processor from the toolbox" ) + // skipping ReleaseProcessor + .def( "ClearProcessor", &KTProcessorToolbox::ClearProcessors, + "Remove all processors from the toolbox" ) + + // processor connections + .def( "MakeConnection", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&, int)) &KTProcessorToolbox::MakeConnection, + "Make a connection between the signal of one processor and the slot of another processor; the signal and slot strings should be formatted as: [processor name]:[signal/slot name]" ) + .def( "MakeConnection", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&)) &KTProcessorToolbox::MakeConnection, + "Make a connection between the signal of one processor and the slot of another processor; the signal and slot strings should be formatted as: [processor name]:[signal/slot name]" ) + .def( "MakeConnection", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&, const std::string&, const std::string&, int)) &KTProcessorToolbox::MakeConnection, + "Make a connection between the signal of one processor and the slot of another processor" ) + .def( "MakeConnection", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&, const std::string&, const std::string&)) &KTProcessorToolbox::MakeConnection, + "Make a connection between the signal of one processor and the slot of another processor" ) + + // breakpoints + .def( "SetBreakpoint", (bool (KTProcessorToolbox::*)(const std::string&)) &KTProcessorToolbox::SetBreakpoint, + "Set a breakpoint at a specific slot; slot string should be formatted as: [processor name]:[slot name]" ) + .def( "SetBreakpoint", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&)) &KTProcessorToolbox::SetBreakpoint, + "Set a breakpoint at a specific slot" ) + + .def( "RemoveBreakpoint", (bool (KTProcessorToolbox::*)(const std::string&)) &KTProcessorToolbox::RemoveBreakpoint, + "Remove a breakpoint; slot string should be formatted as: [processor name]:[slot name]" ) + .def( "RemoveBreakpoint", (bool (KTProcessorToolbox::*)(const std::string&, const std::string&)) &KTProcessorToolbox::RemoveBreakpoint, + "Remove a breakpoint" ) + + // run queue + .def( "PushBackToRunQueue", (bool (KTProcessorToolbox::*)(const std::string&)) &KTProcessorToolbox::PushBackToRunQueue, + "Push one or more processors to the back of the run queue using a comma-separated string" ) + .def( "PushBackToRunQueue", (bool (KTProcessorToolbox::*)(std::vector< std::string >)) &KTProcessorToolbox::PushBackToRunQueue, + "Push one or more processors to the back of the run queue using a vector of strings" ) + + .def( "PopBackOfRunQueue", &KTProcessorToolbox::PopBackOfRunQueue, + "Remove the last item in the run queue, whether it's a single processor or a group" ) + .def( "ClearRunQueue", &KTProcessorToolbox::ClearRunQueue, + "Remove all items from the run queue" ) + + // run methods + .def( "Run", &KTProcessorToolbox::Run, "Start the processors listed in the run queue (blocking)" ) + .def( "AsyncRun", &KTProcessorToolbox::AsyncRun, "Non-blocking call to Run()" ) + .def( "WaitForBreak", &KTProcessorToolbox::WaitForBreak, "Blocking call that waits until a breakpoint is reached; you must manually Continue() to proceed" ) + .def( "WaitForEndOfRun", &KTProcessorToolbox::WaitForEndOfRun, "Blocking call to Continue() execution until the end of the run is reached; you must manually Continue() to proceed" ) + .def( "Continue", &KTProcessorToolbox::Continue, "Resume execution (non-blocking)" ) + .def( "CancelThreads", &KTProcessorToolbox::CancelThreads, "Kill any running thread at the next breakpoint check" ) + .def( "JoinRunThread", &KTProcessorToolbox::JoinRunThread, "Blocking call to wait until Run() is complete; this should only be done by the owner of the thread (typically whoever called Run())" ) + + .def( "GetData", &KTProcessorToolbox::GetData, "Retrieve the data handle from a slot by thread name (i.e. the name of the primary processor for that thread)" ); + + + /* + // these are implemented in the boost::python version but not yet here + + PROPERTYMEMBER(KTProcessorToolbox, RunSingleThreaded) + + // Processor access + .def("GetProcessor", GetProcessor_wrap, return_value_policy(), "Get a pointer to a processor in the toolbox") + .def("AddProcessor", AddProcessor_Ref, "add a processor to the toolbox, toolbox takes ownership") + // TODO: Not 100% certain that the reference count for this processor is now correct, given the return_value_policy + .def("ReleaseProcessor", &KTProcessorToolbox::ReleaseProcessor, return_value_policy(), "Remove a processor from the toolbox and return it to the user, ownership is passed") + */ + ; + + } + +} + +#endif /* NYMPH_KTPROCESSORTOOLBOXPYBIND_HH_ */ diff --git a/Library/Application/KTRunNymph.cc b/Cpp/Library_v1/Application/KTRunNymph.cc similarity index 89% rename from Library/Application/KTRunNymph.cc rename to Cpp/Library_v1/Application/KTRunNymph.cc index c3322d14..7912d069 100644 --- a/Library/Application/KTRunNymph.cc +++ b/Cpp/Library_v1/Application/KTRunNymph.cc @@ -33,7 +33,11 @@ namespace Nymph // This will create all of the requested processors, connect their signals and slots, and fill the run queue. KTProcessorToolbox procTB; +<<<<<<< HEAD:Library_v1/Application/KTRunNymph.cc + if ( ! procTB.Configure( parentConfigNode[procTB.GetConfigName()].as_node() ) ) +======= if( ! procTB.Configure( parentConfigNode[procTB.GetConfigName()].as_node() ) ) +>>>>>>> develop:Library/Application/KTRunNymph.cc { KTERROR( nlog, "Unable to configure processor toolbox. Aborting." ); return -3; diff --git a/Library/Application/KTRunNymph.hh b/Cpp/Library_v1/Application/KTRunNymph.hh similarity index 100% rename from Library/Application/KTRunNymph.hh rename to Cpp/Library_v1/Application/KTRunNymph.hh diff --git a/Library/Application/KTThroughputProfiler.cc b/Cpp/Library_v1/Application/KTThroughputProfiler.cc similarity index 95% rename from Library/Application/KTThroughputProfiler.cc rename to Cpp/Library_v1/Application/KTThroughputProfiler.cc index d30ddc8c..2dc9fcd0 100644 --- a/Library/Application/KTThroughputProfiler.cc +++ b/Cpp/Library_v1/Application/KTThroughputProfiler.cc @@ -28,9 +28,9 @@ namespace Nymph fTimeEnd(), fNDataProcessed(0) { - RegisterSlot("start", this, &KTThroughputProfiler::StartProfiling); - RegisterSlot("data", this, &KTThroughputProfiler::Data); - RegisterSlot("stop", this, &KTThroughputProfiler::Finish); + RegisterSlot("start", this, &KTThroughputProfiler::StartProfiling, {}); + RegisterSlot("data", this, &KTThroughputProfiler::Data, {}); + RegisterSlot("stop", this, &KTThroughputProfiler::Finish, {}); }; KTThroughputProfiler::~KTThroughputProfiler() @@ -64,7 +64,7 @@ namespace Nymph return Diff(fTimeStart, fTimeEnd); } - void KTThroughputProfiler::StartProfiling(KTDataPtr header) + void KTThroughputProfiler::StartProfiling(KTDataHandle header) { KTINFO(proflog, "Profiling started"); fNDataProcessed = 0; @@ -72,7 +72,7 @@ namespace Nymph return; } - void KTThroughputProfiler::Data(KTDataPtr data) + void KTThroughputProfiler::Data(KTDataHandle data) { (void)data; fNDataProcessed++; diff --git a/Library/Application/KTThroughputProfiler.hh b/Cpp/Library_v1/Application/KTThroughputProfiler.hh similarity index 95% rename from Library/Application/KTThroughputProfiler.hh rename to Cpp/Library_v1/Application/KTThroughputProfiler.hh index 7eb3ca61..0d7509f0 100644 --- a/Library/Application/KTThroughputProfiler.hh +++ b/Cpp/Library_v1/Application/KTThroughputProfiler.hh @@ -14,7 +14,7 @@ #include "KTProcessor.hh" -#include "KTData.hh" +#include "KTCoreData.hh" #include #include @@ -70,7 +70,7 @@ namespace Nymph Slots: - "start": void (KTEggHeader*) -- Start the timer - - "data": void (KTDataPtr) -- Increment the counter on the number of data slices + - "data": void (KTDataHandle) -- Increment the counter on the number of data slices - "stop": void () -- Stop the timer */ @@ -86,9 +86,9 @@ namespace Nymph void Start(); void Stop(); - void StartProfiling(KTDataPtr data); + void StartProfiling(KTDataHandle data); - void Data(KTDataPtr data); + void Data(KTDataHandle data); void Finish(); diff --git a/Cpp/Library_v1/CMakeLists.txt b/Cpp/Library_v1/CMakeLists.txt new file mode 100644 index 00000000..c03d3259 --- /dev/null +++ b/Cpp/Library_v1/CMakeLists.txt @@ -0,0 +1,121 @@ +# CMakeLists for Nymph/Library_v1 +# Author: N. Oblath + +include( PythonPackage ) + +set( UTIL_DIR Utility ) +set( DATA_DIR Data ) +set( PROC_DIR Processor ) +set( IO_DIR IO ) +set( APPL_DIR Application ) + +set( NYMPH_HEADERFILES +# ${UTIL_DIR}/KTCacheDirectory.hh +# ${UTIL_DIR}/KTConcurrentQueue.hh +# ${UTIL_DIR}/KTConfigurable.hh +# ${UTIL_DIR}/KTDirectory.hh +# ${UTIL_DIR}/KTEventLoop.hh +# ${UTIL_DIR}/KTException.hh +# ${UTIL_DIR}/KTExtensible.hh +# ${UTIL_DIR}/KTExtensibleStruct.hh +# ${UTIL_DIR}/KTExtensibleStructFactory.hh +# ${UTIL_DIR}/KTLogger.hh +# ${UTIL_DIR}/KTMemberVariable.hh +# ${UTIL_DIR}/KTTIFactory.hh +# ${UTIL_DIR}/KTTime.hh +# ${DATA_DIR}/KTApplyCut.hh +# ${DATA_DIR}/KTCoreData.hh +# ${DATA_DIR}/KTCut.hh +# ${DATA_DIR}/KTCutFilter.hh +# ${DATA_DIR}/KTCutResult.hh +# ${DATA_DIR}/KTCutStatus.hh +# ${DATA_DIR}/KTData.hh +# ${DATA_DIR}/KTRegisterNymphExtData.hh +# ${PROC_DIR}/KTConnection.hh +# ${PROC_DIR}/KTPrimaryProcessor.hh +# ${PROC_DIR}/KTProcessor.hh +# ${PROC_DIR}/KTSignal.hh +# ${PROC_DIR}/KTSignalWrapper.hh +# ${PROC_DIR}/KTSlot.hh +# ${PROC_DIR}/KTSlotWrapper.hh +# ${PROC_DIR}/KTThreadReference.hh +# ${IO_DIR}/KTJSONWriter.hh +# ${IO_DIR}/KTReader.hh +# ${IO_DIR}/KTWriter.hh +# ${APPL_DIR}/KTApplication.hh +# ${APPL_DIR}/KTCommandLineHandler.hh +# ${APPL_DIR}/KTCommandLineOption.hh +# ${APPL_DIR}/KTConfigurator.hh +# ${APPL_DIR}/KTDataQueueProcessor.hh +# ${APPL_DIR}/KTFilenameParsers.hh +# ${APPL_DIR}/KTPrintDataStructure.hh +# ${APPL_DIR}/KTProcessorToolbox.hh +# ${APPL_DIR}/KTRunNymph.hh +# ${APPL_DIR}/KTThroughputProfiler.hh + ${UTIL_DIR}/Exception.hh + ${UTIL_DIR}/MemberVariable.hh +) + +set( NYMPH_SOURCEFILES +# ${UTIL_DIR}/KTCacheDirectory.cc +# ${UTIL_DIR}/KTConfigurable.cc +# ${UTIL_DIR}/KTDirectory.cc +# ${UTIL_DIR}/KTEventLoop.cc +# ${UTIL_DIR}/KTException.cc +# ${UTIL_DIR}/KTTime.cc +# ${DATA_DIR}/KTApplyCut.cc +# ${DATA_DIR}/KTCoreData.cc +# ${DATA_DIR}/KTCut.cc +# ${DATA_DIR}/KTCutFilter.cc +# ${DATA_DIR}/KTCutStatus.cc +# ${DATA_DIR}/KTData.cc +# ${PROC_DIR}/KTPrimaryProcessor.cc +# ${PROC_DIR}/KTProcessor.cc +# ${PROC_DIR}/KTSignalWrapper.cc +# ${PROC_DIR}/KTSlotWrapper.cc +# ${PROC_DIR}/KTThreadReference.cc +# ${IO_DIR}/KTJSONWriter.cc +# ${IO_DIR}/KTReader.cc +# ${IO_DIR}/KTWriter.cc +# ${APPL_DIR}/KTApplication.cc +# ${APPL_DIR}/KTCommandLineHandler.cc +# ${APPL_DIR}/KTConfigurator.cc +# ${APPL_DIR}/KTDataQueueProcessor.cc +# ${APPL_DIR}/KTFilenameParsers.cc +# ${APPL_DIR}/KTPrintDataStructure.cc +# ${APPL_DIR}/KTProcessorToolbox.cc +# ${APPL_DIR}/KTRunNymph.cc +# ${APPL_DIR}/KTThroughputProfiler.cc + ${UTIL_DIR}/Exception.cc +) + +# Python binding headers +set( NYMPH_PYBINDING_HEADERFILES + #${UTIL_DIR}/KTConfigurablePybind.hh + ${UTIL_DIR}/KTParamPybind.hh + ${PROC_DIR}/KTProcessorPybind.hh + ${PROC_DIR}/KTPyProcessor.hh +) + +# Python bindings +set( NYMPH_PYBINDING_SOURCEFILES + ${PROC_DIR}/KTProcessorPybind.cc + NymphPybind.cc +) + +################################################## + +pbuilder_library( Nymph NYMPH_SOURCEFILES "" ) +pbuilder_install_headers( ${NYMPH_HEADERFILES} ) + +if( Nymph_ENABLE_PYTHON ) + set( LIB_DEPENDENCIES Nymph ) + + configure_file( AddLibPythonPath.sh.in AddLibPythonPath.sh @ONLY) + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/AddLibPythonPath.sh DESTINATION ${BIN_INSTALL_DIR} ) + + pybind11_add_module( py_nymph MODULE ${NYMPH_PYBINDING_SOURCEFILES} ) + target_link_libraries( py_nymph PRIVATE ${LIB_DEPENDENCIES} ${EXTERNAL_LIBRARIES} ) + pbuilder_install_libraries( py_nymph ) + pbuilder_install_headers( ${NYMPH_PYBINDING_HEADERFILES} ) +endif( Nymph_ENABLE_PYTHON ) diff --git a/Library/Data/KTApplyCut.cc b/Cpp/Library_v1/Data/KTApplyCut.cc similarity index 78% rename from Library/Data/KTApplyCut.cc rename to Cpp/Library_v1/Data/KTApplyCut.cc index 874bd33b..fc57302d 100644 --- a/Library/Data/KTApplyCut.cc +++ b/Cpp/Library_v1/Data/KTApplyCut.cc @@ -25,7 +25,7 @@ namespace Nymph fAfterCutPassSignal("pass", this), fAfterCutFailSignal("fail", this) { - RegisterSlot("apply", this, &KTApplyCut::ApplyCut); + fApplyCutSW = RegisterSlot("apply", this, &KTApplyCut::ApplyCut, {"all", "pass", "fail"}); } KTApplyCut::~KTApplyCut() @@ -82,25 +82,29 @@ namespace Nymph } - void KTApplyCut::ApplyCut(KTDataPtr dataPtr) + void KTApplyCut::ApplyCut(KTDataHandle dataHandle) { + std::shared_ptr< KTThreadReference > ref = fApplyCutSW->GetThreadRef(); + if (fCut == NULL) { - KTERROR(cutlog, "No cut was specified"); + THROW_THREADREF_EXCEPTION( ref, KTException() << "No cut was specified" ); return; } - bool cutFailed = fCut->Apply(dataPtr); + bool cutFailed = fCut->Apply(dataHandle); + + ref->Break( dataHandle, fApplyCutSW->GetDoBreakpoint() ); if (cutFailed) { - fAfterCutFailSignal(dataPtr); + fAfterCutFailSignal(dataHandle); } else { - fAfterCutPassSignal(dataPtr); + fAfterCutPassSignal(dataHandle); } - fAfterCutSignal(dataPtr); + fAfterCutSignal(dataHandle); return; } diff --git a/Library/Data/KTApplyCut.hh b/Cpp/Library_v1/Data/KTApplyCut.hh similarity index 79% rename from Library/Data/KTApplyCut.hh rename to Cpp/Library_v1/Data/KTApplyCut.hh index 704bf8f3..e9d3b7e7 100644 --- a/Library/Data/KTApplyCut.hh +++ b/Cpp/Library_v1/Data/KTApplyCut.hh @@ -43,12 +43,12 @@ namespace Nymph - "[cut name]": subtree -- specifies the cut to be used; parent node for the cut configuration Slots: - - "apply": void (KTDataPtr) -- Applies the cut to the received data; Requirements are set by the cut; No data is added. + - "apply": void (KTDataHandle) -- Applies the cut to the received data; Requirements are set by the cut; No data is added. Signals: - - "all": void (KTDataPtr) -- Emitted upon application of the cut regardless of cut result. - - "pass": void (KTDataPtr) -- Emitted upon application of the cut if the cut passed. - - "fail": void (KTDataPtr) -- Emitted upon application of the cut if the cut failed. + - "all": void (KTDataHandle) -- Emitted upon application of the cut regardless of cut result. + - "pass": void (KTDataHandle) -- Emitted upon application of the cut if the cut passed. + - "fail": void (KTDataHandle) -- Emitted upon application of the cut if the cut failed. */ class KTApplyCut : public KTProcessor @@ -67,7 +67,7 @@ namespace Nymph KTCut* fCut; public: - void ApplyCut(KTDataPtr); + void ApplyCut(KTDataHandle dataHandle); //*************** @@ -83,6 +83,8 @@ namespace Nymph // Slots //*************** + KTSlotWrapper* fApplyCutSW; + private: }; diff --git a/Cpp/Library_v1/Data/KTCoreData.cc b/Cpp/Library_v1/Data/KTCoreData.cc new file mode 100644 index 00000000..9b80e3b2 --- /dev/null +++ b/Cpp/Library_v1/Data/KTCoreData.cc @@ -0,0 +1,33 @@ +/* + * KTCoreData.cc + * + * Created on: Aug 24, 2012 + * Author: nsoblath + */ + +#include "KTCoreData.hh" + +namespace Nymph +{ + KTCoreData::KTCoreData() : + KTData(), + fCounter( 0 ), + fLastData( false ), + fCutStatus() + { + std::cout << "### KTCoreData constructor" << std::endl; + } + + KTCoreData::~KTCoreData() + { + std::cout << "### KTCoreData destrutor" << std::endl; + } + + + KTDataHandle CreateNewDataHandle() + { + return std::make_shared< KTCoreDataExt >(); + } + + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Data/KTCoreData.hh b/Cpp/Library_v1/Data/KTCoreData.hh new file mode 100644 index 00000000..f6d6901c --- /dev/null +++ b/Cpp/Library_v1/Data/KTCoreData.hh @@ -0,0 +1,88 @@ +/* + * KTCoreData.hh + * + * Created on: Aug 24, 2012 + * Author: nsoblath + */ + +#ifndef KTCOREDATA_HH_ +#define KTCOREDATA_HH_ + +#include "KTData.hh" + +#include "KTCutStatus.hh" +#include "KTLogger.hh" +#include "KTMemberVariable.hh" + +#include +#include + +namespace Nymph +{ + LOGGER( cdlog_h, "KTCoreData.hh" ) + + class KTCoreData : public KTData + { + public: + KTCoreData(); + virtual ~KTCoreData(); + + MEMBERVARIABLE( unsigned, Counter ); + MEMBERVARIABLE( bool, LastData ); + + MEMBERVARIABLE_REF( KTCutStatus, CutStatus ); + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ) + { + std::cout << "### serialize for KTCoreData" << std::endl; + ar( cereal::base_class< KTData >( this ), CEREAL_NVP(fCounter), CEREAL_NVP(fLastData), CEREAL_NVP(fCutStatus) ); + } + }; + + DEFINE_EXT_DATA( KTCoreData, "core" ) + + + // Define KTDataHandle and a convenience function to create new handles + typedef std::shared_ptr< KTCoreDataExt > KTDataHandle; + KTDataHandle CreateNewDataHandle(); + + + // Recursive helper structs for determining if data objects are present + + // End of the recursion; this will only be used if NoOtherDataTypes has nothing in it + template< class... NoOtherDataTypes > + struct DataPresentHelper + { + static bool DataPresent( KTDataHandle ); + }; + + template< class DataType, class... OtherDataTypes > + struct DataPresentHelper< DataType, OtherDataTypes... > + { + static bool DataPresent( KTDataHandle data ); + }; + + template< class... NoOtherDataTypes > + bool DataPresentHelper< NoOtherDataTypes... >::DataPresent( KTDataHandle ) + { + return true; + } + + template< class DataType, class... OtherDataTypes > + bool DataPresentHelper< DataType, OtherDataTypes... >::DataPresent( KTDataHandle data ) + { + if( ! data->Has< DataType >() ) + { + KTERROR( cdlog_h, "Data not found with type <" << typeid( DataType ).name() << ">" ); + return false; + } + return DataPresentHelper< OtherDataTypes... >::DataPresent( data ); + } + + +} /* namespace Nymph */ +#endif /* KTCOREDATA_HH_ */ diff --git a/Library/Data/KTCut.cc b/Cpp/Library_v1/Data/KTCut.cc similarity index 100% rename from Library/Data/KTCut.cc rename to Cpp/Library_v1/Data/KTCut.cc diff --git a/Cpp/Library_v1/Data/KTCut.hh b/Cpp/Library_v1/Data/KTCut.hh new file mode 100644 index 00000000..753d83ac --- /dev/null +++ b/Cpp/Library_v1/Data/KTCut.hh @@ -0,0 +1,209 @@ +/* + * KTCut.hh + * + * Created on: Sept 19, 2014 + * Author: nsoblath + */ + +#ifndef KTCUT_HH_ +#define KTCUT_HH_ + +#include "KTConfigurable.hh" +#include "KTCutResult.hh" +#include "KTCoreData.hh" +#include "KTException.hh" +#include "KTExtensibleStructFactory.hh" +#include "KTLogger.hh" +#include "KTMemberVariable.hh" + +#include "factory.hh" +#include "typename.hh" + +#include + +namespace Nymph +{ + KTLOGGER(cutlog_h, "KTCut.h"); + + /*! + @class KTCut + @author N. S. Oblath + + @brief Base class for a cut that gets applied to data objects. + + @details + A fully implemented cut MUST have the following: + - Public nested class called Result, inheriting from KTExtensibleCutResult< Result >, and containing a public static std::string name sName. + - Cut registration using the macro KT_REGISTER_CUT([class name]) + - Implementation of bool Configure(const scarab::param_node*) + - Implementation of bool Apply(KTCoreData&, ) + + Your cut class should inherit from KTCutOneArg or KTCutTwoArgs, depending on the number of data types involved in your cut. + + The existence of [class name]::Result and [class name]::Result::sName are enforces at compile time by the KT_REGISTER_CUT macro. + + The functions bool Configure(const scarab::param_node*) and void Apply(KTCoreData&, ) are abstract in the base classes, and therefore must be implemented. + + Boolean return value interpretation: + - TRUE means the cut was failed + - FALSE means the cut was passed + + -------------------------------------- + ------- Example Cut Definition ------- + -------------------------------------- + + class KTSomeData; + class KTSomeDataExt; + + // Data must be at least as awesome as fAwesomenessThreshold to pass this cut + class KTAwesomenessCut : public KTCutOnData< KTSomeDataExt > + { + public: + struct Result : KTExtensibleCutResult< Result > + { + static const std::string sName; + }; + + public: + KTAwesomenessCut(const std::string& name = "default-example-cut"); + virtual ~KTAwesomenessCut(); + + bool Configure(const scarab::param_node* node); + + MEMBERVARIABLE(double, AwesomenessThreshold); + + public: + bool Apply(KTCoreData& data, KTSomeData& data); + + }; + + -------------------------------------- + ------- Example Implementation ------- + -------------------------------------- + + const std::string KTAwesomenessCut::Result::sName = "awesomeness-cut"; + + KT_REGISTER_CUT(KTAwesomenessCut); + + KTAwesomenessCut::KTAwesomenessCut(const std::string& name) : + KTCutOnData(name), + fAwesomenessThreshold(1000000.) + { + SetApplyFunc( this, &KTAwesomeCut::Apply ); + } + + KTAwesomenessCut::~KTExampleCut() + {} + + bool KTAwesomenessCut::Configure(const scarab::param_node* node) + { + if (node == NULL) return true; + SetAwesomenessThreshold(node->get_value("awesomeness", GetAwesomenessThreshold())); + return true; + } + + bool KTAwesomenessCut::Apply(KTCoreData& data, KTSomeData& someData) + { + bool isCut = someData.Awesomeness() < fAwesomenessThreshold; + data.GetCutStatus().AddCutResult< KTAwesomenessCut::Result >(isCut); + return isCut; + } + + */ + + //************************************ + // KTCut -- base class for all cuts + //************************************ + + class KTCut : public KTConfigurable + { + public: + KTCut(const std::string& name = "default-cut-name"); + virtual ~KTCut(); + + virtual bool Apply(KTDataHandle) = 0; + + }; + + //******************************************************* + // KTCutOnData -- cut applied to one or more data objects + //******************************************************* + + template< class ... XExtDataTypes > + class KTCutOnData : public KTCut + { + public: + KTCutOnData( const std::string& name = "default-cut-name" ); + virtual ~KTCutOnData(); + + virtual bool Apply( KTDataHandle dataHandle ); + + protected: + template< class... SomeExtDataTypes > + bool DataPresent( KTDataHandle data ); + + template< class XFuncOwnerType, class... XFuncDataTypes > + void SetApplyFunc( XFuncOwnerType* owner, bool (XFuncOwnerType::*func)( KTCoreData&, const XFuncDataTypes&... ) ); + + std::function< bool ( KTCoreDataExt& data, const XExtDataTypes&... dataType ) > fFunc; + + }; + + + //******************* + // Implementations + //******************* + + template< class ... XExtDataTypes > + KTCutOnData< XExtDataTypes... >::KTCutOnData( const std::string& name ) : + KTCut(name) + { + } + + template< class ... XExtDataTypes > + KTCutOnData< XExtDataTypes... >::~KTCutOnData() + {} + + template< class ... XExtDataTypes > + bool KTCutOnData< XExtDataTypes... >::Apply( KTDataHandle dataHandle ) + { + // Check to ensure that the required data type is present + if( ! DataPresent< XExtDataTypes... >( dataHandle ) ) + { + KTERROR( cutlog_h, "Failed to find all of the necessary data types in slot <" << fConfigName << ">. Aborting." ); + BOOST_THROW_EXCEPTION( KTException() << "Failed to find all of the necessary data types in slot <" << fConfigName << ">. Aborting." << eom ); + } + + try + { + return fFunc( dataHandle->Of< KTCoreDataExt >() , dataHandle->Of< XExtDataTypes >()... ); + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct slotData_RunFunc >( "Something went wrong in slot <" + fConfigName + ">. Aborting." ); + throw; + } + } + + template< class... XExtDataTypes > + template< typename... SomeExtDataTypes > + bool KTCutOnData< XExtDataTypes... >::DataPresent( KTDataHandle data ) + { + return DataPresentHelper< SomeExtDataTypes... >::DataPresent( data ); + } + + template< class... XExtDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + void KTCutOnData< XExtDataTypes... >::SetApplyFunc( XFuncOwnerType* owner, bool (XFuncOwnerType::*func)( KTCoreData&, const XFuncDataTypes&... ) ) + { + fFunc = [func, owner]( KTCoreDataExt& data, const XExtDataTypes&... testData )->bool {return (owner->*func)(data, testData...);}; + } + + + // this macro enforces the existence of cut_class::Result and cut_class::Result::sName at compile time +#define KT_REGISTER_CUT(cut_class, cut_name) \ + static ::scarab::registrar< ::Nymph::KTCut, cut_class, const std::string& > sCut##cut_class##Registrar( cut_name ); + +} /* namespace Nymph */ + +#endif /* KTCUT_HH_ */ diff --git a/Library/Data/KTCutFilter.cc b/Cpp/Library_v1/Data/KTCutFilter.cc similarity index 66% rename from Library/Data/KTCutFilter.cc rename to Cpp/Library_v1/Data/KTCutFilter.cc index 89126454..05d19066 100644 --- a/Library/Data/KTCutFilter.cc +++ b/Cpp/Library_v1/Data/KTCutFilter.cc @@ -28,7 +28,7 @@ namespace Nymph fAfterCutPassSignal("pass", this), fAfterCutFailSignal("fail", this) { - RegisterSlot("filter", this, &KTCutFilter::FilterData); + fFilterDataSW = RegisterSlot("filter", this, &KTCutFilter::FilterData, {"all", "pass", "fail"}); } KTCutFilter::~KTCutFilter() @@ -54,14 +54,14 @@ namespace Nymph return true; } - bool KTCutFilter::Filter(KTData& data) + bool KTCutFilter::Filter(const KTCoreData& data) { if (fAllBits) { - return data.GetCutStatus().IsCut(); + return data.CutStatus().IsCut(); } - KTCutStatus& cutStatus = data.GetCutStatus(); + const KTCutStatus& cutStatus = data.CutStatus(); if (fConvertToBitset) { fCutMask = cutStatus.ToBitset(fCutMaskInt); @@ -72,18 +72,25 @@ namespace Nymph return cutStatus.IsCut(fCutMask); } - void KTCutFilter::FilterData(KTDataPtr dataPtr) + void KTCutFilter::FilterData(KTDataHandle dataHandle) { - // all KTDataPtr's have KTData, so we won't bother checking - if (Filter(dataPtr->Of< KTData >())) + std::shared_ptr< KTThreadReference > ref = fFilterDataSW->GetThreadRef(); + + // all KTDataHandle's have KTCoreData, so we won't bother checking + + bool failCut = Filter(dataHandle->Of< KTCoreDataExt >()); + + ref->Break( dataHandle, fFilterDataSW->GetDoBreakpoint() ); + + if (failCut) { - fAfterCutFailSignal(dataPtr); + fAfterCutFailSignal(dataHandle); } else { - fAfterCutPassSignal(dataPtr); + fAfterCutPassSignal(dataHandle); } - fAfterCutSignal(dataPtr); + fAfterCutSignal(dataHandle); return; } diff --git a/Library/Data/KTCutFilter.hh b/Cpp/Library_v1/Data/KTCutFilter.hh similarity index 84% rename from Library/Data/KTCutFilter.hh rename to Cpp/Library_v1/Data/KTCutFilter.hh index 3b501e41..dacd5717 100644 --- a/Library/Data/KTCutFilter.hh +++ b/Cpp/Library_v1/Data/KTCutFilter.hh @@ -33,7 +33,7 @@ namespace Nymph KTCutFilter checks the status of cuts that have already been applied to a data. If the bitwise AND of the cut status with the configurable cut mask is non-zero, than the data fails the filter. - Interpretation of the boolean returned by Filter(KTData&): + Interpretation of the boolean returned by Filter(KTCoreData&): - TRUE means the data failed the cut filter. - FALSE means the data passed the cut filter. @@ -46,12 +46,12 @@ namespace Nymph - "cut-mask-int": unsigned int -- Set the cut mask with an unsigned integer's bit values. Slots: - - "filter": void (KTDataPtr) -- Checks the cut status of the received data ANDed with the cut mask; No data is added. + - "filter": void (KTDataHandle) -- Checks the cut status of the received data ANDed with the cut mask; No data is added. Signals: - - "all": void (KTDataPtr) -- Emitted after cut status is checked for all data. - - "pass": void (KTDataPtr) -- Emitted after cut status is checked if the cut filter passed. - - "fail": void (KTDataPtr) -- Emitted after cut status is checked if the cut filter failed. + - "all": void (KTDataHandle) -- Emitted after cut status is checked for all data. + - "pass": void (KTDataHandle) -- Emitted after cut status is checked if the cut filter passed. + - "fail": void (KTDataHandle) -- Emitted after cut status is checked if the cut filter failed. */ class KTCutFilter : public KTProcessor @@ -78,9 +78,9 @@ namespace Nymph bool fAllBits; public: - bool Filter(KTData& data); + bool Filter(const KTCoreData& data); - void FilterData(KTDataPtr); + void FilterData(KTDataHandle dataHandle); //*************** @@ -96,6 +96,8 @@ namespace Nymph // Slots //*************** + KTSlotWrapper* fFilterDataSW; + private: }; diff --git a/Cpp/Library_v1/Data/KTCutResult.hh b/Cpp/Library_v1/Data/KTCutResult.hh new file mode 100644 index 00000000..dbc04385 --- /dev/null +++ b/Cpp/Library_v1/Data/KTCutResult.hh @@ -0,0 +1,36 @@ +/* + * KTCutResult.hh + * + * Created on: Sept 19, 2014 + * Author: nsoblath + */ + +#ifndef KTCUTRESULT_HH_ +#define KTCUTRESULT_HH_ + +#include + +namespace Nymph +{ + struct KTCutResult + { + std::string fName; + bool fState; + bool fAssigned; + KTCutResult() : fName(), fState( false ), fAssigned( false ) {} + KTCutResult( const std::string& name, bool state ) : fName( name ), fState( state ), fAssigned( true ) {} + }; + + struct CheckCutResultName + { + std::string fName; + CheckCutResultName( const std::string& name ) : fName( name ) {} + bool operator()( const KTCutResult& result ) + { + return result.fName == fName; + } + }; + +} /* namespace Nymph */ + +#endif /* KTCUTRESULT_HH_ */ diff --git a/Cpp/Library_v1/Data/KTCutStatus.cc b/Cpp/Library_v1/Data/KTCutStatus.cc new file mode 100644 index 00000000..9396e76a --- /dev/null +++ b/Cpp/Library_v1/Data/KTCutStatus.cc @@ -0,0 +1,94 @@ +/* + * KTCut.cc + * + * Created on: Aug 24, 2012 + * Author: nsoblath + */ + +#include "KTCutStatus.hh" + +#include "KTExtensibleStructFactory.hh" +#include "KTLogger.hh" + +namespace Nymph +{ + KTLOGGER(cutlog, "KTCut"); + + KTCutStatus::KTCutStatus() : + fCutResults(), + fSummary() + { + } + + KTCutStatus::KTCutStatus(const KTCutStatus& orig) : + fCutResults(orig.fCutResults), + fSummary() + { + UpdateStatus(); + } + + KTCutStatus::~KTCutStatus() + {} + + KTCutStatus& KTCutStatus::operator=(const KTCutStatus& rhs) + { + fCutResults = rhs.fCutResults; + UpdateStatus(); + return *this; + } + + void KTCutStatus::AssignCutResult(unsigned maskPos, const std::string& name, bool state, bool doUpdateStatus) + { + KTDEBUG(cutlog, "Assigning cut result <" << name << "> to position <" << maskPos << "> with state <" << state << ">"); + if( maskPos >= fCutResults.size() ) + { + fCutResults.resize( maskPos + 1 ); + } + KTDEBUG(cutlog, "Cut result size is now <" << fCutResults.size() << ">"); + if( fCutResults[maskPos].fAssigned ) + { + throw KTException() << "Mask position <" << maskPos << "> has already been assigned"; + } + fCutResults[maskPos].fName = name; + fCutResults[maskPos].fState = state; + fCutResults[maskPos].fAssigned = true; + if( doUpdateStatus ) UpdateStatus(); + return; + } + + void KTCutStatus::UpdateStatus() + { + KTDEBUG(cutlog, "Updating cut summary"); + unsigned nCuts = fCutResults.size(); + fSummary.resize(nCuts, false); + + // loop through to set cuts + for (unsigned iCut = 0; iCut < nCuts; ++iCut) + { + fSummary[iCut] = fCutResults[iCut].fAssigned && fCutResults[iCut].fState; + } + KTDEBUG(cutlog, "Cut summary bitset: " << fSummary); + return; + } + + std::string KTCutStatus::CutResultsPresent() const + { + std::string cutsPresent; + for (auto cutIt = fCutResults.cbegin(); cutIt != fCutResults.cend(); ++cutIt) + { + if (! cutIt->fName.empty()) + { + cutsPresent = cutIt->fName + " " + cutsPresent; + } + } + return cutsPresent; + } + + std::ostream& operator<<(std::ostream& out, const KTCutStatus& status) + { + out << "Cut summary: " << status.fSummary << '\n'; + return out; + } + + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Data/KTCutStatus.hh b/Cpp/Library_v1/Data/KTCutStatus.hh new file mode 100644 index 00000000..77c41ee8 --- /dev/null +++ b/Cpp/Library_v1/Data/KTCutStatus.hh @@ -0,0 +1,316 @@ +/* + * KTCutStatus.hh + * + * Created on: Sept 19, 2014 + * Author: nsoblath + */ + +#ifndef KTCUTSTATUS_HH_ +#define KTCUTSTATUS_HH_ + + +#include "KTCutResult.hh" + +#include "KTException.hh" + +#include "cereal/access.hpp" +#include "cereal/types/string.hpp" +#include "cereal/types/vector.hpp" + +#include + +#include +#include +#include + +namespace Nymph +{ + /*! + @class KTCutStatus + @author N. S. Oblath + + @brief Provides easy access to cut result information. + + @details + The cut results can be imagined as an array of booleans, specifying whether the cut was passed: [true, false, false, true, . . . ]. + + Cuts are assigned both an array position (non-negative integer) and a name (string) before or when the results are set. + + KTCutStatus is typically used as a member variable of KTCoreData, the top-level data object. + + KTCutStatus owns the set of KTCutResults that have been added to a data object. + It also owns a summary of those cuts (implemented with boost::dynamic_bitset). + + You can check if the data has been cut with the IsCut functions. + - IsCut() returns true if any cut results are true; + - IsCut(const bitset_type& mask), IsCut(unsigned int mask), and IsCut(const std::string& mask) allow you to specify + a cut mask, and return true if any of the cut results specified by the mask are true. + + When specifying a cut mask, bits set to true specify cuts that should be used: + - bitset_type is boost::dynamic_bitset; + - unsigned integer masks use the bits of the integer; + - std::string masks are strings with each character either a 0 or 1. + + With KTCutStatus you can interact with individual cut results in the following ways: + - Get the number of cut results with size(), + - Get a reference to the cut results with CutResults(), + - If you need to manually update the cut status bitset, use UpdateStatus(), + - Add cut results to a data object with AssignCutResult(), + - Remove a cut result with RemoveCutResult(), + - Check to see if a particular cut result is present using HasCutResult(), + - Get the value of a cut result with GetCutState(), and + - Set the value of a cut result with SetCutState(), + + Cut results can typically be accessed by either name or mask position. + */ + + // external serialization function for KTCutResult + template< class Archive > + void serialize( Archive & archive, KTCutResult& cutResult ) + { + archive( CEREAL_NVP(cutResult.fName), CEREAL_NVP(cutResult.fState), CEREAL_NVP(cutResult.fAssigned) ); + } + + class KTCutStatus + { + public: + typedef boost::dynamic_bitset< > bitset_type; + + typedef std::vector< KTCutResult > CutResults_t; + + typedef CutResults_t::iterator CutResultsIt; + typedef CutResults_t::const_iterator CutResultsCIt; + + public: + KTCutStatus(); + KTCutStatus(const KTCutStatus& orig); + ~KTCutStatus(); + + KTCutStatus& operator=(const KTCutStatus& rhs); + + /// Returns the size of the cut results vector + size_t size() const; + + /// Returns a reference to the cut results vector + const CutResults_t& CutResults() const; + + /// Updates the summary bitset from the cut results vector + void UpdateStatus(); + + /// Adds a new cut result if it doesn't exist, and assigns the specified name and state at maskPos; throws KTException if maskPos is already assigned + void AssignCutResult(unsigned maskPos, const std::string& name, bool state=false, bool doUpdateStatus=true); + + /// Removes the cut by erasing the name and setting the state to false + void RemoveCutResult(const std::string& cutName, bool doUpdateStatus=true); + /// Removes the cut by erasing the name and setting the state to false + void RemoveCutResult(unsigned maskPos, bool doUpdateStatus=true); + + CutResultsIt FindCutResult(const std::string& cutName); + CutResultsCIt FindCutResultC(const std::string& cutName) const; + + CutResultsIt CutResultsEnd(); + CutResultsCIt CutResultsEndC() const; + + /// Returns whether or not the specified cut is present + bool HasCutResult(const std::string& cutName) const; + /// Returns whether or not the specified cut is present (whether or not it has been explicitly assigned) + bool HasCutResult(unsigned maskPos) const; + + /// Returns the state of the named cut; throws KTException if it doesn't exist + bool GetCutState(const std::string& cutName) const; + /// Returns the state of the specified cut; throws KTException if it doesn't exist + bool GetCutState(unsigned maskPos) const; + + /// Sets the state of the specified cut; throws KTException if it doesn't exist + void SetCutState(const std::string& cutName, bool state, bool doUpdateStatus=true); + /// Sets the state of the specified cut; throws KTException if it doesn't exist + void SetCutState(unsigned maskPos, bool state, bool doUpdateStatus=true); + + /// Returns a string with the names of the cuts that are present in bitset order + std::string CutResultsPresent() const; + + private: + friend std::ostream& operator<<(std::ostream& out, const KTCutStatus& status); + + CutResults_t fCutResults; + + bitset_type fSummary; + + public: + bool IsCut() const; + bool IsCut(const bitset_type& mask) const; + bool IsCut(unsigned long long mask) const; + bool IsCut(const std::string& mask) const; + + bitset_type ToBitset(unsigned long long mask) const; + bitset_type ToBitset(const std::string& mask) const; + + private: + friend class cereal::access; + + template< class Archive > + void save( Archive& ar ) const; + + template< class Archive > + void load( Archive& ar ); + + }; + + std::ostream& operator<<(std::ostream& out, const KTCutStatus& status); + + + inline const KTCutStatus::CutResults_t& KTCutStatus::CutResults() const + { + return fCutResults; + } + + inline KTCutStatus::CutResultsIt KTCutStatus::FindCutResult( const std::string& name ) + { + if( name.empty() ) return fCutResults.end(); + return std::find_if( fCutResults.begin(), fCutResults.end(), CheckCutResultName(name) ); + } + + inline KTCutStatus::CutResultsCIt KTCutStatus::FindCutResultC( const std::string& name ) const + { + if( name.empty() ) return fCutResults.cend(); + return std::find_if( fCutResults.cbegin(), fCutResults.cend(), CheckCutResultName(name) ); + } + + inline KTCutStatus::CutResultsIt KTCutStatus::CutResultsEnd() + { + return fCutResults.end(); + } + + inline KTCutStatus::CutResultsCIt KTCutStatus::CutResultsEndC() const + { + return fCutResults.cend(); + } + + inline bool KTCutStatus::HasCutResult( const std::string& name ) const + { + return FindCutResultC(name) != std::end(fCutResults); + } + + inline bool KTCutStatus::HasCutResult( unsigned maskPos ) const + { + return maskPos < fCutResults.size() && fCutResults[maskPos].fAssigned; + } + + inline bool KTCutStatus::GetCutState( const std::string& name ) const + { + CutResultsCIt cutIt = FindCutResultC(name); + if (cutIt != fCutResults.cend()) + { + return cutIt->fState; + } + throw KTException() << "Unable to find cut <" << name << ">"; + } + + inline bool KTCutStatus::GetCutState( unsigned maskPos ) const + { + if (maskPos < fCutResults.size()) + { + return fCutResults[maskPos].fState; + } + throw KTException() << "Mask position <" << maskPos << "> is out of range (only " << size() << " cuts are present)"; + } + + inline void KTCutStatus::SetCutState(const std::string& name, bool state, bool doUpdateStatus) + { + CutResultsIt cutIt = FindCutResult(name); + if (cutIt != fCutResults.end()) + { + cutIt->fState = state; + if (doUpdateStatus) UpdateStatus(); + return; + } + throw KTException() << "Unable to find cut <" << name << ">"; + } + + inline void KTCutStatus::SetCutState(unsigned maskPos, bool state, bool doUpdateStatus) + { + if (maskPos < fCutResults.size()) + { + fCutResults[maskPos].fState = state; + if (doUpdateStatus) UpdateStatus(); + return; + } + throw KTException() << "Mask position <" << maskPos << "> is out of range (only "<< size() << " cuts are present)"; + } + + inline void KTCutStatus::RemoveCutResult(const std::string& name, bool doUpdateStatus) + { + CutResultsIt cutIt = FindCutResult(name); + if (cutIt != fCutResults.end()) + { + cutIt->fName = ""; + cutIt->fState = false; + } + if (doUpdateStatus) UpdateStatus(); + return; + } + + inline void KTCutStatus::RemoveCutResult(unsigned maskPos, bool doUpdateStatus) + { + if (maskPos < fCutResults.size()) + { + fCutResults[maskPos].fName = ""; + fCutResults[maskPos].fState = false; + } + if (doUpdateStatus) UpdateStatus(); + return; + } + + inline size_t KTCutStatus::size() const + { + return fSummary.size(); + } + + inline bool KTCutStatus::IsCut() const + { + return fSummary.any(); + } + + inline bool KTCutStatus::IsCut(const bitset_type& mask) const + { + return (fSummary & mask).any(); + } + + inline bool KTCutStatus::IsCut(unsigned long long mask) const + { + return IsCut(ToBitset(mask)); + } + + inline bool KTCutStatus::IsCut(const std::string& mask) const + { + return IsCut(ToBitset(mask)); + } + + inline KTCutStatus::bitset_type KTCutStatus::ToBitset(unsigned long long mask) const + { + return bitset_type(fSummary.size(), mask); + } + + inline KTCutStatus::bitset_type KTCutStatus::ToBitset(const std::string& mask) const + { + return bitset_type(mask); + } + + template< class Archive > + void KTCutStatus::save( Archive& ar ) const + { + std::cout << "### save for KTCutStatus" << std::endl; + ar( CEREAL_NVP(fCutResults) ); + } + + template< class Archive > + void KTCutStatus::load( Archive& ar ) + { + std::cout << "### load for KTCutStatus" << std::endl; + ar( CEREAL_NVP(fCutResults) ); + UpdateStatus(); + } + +} /* namespace Nymph */ + +#endif /* KTCUTSTATUS_HH_ */ diff --git a/Cpp/Library_v1/Data/KTData.cc b/Cpp/Library_v1/Data/KTData.cc new file mode 100644 index 00000000..536cb8ef --- /dev/null +++ b/Cpp/Library_v1/Data/KTData.cc @@ -0,0 +1,37 @@ +/* + * KTData.cc + * + * Created on: Aug 24, 2012 + * Author: nsoblath + */ + +#include "KTData.hh" + +namespace Nymph +{ + KTData::KTData() + { + std::cout << "### KTData constructor" << std::endl; + } + + KTData::~KTData() + { + std::cout << "### KTData destructor" << std::endl; + } + + KTDataRider::KTDataRider() : + fName() + { + std::cout << "### KTDataRider for <" << fName << "> constructor" << std::endl; + } + + KTDataRider::KTDataRider( const KTDataRider& orig ) : + fName( orig.fName ) + {} + + KTDataRider::~KTDataRider() + { + std::cout << "### KTDataRider for <" << fName << "> destructor" << std::endl; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Data/KTData.hh b/Cpp/Library_v1/Data/KTData.hh new file mode 100644 index 00000000..90b3b6ad --- /dev/null +++ b/Cpp/Library_v1/Data/KTData.hh @@ -0,0 +1,96 @@ +/* + * KTData.hh + * + * Created on: Aug 24, 2012 + * Author: nsoblath + */ + +#ifndef KTDATA_HH_ +#define KTDATA_HH_ + +#include "KTExtensible.hh" + +#include "KTMemberVariable.hh" + +#include "cereal/types/polymorphic.hpp" // required to avoid compile error +#include "cereal/types/base_class.hpp" // required for base-class specification +#include "cereal/access.hpp" // required for private member access + +#include + +namespace Nymph +{ + + class KTData + { + public: + KTData(); + virtual ~KTData(); + + template< class Archive > + void serialize( Archive& ar ) + { + std::cout << "### serialize for KTData" << std::endl; + } + }; + + class KTDataRider + { + public: + KTDataRider(); + KTDataRider( const KTDataRider& orig ); + virtual ~KTDataRider(); + + MEMBERVARIABLE_REF( std::string, Name ); + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); + }; + + template< class Archive > + void KTDataRider::serialize( Archive& ar ) + { + std::cout << "### serialize for KTDataRider" << std::endl; + ar( fName ); + } + + template< class XDerivedType > + class KTExtensibleDataRider : public KTExtensible< XDerivedType, KTDataRider > + { + public: + KTExtensibleDataRider() = delete; + KTExtensibleDataRider( const std::string& name ) { KTDataRider::fName = name; } + KTExtensibleDataRider( const KTExtensibleDataRider< XDerivedType >& orig ) : KTExtensible< XDerivedType, KTDataRider >( *this ) {} + virtual ~KTExtensibleDataRider() {} + + template< class Archive > + void serialize( Archive& ar ) + { + std::cout << "### serialize for KTExtensibleDataRider< XDerivedType >" << std::endl; + ar( cereal::base_class< KTExtensible< XDerivedType, KTDataRider > >( this ) ); + } + }; + +#define DEFINE_EXT_DATA_2( ex_data_class_name, data_class_name, label ) \ + class ex_data_class_name : public data_class_name, public KTExtensibleDataRider< ex_data_class_name > \ + { \ + public: \ + ex_data_class_name() : data_class_name(), KTExtensibleDataRider< ex_data_class_name >( label ) {} \ + ex_data_class_name( const ex_data_class_name& orig ) : data_class_name( *this ), KTExtensibleDataRider< ex_data_class_name >( *this ) {} \ + virtual ~ex_data_class_name() {} \ + \ + template< class Archive > \ + void serialize( Archive& ar ) \ + { \ + std::cout << "### serialize for ex_data_class_name" << std::endl; \ + ar( cereal::base_class< data_class_name >( this ), cereal::base_class< KTExtensibleDataRider< ex_data_class_name > >( this ) ); \ + } \ + }; + +#define DEFINE_EXT_DATA( data_class_name, label ) DEFINE_EXT_DATA_2( PASTE(data_class_name, Ext), data_class_name, label ) + +} /* namespace Nymph */ +#endif /* KTDATA_HH_ */ diff --git a/Cpp/Library_v1/Data/KTRegisterNymphExtData.hh b/Cpp/Library_v1/Data/KTRegisterNymphExtData.hh new file mode 100644 index 00000000..cc2c479c --- /dev/null +++ b/Cpp/Library_v1/Data/KTRegisterNymphExtData.hh @@ -0,0 +1,29 @@ +/* + * KTRegisterNymphExtData.hh + * + * Created on: Feb 17, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTREGISTERNYMPHEXTDATA_HH_ +#define NYMPH_KTREGISTERNYMPHEXTDATA_HH_ + +/** + * @file KTRegisterNymphExtData.hh + * + * Register the extensible data objects that live in Nymph here. + * This file should be included with registration headers packages using Nymph's extensible data setup. + * + * This header should be included _after_ the relevant archives are registered (after #include'ing the headers, generally) + * + */ + + +#include + +#include "KTCoreData.hh" + +CEREAL_REGISTER_TYPE( Nymph::KTCoreDataExt ); + + +#endif /* NYMPH_KTREGISTERNYMPHEXTDATA_HH_ */ diff --git a/Cpp/Library_v1/Data/KTTester.cc b/Cpp/Library_v1/Data/KTTester.cc new file mode 100644 index 00000000..e0ce2951 --- /dev/null +++ b/Cpp/Library_v1/Data/KTTester.cc @@ -0,0 +1,24 @@ +/* + * KTTester.cc + * + * Created on: Aug 26, 2019 + * Author: obla999 + */ + +#include "KTTester.hh" + +namespace Nymph +{ + + KTTester::KTTester() + { + // TODO Auto-generated constructor stub + + } + + KTTester::~KTTester() + { + // TODO Auto-generated destructor stub + } + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Data/KTTester.hh b/Cpp/Library_v1/Data/KTTester.hh new file mode 100644 index 00000000..f0e3f5b9 --- /dev/null +++ b/Cpp/Library_v1/Data/KTTester.hh @@ -0,0 +1,41 @@ +/* + * KTTester.hh + * + * Created on: Aug 26, 2019 + * Author: obla999 + */ + +#ifndef LIBRARY_DATA_KTTESTER_HH_ +#define LIBRARY_DATA_KTTESTER_HH_ + +//#include "KTConfigurable.hh" + +#include "KTConnection.hh" +//#include "KTLogger.hh" +#include "KTSignalWrapper.hh" +#include "KTSlotWrapper.hh" + +//#include "factory.hh" + +//#include + +//#include +//#include +//#include +//#include +//#include +//#include + +namespace Nymph +{ + + class KTTester + { + public: + KTTester(); + virtual ~KTTester(); + }; + +} /* namespace Nymph */ + +#endif /* LIBRARY_DATA_KTTESTER_HH_ */ diff --git a/Cpp/Library_v1/IO/KTJSONWriter.cc b/Cpp/Library_v1/IO/KTJSONWriter.cc new file mode 100644 index 00000000..36add475 --- /dev/null +++ b/Cpp/Library_v1/IO/KTJSONWriter.cc @@ -0,0 +1,57 @@ +/* + * KTJSONWriter.cc + * + * Created on: Feb 1, 2018 + * Author: N.S. Oblath + * +*/ + +#include "KTJSONWriter.hh" + +#include "KTLogger.hh" + + +namespace Nymph +{ + KT_REGISTER_PROCESSOR( KTJSONWriter, "json-writer" ); + + KTJSONWriter::KTJSONWriter( const std::string& name ) : + KTWriterWithTypists< KTJSONWriter, KTJSONTypeWriter >( name ), + fFilename( "json_writer_default_file.json" ), + fStreamOutPtr( nullptr ), + fArchiveOutPtr( nullptr ), + fExtDataSlot( "ext-data", this, &KTJSONWriter::WriteExtData ), + fCoreDataSlot( "core", this, &KTJSONWriter::WriteData< KTCoreDataExt > ) + { + } + + KTJSONWriter::~KTJSONWriter() + { + // archive must be delete before the stream! + delete fArchiveOutPtr; + delete fStreamOutPtr; + } + + bool KTJSONWriter::Configure( const scarab::param_node& node ) + { + SetFilename( node.get_value( "filename", fFilename ) ); + + return true; + } + + void KTJSONWriter::WriteExtData( KTDataHandle handle ) + { + if( fStreamOutPtr == nullptr ) + { + fStreamOutPtr = new std::ofstream( fFilename ); + fArchiveOutPtr = new cereal::JSONOutputArchive( *fStreamOutPtr ); + } + + // Write to JSON archive + KTDEBUG( avlog_hh, "Writing extensible data to JSON archive" ); + (*fArchiveOutPtr)( handle ); + + return; + } + +} diff --git a/Cpp/Library_v1/IO/KTJSONWriter.hh b/Cpp/Library_v1/IO/KTJSONWriter.hh new file mode 100644 index 00000000..b6ba7a8d --- /dev/null +++ b/Cpp/Library_v1/IO/KTJSONWriter.hh @@ -0,0 +1,107 @@ +/* + * KTJSONWriter.hh + * + * Created on: Feb 1, 2018 + * Author: N.S. Oblath + * +*/ + +#ifndef NYMPH_KTJSONWRITER_HH_ +#define NYMPH_KTJSONWRITER_HH_ + +// included first so that the archive is registered before the data classes +#include "cereal/archives/json.hpp" +// then register the nymph extensible data classes +#include "KTRegisterNymphExtData.hh" + + +#include "KTWriter.hh" +#include "KTSlot.hh" + +#include "KTLogger.hh" + +#include + +KTLOGGER( avlog_hh, "KTJSONWriter" ); + +namespace Nymph +{ + class KTJSONWriter; + + typedef KTDerivedTypeWriter< KTJSONWriter > KTJSONTypeWriter; + + /*! + @class KTJSONWriter + @author N. S. Oblath + + @brief Writes data to a JSON file + + @details + Can write single data objects or full extensible data objects. + + The Cereal serialization library is used for this writer. + Data objects must have a serial or save function to be successfully serialized. + + The slots for this writer can be extended in other compilation units using the type-writer system. + As an example, see Executables/Validation/KTJSONTypeWriterValidation and KTRegisterNymphValidationExtData. + + Data objects must be registered after the Cereal JSON archive is included, and before any serialization takes place. + As an example when extending the writer, see KTRegisterNymphValidationExtData. + + Configuration name: "json-writer" + + Available configuration options: + - "filename": string -- JSON filename to write to + + Signals: N/A + + Slots: + - "ext-data": void (KTDataHandle) -- write a full extensible data object + - "core" void (KTDataHandle) -- write a KTCoreData object; requires KTCoreData + + */ + class KTJSONWriter : public KTWriterWithTypists< KTJSONWriter, KTJSONTypeWriter > + { + public: + KTJSONWriter( const std::string& name = "json-writer" ); + virtual ~KTJSONWriter(); + + bool Configure( const scarab::param_node& node ); + + MEMBERVARIABLE( std::string, Filename ); + + public: + template< class XDataType > + void WriteData( const XDataType& data ); + + void WriteExtData( KTDataHandle handle ); + + private: + std::ofstream* fStreamOutPtr; + cereal::JSONOutputArchive* fArchiveOutPtr; + + private: + KTSlot< KTDataHandle > fExtDataSlot; + + KTSlotData< void, KTCoreDataExt > fCoreDataSlot; + }; + + template< class XDataType > + void KTJSONWriter::WriteData( const XDataType& data ) + { + if( fStreamOutPtr == nullptr ) + { + fStreamOutPtr = new std::ofstream( fFilename ); + fArchiveOutPtr = new cereal::JSONOutputArchive( *fStreamOutPtr ); + } + + // Write to JSON archive + KTDEBUG( avlog_hh, "Writing data to JSON archive" ); + (*fArchiveOutPtr)( data ); + + return; + } + +} // namespace Nymph + +#endif // NYMPH_KTJSONWRITER_HH_ diff --git a/Library/IO/KTReader.cc b/Cpp/Library_v1/IO/KTReader.cc similarity index 85% rename from Library/IO/KTReader.cc rename to Cpp/Library_v1/IO/KTReader.cc index b3ac907f..0aa7b2dd 100644 --- a/Library/IO/KTReader.cc +++ b/Cpp/Library_v1/IO/KTReader.cc @@ -11,7 +11,7 @@ namespace Nymph { KTReader::KTReader(const std::string& name) : - KTPrimaryProcessor(name) + KTPrimaryProcessor({}, name) { } diff --git a/Library/IO/KTReader.hh b/Cpp/Library_v1/IO/KTReader.hh similarity index 100% rename from Library/IO/KTReader.hh rename to Cpp/Library_v1/IO/KTReader.hh diff --git a/Library/IO/KTWriter.cc b/Cpp/Library_v1/IO/KTWriter.cc similarity index 100% rename from Library/IO/KTWriter.cc rename to Cpp/Library_v1/IO/KTWriter.cc diff --git a/Library/IO/KTWriter.hh b/Cpp/Library_v1/IO/KTWriter.hh similarity index 81% rename from Library/IO/KTWriter.hh rename to Cpp/Library_v1/IO/KTWriter.hh index e89b6776..278e6800 100644 --- a/Library/IO/KTWriter.hh +++ b/Cpp/Library_v1/IO/KTWriter.hh @@ -33,7 +33,7 @@ namespace Nymph class KTDerivedTypeWriter : public KTTypeWriter { public: - KTDerivedTypeWriter(); + KTDerivedTypeWriter(XWriter* writer); virtual ~KTDerivedTypeWriter(); void SetWriter(XWriter* writer); @@ -44,9 +44,9 @@ namespace Nymph template< class XWriter > - KTDerivedTypeWriter< XWriter >::KTDerivedTypeWriter() : + KTDerivedTypeWriter< XWriter >::KTDerivedTypeWriter(XWriter* writer) : KTTypeWriter(), - fWriter(NULL) + fWriter(writer) { } @@ -96,13 +96,13 @@ namespace Nymph KTWriter(name), fTypeWriters() { - KTTIFactory< XTypist >* twFactory = KTTIFactory< XTypist >::get_instance(); - for (typename KTTIFactory< XTypist >::FactoryCIt factoryIt = twFactory->GetFactoryMapBegin(); + KTTIFactory< XTypist, XWriter* >* twFactory = KTTIFactory< XTypist, XWriter* >::get_instance(); + for (typename KTTIFactory< XTypist, XWriter* >::FactoryCIt factoryIt = twFactory->GetFactoryMapBegin(); factoryIt != twFactory->GetFactoryMapEnd(); factoryIt++) { - XTypist* newTypeWriter = twFactory->Create(factoryIt); - newTypeWriter->SetWriter(static_cast< XWriter* >(this)); + XTypist* newTypeWriter = twFactory->Create(factoryIt, static_cast< XWriter* >(this)); + //newTypeWriter->SetWriter(static_cast< XWriter* >(this)); newTypeWriter->RegisterSlots(); fTypeWriters.insert(typename TypeWriterMap::value_type(factoryIt->first, newTypeWriter)); } @@ -130,6 +130,8 @@ namespace Nymph return static_cast< XTypeWriter* >(it->second); } +#define KT_REGISTER_TYPE_WRITER(writer_class, type_writer_base_class, typist) \ + static ::Nymph::KTTIRegistrar< type_writer_base_class, type_writer_base_class##typist, writer_class* > s##type_writer_base_class##typist##Registrar; #define KT_REGISTER_WRITER(writer_class, writer_name) \ static ::scarab::registrar< ::Nymph::KTWriter, writer_class, const std::string& > s##writer_class##WriterRegistrar(writer_name); diff --git a/Cpp/Library_v1/NymphPybind.cc b/Cpp/Library_v1/NymphPybind.cc new file mode 100644 index 00000000..39ceb7c7 --- /dev/null +++ b/Cpp/Library_v1/NymphPybind.cc @@ -0,0 +1,35 @@ +/* + * NymphPybind.cc + * + * Created on: Jan 27, 2018 + * Author: N.S. Oblath + */ + +#include "KTParamPybind.hh" + +#include "KTProcessorToolboxPybind.hh" +#include "KTProcessorPybind.hh" + +#include + +//#include "Utility/KTConfigurablePybind.hh" + +PYBIND11_MODULE( py_nymph, mod ) +{ + //Nymph::ExportKTConfigurable( mod ); + //Nymph::ExportKTProcessor( mod ); + Nymph::ExportKTProcessorToolbox( mod ); + Nymph::ExportParamPybind( mod ); + + pybind11::class_< Nymph::KTProcessor, std::shared_ptr >( mod, "KTProcessor" ); + + pybind11::class_< Nymph::KTWrapProcessor, Nymph::KTPyWrapProcessor, Nymph::KTProcessor, std::shared_ptr > wrap_processor(mod, "WrapProcessor", pybind11::dynamic_attr()); + wrap_processor + .def(pybind11::init<>()) + .def("WrapFunction", &Nymph::KTWrapProcessor::WrapFunction) + .def("Configure", &Nymph::KTWrapProcessor::Configure); + +} + + + diff --git a/Library/Processor/KTConnection.hh b/Cpp/Library_v1/Processor/KTConnection.hh similarity index 100% rename from Library/Processor/KTConnection.hh rename to Cpp/Library_v1/Processor/KTConnection.hh diff --git a/Cpp/Library_v1/Processor/KTNymphSignals.hh b/Cpp/Library_v1/Processor/KTNymphSignals.hh new file mode 100644 index 00000000..aa52aba0 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTNymphSignals.hh @@ -0,0 +1,46 @@ +/* + * KTNymphSignals.hh + * + * Created on: Feb 25, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTNYMPHSIGNALS_HH_ +#define NYMPH_KTNYMPHSIGNALS_HH_ + +#include "KTSignal.hh" + +#include "KTCoreData.hh" + +namespace Nymph +{ + /// Convenience typedef for done signals + typedef KTSignal<> KTSignalDone; + + // Convenience typedef for backwards compatibility + template< typename XDataType > + using KTSignalOneArg = KTSignal< XDataType >; + + /*! + @class KTSignalData + @author N. S. Oblath + + @brief Creates a signal that takes a KTDataHandle object as its argument. + + @details + The purpose of the signal is for passing KTCoreData pointers between Processors. + The signal is emitted by calling operator(). + If a KTDataSlot is being used, and the Slot has been given a pointer to this signal, the Slot will emit the Signal. + + Usage: + In your Processor's header add a member variable of type KTSignalData. + + Initialize the signal with the processor's 'this' pointer and the name of the signal. + + That's it! + */ + typedef KTSignal< KTDataHandle > KTSignalData; + +} /* namespace Nymph */ + +#endif /* NYMPH_KTNYMPHSIGNALS_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTNymphSlot.hh b/Cpp/Library_v1/Processor/KTNymphSlot.hh new file mode 100644 index 00000000..a584b3c1 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTNymphSlot.hh @@ -0,0 +1,398 @@ +/* + * KTNymphSlot.hh + * + * Created on: Feb 25, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTNYMPHSLOT_HH_ +#define NYMPH_KTNYMPHSLOT_HH_ + +#include "KTSlot.hh" + +#include "KTException.hh" +#include "KTNymphSignals.hh" +#include "KTProcessor.hh" + +namespace Nymph +{ + // Typedefs for backwards compatibility + + typedef KTSlot<> KTSlotNoArg; + + template < typename Arg1 > + using KTSlotOneArg = KTSlot< Arg1 >; + + template< typename Arg1, typename Arg2 > + using KTSlotTwoArg = KTSlot< Arg1, Arg2 >; + + + template + struct foo {}; + + template < typename... Types1, template class T + , typename... Types2, template class V + , typename U > + void + bar(const T&, const V&, const U& u) + { + std::cout << sizeof...(Types1) << std::endl; + std::cout << sizeof...(Types2) << std::endl; + std::cout << u << std::endl; + } + + + /*! + @class KTSlotData + @author N. S. Oblath + + @brief Creates a slot that takes a KTDataHandle object as the argument; the function that gets called should take 0 or more DataType&'s as its argument. + + @details + Usage: + This slot type adds the slot function (signature void (KTDataHandle). + Your processor (or, optionally, a different object) must have a member function with the signature bool (DataType1&, . . .). + The slot function checks that the provided KTCoreData object contains data of type DataType, and then calls the member function. + + In your Processor's header add a member variable of type KTSlotOneArg< DataType >. + The variable may be private. + + Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. + Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. + + Also optionally, a signal to be emitted after the return of the member function can be specified as the last argument. + */ + template< class XReturnType, class... XDataArgs > + class KTSlotData : public KTSlot< KTDataHandle > + { + public: + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ), KTSignalData* signalPtr ); + + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ) ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ), KTSignalData* signalPtr ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ) ); + + virtual ~KTSlotData(); + + void operator()( KTDataHandle data ); + + protected: + template< typename... DataTypes > + bool DataPresent( KTDataHandle data ); + + //function_signature fFunc; + std::function< XReturnType (const XDataArgs&..., XReturnType&) > fFunc; + + KTSignalData* fSignalPtr; + }; + + // partial specialization for no new data type + template< class... XDataArgs > + class KTSlotData< void, XDataArgs... > : public KTSlot< KTDataHandle > + { + public: + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ), KTSignalData* signalPtr ); + + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ) ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ), KTSignalData* signalPtr ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ) ); + + virtual ~KTSlotData(); + + void operator()( KTDataHandle data ); + + protected: + template< typename... DataTypes > + bool DataPresent( KTDataHandle data ); + + //function_signature fFunc; + std::function< void (const XDataArgs&...) > fFunc; + + KTSignalData* fSignalPtr; + }; + + + /*! + @class KTSlotDone + @author N. S. Oblath + + @brief Creates a slot to receive indication that upstream processing is complete and will emit a similar signal. + + @details + Usage: + This slot type adds the slot function (signature void ()). + Your processor (or, optionally, a different object) must have a member function with the signature void (). + The slot calls the member function. + + In your Processor's header add a member variable of type KTDoneSlot. + The variable may be private. + + Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. + Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. + + Also optionally, a signal to be emitted after the return of the member function can be specified as the last argument. + */ + class KTSlotDone : public KTSlot<> + { + public: + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType > + KTSlotDone( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr=nullptr ); + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType > + KTSlotDone( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr=nullptr ); + virtual ~KTSlotDone(); + + void operator()(); + + protected: + std::function< void () > fFunc; + + KTSignalDone* fSignalPtr; + }; + + + //******************* + // Implementations + //******************* + + // KTSlotData + + template< class XReturnType, class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< XReturnType, XDataTypes... >::KTSlotData(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ), KTSignalData* signalPtr) : + KTSlot( name, owner, this, &KTSlotData::operator(), {signalPtr->Name()} ), + fFunc( [func, owner]( const XDataTypes&... args ){ return (owner->*func)(args...);} ), + fSignalPtr( signalPtr ) + { + } + + template< class XReturnType, class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< XReturnType, XDataTypes... >::KTSlotData(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& )) : + KTSlot( name, owner, this, &KTSlotData::operator() ), + fFunc( [func, owner]( const XDataTypes&... args ){ return (owner->*func)(args...);} ), + fSignalPtr( nullptr ) + { + } + + template< class XReturnType, class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< XReturnType, XDataTypes... >::KTSlotData(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& ), KTSignalData* signalPtr) : + KTSlot( name, proc, this, &KTSlotData::operator(), { signalPtr->Name()} ), + fFunc( [func, owner]( const XDataTypes&... args ){return (owner->*func) (args... );} ), + fSignalPtr( signalPtr ) + { + } + + template< class XReturnType, class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< XReturnType, XDataTypes... >::KTSlotData(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&..., XReturnType& )) : + KTSlot( name, proc, this, &KTSlotData::operator() ), + fFunc( [func, owner]( const XDataTypes&... args ){return (owner->*func) (args... );} ), + fSignalPtr( nullptr ) + { + } + + template< class XReturnType, class... XDataTypes > + KTSlotData< XReturnType, XDataTypes... >::~KTSlotData() + { + } + + template< class XReturnType, class... XDataTypes > + void KTSlotData< XReturnType, XDataTypes... >::operator()( KTDataHandle dataHandle ) + { + // Standard data slot pattern: + + std::shared_ptr< KTThreadReference > ref = fThreadRef; + + // Check to ensure that the required data type is present + if( ! DataPresent< XDataTypes... >( dataHandle ) ) + { + KTERROR( slotlog, "Failed to find all of the necessary data types in slot <" << fName << ">. Aborting." ); + THROW_THREADREF_EXCEPTION( ref, KTException() << "Failed to find all of the necessary data types in slot <" << fName << ">. Aborting." ); + return; + } + + // Call the function + try + { + fFunc( dataHandle->Of< XDataTypes >()... , dataHandle->Of< XReturnType >() ); + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct slotData_RunFunc >( "Something went wrong in slot <" + fName + ">. Aborting." ); + ref->SetReturnException( boost::current_exception() ); + } + + // Perform breakpoint here if necessary (either if initiated here or if stopping here due to a breakpoint elsewhere) + // Sets the dataHandle into the return + ref->Break( dataHandle, fDoBreakpoint ); + + // If there's a signal pointer, emit the signal + if( fSignalPtr != nullptr ) + { + (*fSignalPtr)( dataHandle ); + } + return; + } + + template< class XReturnType, class... XDataTypes > + template< typename... DataTypes > + bool KTSlotData< XReturnType, XDataTypes... >::DataPresent( KTDataHandle data ) + { + return DataPresentHelper< DataTypes... >::DataPresent( data ); + } + + + // KTSlotData with no return type + + template< class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< void, XDataTypes... >::KTSlotData(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ), KTSignalData* signalPtr) : + KTSlot( name, owner, this, &KTSlotData::operator(), {signalPtr->Name()} ), + fFunc( [func, owner]( const XDataTypes&... args ){ return (owner->*func)(args...);} ), + fSignalPtr( signalPtr ) + { + } + + template< class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< void, XDataTypes... >::KTSlotData(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... )) : + KTSlot( name, owner, this, &KTSlotData::operator() ), + fFunc( [func, owner]( const XDataTypes&... args ){ return (owner->*func)(args...);} ), + fSignalPtr( nullptr ) + { + } + + template< class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< void, XDataTypes... >::KTSlotData(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... ), KTSignalData* signalPtr) : + KTSlot( name, proc, this, &KTSlotData::operator(), { signalPtr->Name()} ), + fFunc( [func, owner]( const XDataTypes&... args ){return (owner->*func) (args... );} ), + fSignalPtr( signalPtr ) + { + } + + template< class... XDataTypes > + template< class XFuncOwnerType, class... XFuncDataTypes > + KTSlotData< void, XDataTypes... >::KTSlotData(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( const XFuncDataTypes&... )) : + KTSlot( name, proc, this, &KTSlotData::operator() ), + fFunc( [func, owner]( const XDataTypes&... args ){return (owner->*func) (args... );} ), + fSignalPtr( nullptr ) + { + } + + template< class... XDataTypes > + KTSlotData< void, XDataTypes... >::~KTSlotData() + { + } + + template< class... XDataTypes > + void KTSlotData< void, XDataTypes... >::operator()( KTDataHandle dataHandle ) + { + // Standard data slot pattern: + + std::shared_ptr< KTThreadReference > ref = fThreadRef; + + // Check to ensure that the required data type is present + if( ! DataPresent< XDataTypes... >( dataHandle ) ) + { + KTERROR( slotlog, "Failed to find all of the necessary data types in slot <" << fName << ">. Aborting." ); + THROW_THREADREF_EXCEPTION( ref, KTException() << "Failed to find all of the necessary data types in slot <" << fName << ">. Aborting." ); + return; + } + + // Call the function + try + { + fFunc( dataHandle->Of< XDataTypes >()... ); + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct slotData_RunFunc >( "Something went wrong in slot <" + fName + ">. Aborting." ); + ref->SetReturnException( boost::current_exception() ); + } + + // Perform breakpoint here if necessary (either if initiated here or if stopping here due to a breakpoint elsewhere) + // Sets the dataHandle into the return + ref->Break( dataHandle, fDoBreakpoint ); + + // If there's a signal pointer, emit the signal + if( fSignalPtr != nullptr ) + { + (*fSignalPtr)( dataHandle ); + } + return; + } + + template< class... XDataTypes > + template< typename... DataTypes > + bool KTSlotData< void, XDataTypes... >::DataPresent( KTDataHandle data ) + { + return DataPresentHelper< DataTypes... >::DataPresent( data ); + } + + + // KTSlotDone + + template< class XFuncOwnerType > + KTSlotDone::KTSlotDone(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr) : + KTSlot( name, owner, this, &KTSlotDone::operator(), {signalPtr->Name()} ), + fFunc( [func, owner](){ return (owner->*func)(); } ), + fSignalPtr( signalPtr ) + { + } + + template< class XFuncOwnerType > + KTSlotDone::KTSlotDone(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr) : + KTSlot( name, proc, this, &KTSlotDone::operator(), {signalPtr->Name()} ), + fFunc( [func, owner](){ return (owner->*func)(); } ), + fSignalPtr( signalPtr ) + { + } + + inline KTSlotDone::~KTSlotDone() + { + } + + inline void KTSlotDone::operator()() + { + // Call the function + fFunc(); + + // Perform breakpoint here if necessary (either if initiated here or if stopping here due to a breakpoint elsewhere) + // Sets the dataHandle into the return + fThreadRef->Break( KTDataHandle(), fDoBreakpoint ); + + // If there's a signal pointer, emit the signal + if( fSignalPtr != nullptr ) + { + (*fSignalPtr)(); + } + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_KTNYMPHSLOT_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTPrimaryProcessor.cc b/Cpp/Library_v1/Processor/KTPrimaryProcessor.cc new file mode 100644 index 00000000..babcccdc --- /dev/null +++ b/Cpp/Library_v1/Processor/KTPrimaryProcessor.cc @@ -0,0 +1,69 @@ +/* + * KTPrimaryProcessor.cc + * + * Created on: Oct 10, 2012 + * Author: nsoblath + */ + +#include "KTPrimaryProcessor.hh" + +#include "KTException.hh" +#include "KTLogger.hh" + +namespace Nymph +{ + KTLOGGER( proclog, "KTPrimaryProcessor" ); + + KTPrimaryProcessor::KTPrimaryProcessor( std::initializer_list< std::string > signals, const std::string& name ) : + KTProcessor( name ), + fSignalsEmitted( signals ), + fThreadRef(), + fDoBreakpoint(false) + { + + } + + KTPrimaryProcessor::~KTPrimaryProcessor() + { + } + + void KTPrimaryProcessor::operator ()( std::shared_ptr< KTThreadReference > ref, boost::condition_variable& startedCV, bool& startedFlag ) + { + fThreadRef = ref; + + // pass updated thread reference to downstream slots + for( auto sigIt = fSignalsEmitted.begin(); sigIt != fSignalsEmitted.end(); ++sigIt ) + { + // loop over all processor:slots called by this signal + auto sigConnRange = fSigConnMap.equal_range( *sigIt ); + for( SigConnMapCIt sigConnIt = sigConnRange.first; sigConnIt != sigConnRange.second; ++sigConnIt ) + { + // pass the update on to the connected-to processor + sigConnIt->second.first->PassThreadRefUpdate( sigConnIt->second.second, fThreadRef ); + } + } + + startedFlag = true; + startedCV.notify_all(); + + // go! + try + { + if( ! Run() ) + { + KTERROR( proclog, "An error occurred during processor running." ); + THROW_THREADREF_EXCEPTION( fThreadRef, KTException() << "An error occurred during processor running" ); + } + else + { + fThreadRef->SetReturnValue( KTDataHandle() ); + } + } + catch( boost::exception& e ) + { + fThreadRef->SetReturnException( boost::current_exception() ); + } + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Processor/KTPrimaryProcessor.hh b/Cpp/Library_v1/Processor/KTPrimaryProcessor.hh new file mode 100644 index 00000000..218eb0fc --- /dev/null +++ b/Cpp/Library_v1/Processor/KTPrimaryProcessor.hh @@ -0,0 +1,53 @@ +/* + * KTPrimaryProcessor.hh + * + * Created on: Oct 10, 2012 + * Author: nsoblath + */ + +#ifndef KTPRIMARYPROCESSOR_HH_ +#define KTPRIMARYPROCESSOR_HH_ + +#include "KTProcessor.hh" + +#include "KTCoreData.hh" +#include "KTThreadReference.hh" + +#include + +#include + +namespace Nymph +{ + + class KTPrimaryProcessor : public KTProcessor + { + public: + KTPrimaryProcessor( std::initializer_list< std::string > signals, const std::string& name = "default-primary-processor-name" ); + virtual ~KTPrimaryProcessor(); + + public: + /// Callable function used by std::thread + void operator()( std::shared_ptr< KTThreadReference > ref, boost::condition_variable& startedCV, bool& startedFlag ); + + /// Starts the main action of the processor + virtual bool Run() = 0; + + std::shared_ptr< KTThreadReference > GetThreadRef(); + + MEMBERVARIABLE( bool, DoBreakpoint ); + + protected: + std::vector< std::string > fSignalsEmitted; + + std::shared_ptr< KTThreadReference > fThreadRef; + + }; + + inline std::shared_ptr< KTThreadReference > KTPrimaryProcessor::GetThreadRef() + { + return fThreadRef; + } + +} /* namespace Nymph */ +#endif /* KTPRIMARYPROCESSOR_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTProcessor.cc b/Cpp/Library_v1/Processor/KTProcessor.cc new file mode 100644 index 00000000..c28cfa98 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTProcessor.cc @@ -0,0 +1,150 @@ +/* + * KTProcessor.cc + * + * Created on: Jan 5, 2012 + * Author: nsoblath + */ + +#include "KTProcessor.hh" + +#include "KTException.hh" +//#include "KTLogger.hh" + +#include + +using std::string; + +namespace Nymph +{ + //KTLOGGER(proclog, "KTProcessor"); + + KTProcessor::KTProcessor(const string& name) : + KTConfigurable(name), + fSignalMap(), + fSlotMap(), + fSlotToSigMap(), + fSigConnMap() + { + } + + KTProcessor::~KTProcessor() + { + for (SlotMapIt iter = fSlotMap.begin(); iter != fSlotMap.end(); iter++) + { + delete iter->second; + } + for (SigMapIt iter = fSignalMap.begin(); iter != fSignalMap.end(); iter++) + { + delete iter->second; + } + } + + void KTProcessor::PassThreadRefUpdate(const std::string& slotName, std::shared_ptr< KTThreadReference > threadRef) + { + std::function< void(std::shared_ptr< KTThreadReference >) > funcObj = [this, &slotName](std::shared_ptr< KTThreadReference > ref){ GetSlot(slotName)->ThreadRef() = ref; }; + PassToConnProcs(slotName, funcObj, threadRef); + return; + } + + void KTProcessor::ConnectASlot(const std::string& signalName, KTProcessor* processor, const std::string& slotName, int groupNum) + { + // get the signal and slot wrapper pointers + KTSignalBase* signal = GetSignal(signalName); + KTSlotBase* slot = processor->GetSlot(slotName); + + try + { + // make the connection + ConnectSignalToSlot(signal, slot, groupNum); + } + catch( KTSignalException& e ) + { + e << KTErrorMsgInfo< struct proc_Sig_0 >( "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the signal." ); + e << KTErrorMsgInfo< struct proc_Sig_1 >( "You may have the signal name wrong." ); + throw; + } + catch( KTSlotException& e ) + { + e << KTErrorMsgInfo< struct proc_Slot_0 >( "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem with the slot." ); + e << KTErrorMsgInfo< struct proc_Slot_1 >( "You may have the slot name wrong." ); + throw; + } + catch( KTConnectionException& e ) + { + e << KTErrorMsgInfo< struct proc_Conn_0 >( "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> due to a problem making the connection." ); + e << KTErrorMsgInfo< struct proc_Conn_1 >( "Check that the signatures of the signal and slot match exactly." ); + throw; + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct proc_Unkn >( "Unable to connect signal <" + signalName + "> to slot <" + slotName + "> for an unknown reason." ); + throw; + } + + // record the connection in the signal-connection map + fSigConnMap.insert(SigConnMapVal(signalName, std::make_pair(processor, slotName))); + + KTDEBUG(processorlog, "Connected signal <" << this->GetConfigName() << ":" << signalName << "> to slot <" << processor->GetConfigName() << ":" << slotName << ">"); + + return; + } + + void KTProcessor::ConnectSignalToSlot(KTSignalBase* signal, KTSlotBase* slot, int groupNum) + { + if (signal == nullptr) + { + BOOST_THROW_EXCEPTION( KTSignalException() << "Signal pointer was NULL" << eom ); + } + if (slot == nullptr) + { + BOOST_THROW_EXCEPTION( KTSlotException() << "Slot pointer was NULL" << eom ); + } + + signal->Connect(slot, groupNum); + + return; + } + + KTSignalBase* KTProcessor::GetSignal(const std::string& name) + { + SigMapIt iter = fSignalMap.find(name); + if (iter == fSignalMap.end()) + { + return NULL; + } + return iter->second; + } + + KTSlotBase* KTProcessor::GetSlot(const std::string& name) + { + SlotMapIt iter = fSlotMap.find(name); + if (iter == fSlotMap.end()) + { + return NULL; + } + return iter->second; + } + + bool KTProcessor::GetDoBreakpoint(const std::string& slotName) + { + KTSlotBase* slot = GetSlot(slotName); + if (slot != nullptr) + { + return slot->GetDoBreakpoint(); + } + BOOST_THROW_EXCEPTION( KTException() << "Slot <" << slotName << "> was not found" << eom ); + return false; + } + + void KTProcessor::SetDoBreakpoint(const std::string& slotName, bool flag) + { + KTSlotBase* slot = GetSlot(slotName); + if (slot != nullptr) + { + return slot->SetDoBreakpoint(flag); + } + BOOST_THROW_EXCEPTION( KTException() << "Slot <" << slotName << "> was not found" << eom ); + return; + } + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Processor/KTProcessor.hh b/Cpp/Library_v1/Processor/KTProcessor.hh new file mode 100644 index 00000000..2575131c --- /dev/null +++ b/Cpp/Library_v1/Processor/KTProcessor.hh @@ -0,0 +1,161 @@ +/** + @file KTProcessor.hh + @brief Contains KTProcessor + @details KTProcessor is the processor base class + @author: N. S. Oblath + @date: Jan 5, 2012 + */ + +#ifndef KTPROCESSOR_HH_ +#define KTPROCESSOR_HH_ + +#include "KTConfigurable.hh" + +#include "KTLogger.hh" +#include "KTNymphSlot.hh" + +#include "factory.hh" + +#include +#include +#include +#include +#include + +namespace Nymph +{ + KTLOGGER(processorlog, "KTProcessor.hh"); + + struct KTProcessorException : virtual public KTException + {}; + + struct KTSignalException : virtual public KTException {}; + struct KTSlotException : virtual public KTException {}; + + class KTThreadReference; + + class KTProcessor : public KTConfigurable + { + protected: + typedef std::map< std::string, KTSignalBase* > SignalMap; + typedef SignalMap::iterator SigMapIt; + typedef SignalMap::value_type SigMapVal; + + typedef std::map< std::string, KTSlotBase* > SlotMap; + typedef SlotMap::iterator SlotMapIt; + typedef SlotMap::value_type SlotMapVal; + + typedef std::multimap< std::string, std::string > SlotToSigMap; + typedef SlotToSigMap::iterator SlotToSigMapIt; + typedef SlotToSigMap::const_iterator SlotToSigMapCIt; + typedef SlotToSigMap::value_type SlotToSigMapVal; + + typedef std::multimap< std::string, std::pair< KTProcessor*, std::string > > SigConnMap; + typedef SigConnMap::iterator SigConnMapIt; + typedef SigConnMap::const_iterator SigConnMapCIt; + typedef SigConnMap::value_type SigConnMapVal; + + public: + KTProcessor(const std::string& name="default-proc-name"); + virtual ~KTProcessor(); + + template< class XDerivedProc > + static scarab::registrar< Nymph::KTProcessor, XDerivedProc, const std::string& >* RegisterProcessor( const std::string& name ); + + public: + /// For a slot that is called, update the slot's ThreadRef, and pass the update to any slots that get called by signals emitted by this slot + void PassThreadRefUpdate(const std::string& slotName, std::shared_ptr< KTThreadReference > threadRef); + + static void ConnectSignalToSlot(KTSignalBase* signal, KTSlotBase* slot, int groupNum=-1); + + void ConnectASlot(const std::string& signalName, KTProcessor* processor, const std::string& slotName, int groupNum=-1); + void ConnectASignal(KTProcessor* processor, const std::string& signalName, const std::string& slotName, int groupNum=-1); + + void RegisterSignal(std::string name, KTSignalBase* signal); + + void RegisterSlot(std::string name, KTSlotBase* slot, std::initializer_list< std::string > signals = {}); + + KTSignalBase* GetSignal(const std::string& name); + + KTSlotBase* GetSlot(const std::string& name); + + bool GetDoBreakpoint(const std::string& slotName); + void SetDoBreakpoint(const std::string& slotName, bool flag); + + protected: + template< typename XReturn, typename... XArgs > + void PassToConnProcs(const std::string& slotName, std::function< XReturn(XArgs...) > function, XArgs... args); + + SignalMap fSignalMap; + + SlotMap fSlotMap; + + // maps which slots call which signals in this processor + SlotToSigMap fSlotToSigMap; + + // maps which signals get connected to which slots in other processors + SigConnMap fSigConnMap; + }; + + + void KTProcessor::RegisterSignal(std::string name, KTSignalBase* signal) + { + KTDEBUG(processorlog, "Registering signal <" << name << "> in processor <" << fConfigName << ">"); + fSignalMap.insert(SigMapVal(name, signal)); + return; + } + + void KTProcessor::RegisterSlot(std::string name, KTSlotBase* slot, std::initializer_list< std::string > signals) + { + KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); + + fSlotMap.insert(SlotMapVal(name, slot)); + + for (auto sigIt = signals.begin(); sigIt != signals.end(); ++sigIt) + { + fSlotToSigMap.insert(SlotToSigMapVal(name, *sigIt)); + KTDEBUG(processorlog, "Slot-to-signal connection <" << name << "> --> <" << *sigIt << ">"); + } + return; + } + + inline void KTProcessor::ConnectASignal(KTProcessor* processor, const std::string& signalName, const std::string& slotName, int groupNum) + { + processor->ConnectASlot(signalName, this, slotName, groupNum); + return; + } + + template< typename XReturn, typename... XArgs > + void KTProcessor::PassToConnProcs(const std::string& slotName, std::function< XReturn(XArgs...) > function, XArgs... args) + { + // update this slot + function(args...); + + // get the list of slot-to-signal connections for this slot + auto stsRange = fSlotToSigMap.equal_range(slotName); + + // loop over signals called in the performance of slot slotName + for (SlotToSigMapCIt stsIt = stsRange.first; stsIt != stsRange.second; ++stsIt) + { + // loop over all processor:slots called by this signal + auto sigConnRange = fSigConnMap.equal_range(stsIt->second); + for (SigConnMapCIt sigConnIt = sigConnRange.first; sigConnIt != sigConnRange.second; ++sigConnIt) + { + // pass the update on to the connected-to processor + sigConnIt->second.first->PassToConnProcs(sigConnIt->second.second, function, args...); + } + } + return; + } + + template< class XDerivedProc > + scarab::registrar< KTProcessor, XDerivedProc, const std::string& >* KTProcessor::RegisterProcessor( const std::string& name ) + { + return new scarab::registrar< KTProcessor, XDerivedProc, const std::string& >( name ); + } + +#define KT_REGISTER_PROCESSOR(proc_class, proc_name) \ + static ::scarab::registrar< ::Nymph::KTProcessor, proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); + +} /* namespace Nymph */ +#endif /* KTPROCESSOR_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTProcessorPybind.cc b/Cpp/Library_v1/Processor/KTProcessorPybind.cc new file mode 100644 index 00000000..04cc1515 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTProcessorPybind.cc @@ -0,0 +1,48 @@ +/* + * KTProcessorPybind.cc + * + * Created on: Feb 06, 2018 + * Author: cclaessens + */ + +#include "KTProcessor.hh" +#include "KTProcessorPybind.hh" + + +#include "KTLogger.hh" + +namespace Nymph +{ + + KTLOGGER(procpylog, "KTProcessorPybind"); + KT_REGISTER_PROCESSOR(KTWrapProcessor, "base-wrap-proc"); + + KTWrapProcessor::KTWrapProcessor( const std::string& name ) : + KTProcessor( name ), + fSlot("wrap-slot", this, &KTWrapProcessor::SlotFunc) + + { + } + + KTWrapProcessor::~KTWrapProcessor() + { + } + + bool KTWrapProcessor::Configure(const scarab::param_node&) + { + return true; + } + + void KTWrapProcessor::SlotFunc(int input) + { + KTINFO(procpylog, "Calling wrap function "); + this->WrapFunction(input); + return; + } + + void KTWrapProcessor::WrapFunction(int input) + { + KTINFO(procpylog, "Hey, I'm base class"); + return; + } +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Processor/KTProcessorPybind.hh b/Cpp/Library_v1/Processor/KTProcessorPybind.hh new file mode 100644 index 00000000..41bed4ca --- /dev/null +++ b/Cpp/Library_v1/Processor/KTProcessorPybind.hh @@ -0,0 +1,89 @@ +/* + * KTProcessorPybind.hh + * + * Created on: Jan 24, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTPROCESSORPYBIND_HH_ +#define NYMPH_KTPROCESSORPYBIND_HH_ + +#include "KTPyProcessor.hh" +#include "KTProcessor.hh" +#include "KTSlot.hh" + +#include "pybind11/pybind11.h" + +namespace Nymph +{ + +/* void ExportKTProcessor( pybind11::module& mod ) + { + pybind11::class_< KTProcessor, KTConfigurable >( mod, "KTProcessor" ) + .def( pybind11::init< const std::string & >() ) + + .def( "ConnectASlot", &Nymph::KTProcessor::ConnectASlot ) + .def( "ConnectASignal", &Nymph::KTProcessor::ConnectASignal ) + + .def( "GetDoBreakpoint", &Nymph::KTProcessor::GetDoBreakpoint ) + .def( "SetDoBreakpoint", &Nymph::KTProcessor::SetDoBreakpoint ) + ; + + pybind11::class_< KTPyProcessor >( mod, "KTPyProcessor" ) + .def( pybind11::init< const std::string & >() ) + ; + + + + }*/ + + // processor with wrapfunction + class KTWrapProcessor : public KTProcessor + { + public: + KTWrapProcessor( const std::string& name = "base-wrap-proc" ); + virtual ~KTWrapProcessor(); + + bool Configure(const scarab::param_node& node); + + void SlotFunc(int); + virtual void WrapFunction(int input); + + private: + KTSlot< int > fSlot; + + }; + + // helper class + class KTPyWrapProcessor: public KTWrapProcessor + { + public: + // Inherit the constructors + using KTWrapProcessor::KTWrapProcessor; + + // Trampoline (need one for each virtual function) + void WrapFunction(int input) override { + // I dont know what these two lines do, + // but at least the first one is necessary or nymph hangs when calling this as python method + pybind11::gil_scoped_release release; + pybind11::gil_scoped_acquire acquire; + PYBIND11_OVERLOAD_PURE( + void, // Return type + KTWrapProcessor, // Parent class + WrapFunction, // Name of function in C++ (must match Python name) + input // Argument(s) + ); + } + bool Configure(const scarab::param_node& node) override { + PYBIND11_OVERLOAD( + bool, // Return type + KTWrapProcessor, // Parent class + Configure, // Name of function in C++ (must match Python name) + node // Argument(s) + ); + } + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_KTPROCESSORPYBIND_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTPyProcessor.hh b/Cpp/Library_v1/Processor/KTPyProcessor.hh new file mode 100644 index 00000000..52f27b1b --- /dev/null +++ b/Cpp/Library_v1/Processor/KTPyProcessor.hh @@ -0,0 +1,32 @@ +/* + * KTPyProcessor.hh + * + * Created on: Jan 29, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTPYPROCESSOR_HH_ +#define NYMPH_KTPYPROCESSOR_HH_ + +#include "KTProcessor.hh" + +namespace Nymph +{ + + class KTPyProcessor : public KTProcessor + { + public: + using KTProcessor::KTProcessor; // inherit constructors + + // for now, override KTConfigurable::Configure(const scarab::param_node&) with a non-functional function + bool Configure(const scarab::param_node& ) + { + return true; + } + }; + + + +} /* namespace Nymph */ + +#endif /* NYMPH_KTPYPROCESSOR_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTSignal.hh b/Cpp/Library_v1/Processor/KTSignal.hh new file mode 100644 index 00000000..c3a8f8b5 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTSignal.hh @@ -0,0 +1,250 @@ +/* + * KTSignal.hh + * + * Created on: Jan 15, 2013 + * Author: nsoblath + */ + +#ifndef KTSIGNAL_HH_ +#define KTSIGNAL_HH_ + +#include "KTSignalSlotBase.hh" + +#include "KTConnection.hh" +#include "KTException.hh" +#include "KTLogger.hh" + +#include + +namespace Nymph +{ + KTLOGGER( signallog, "KTSignal" ); + + struct KTConnectionException : public KTException + {}; + + /// A m_signal object may call multiple slots with the + /// same signature. You can connect functions to the m_signal + /// which will be called when the emit() method on the + /// m_signal object is invoked. Any argument passed to emit() + /// will be passed to the given functions. + /// The name "m_signal" was chosen to avoid conflicting with std::signal. + template< typename... XArgs > + class KTSignal : public KTSignalBase + { + public: + using signature = void( XArgs... ); + using boost_signal = boost::signals2::signal< signature >; + + public: + template< typename XOwner > + KTSignal( const std::string& name, XOwner* owner ); + KTSignal( const KTSignal& ) = delete; + KTSignal( KTSignal&& ) = delete; + virtual ~KTSignal(); + + virtual void Connect( KTSlotBase* slot, int group ); + + // disconnects a previously connected function + void Disconnect( KTSlotBase* slot ) const; + + // disconnects all previously connected functions + void DisconnectAll() const; + + // calls all connected functions + void Emit( XArgs... args ); + void operator()( XArgs... args ); + + private: + typedef std::map< KTSlotBase*, KTConnection > slot_map; // to get around the problem of having a comma inside a macro function argument + MEMBERVARIABLE_REF_MUTABLE_CONST( slot_map, Slots ); + + boost_signal fInternalSignal; + }; + + + /*! + @class KTSignal + @author N. S. Oblath + + @brief Creates a signal that takes 0 or more arguments. + + @details + The signal is emitted by calling operator(). + If a KTSlot is being used, and the Slot has been given a pointer to this signal, the Slot will emit the Signal. + + Usage: + In your Processor's header add a member variable of type KTSignal< ArgumentTypes >. + + Initialize the signal with the processor's 'this' pointer and the name of the signal. + + To use the signal, call it as: fSignalObject(arg); + *//* + template< class... XSignalArguments > + class KTSignal + { + public: + typedef void (signature)( XSignalArguments... ); + typedef boost::signals2::signal< signature > boost_signal; + typedef typename boost::signals2::signal< signature >::slot_type slot_type; + + public: + KTSignal(); + KTSignal( const std::string& name, KTProcessor* proc ); + virtual ~KTSignal(); + + protected: + KTSignal( const KTSignal& ); + + public: + void operator()( XSignalArguments... args ); + + boost_signal* Signal(); + + const std::string& GetName() const; + + protected: + boost_signal fSignal; + + std::string fName; + }; +*/ + + + //******************* + // Implementations + //******************* + + template< typename... XArgs > + template< typename XOwner > + KTSignal< XArgs... >::KTSignal< XOwner >( const std::string& name, XOwner* owner ) : + fSlots(), + fInternalSignal() + { + owner->RegisterSignal( name, this ); + } + + template< typename... XArgs > + KTSignal< XArgs... >::~KTSignal() + { + DisconnectAll(); + } + + template< typename... XArgs > + void KTSignal< XArgs... >::Connect( KTSlotBase* slot, int group ) + { + if( fSlots.count( slot ) != 0 ) + { + KTWARN( signallog, "Signal <" << fName << "> is already connected to slot <" << slot->Name() << ">" ); + return; + } + + // ensure that the slot is of the correct type + KTSlot< XArgs... >* derivedSlot = dynamic_cast< KTSlot< XArgs... >* >( slot ); + if( slot == nullptr ) + { + BOOST_THROW_EXCEPTION( KTConnectionException() << "Trying to connect signal <" << fName << "> to slot <" << slot->Name() << ">, but cannot make the connection:\n" << + "\tUnable to cast from KTSlotBase to this signal's derived type." << eom ); + } + + KTConnection connection; + if( group >= 0 ) + { + connection = fInternalSignal.connect( group, derivedSlot->Function() ); + } + else + { + connection = fInternalSignal.connect( derivedSlot->Function() ); + } + + fSlots.insert( std::make_pair< KTSlotBase*, KTConnection >( slot, connection ) ); + slot->AddConnection( this ); + + return; + } + + // disconnects a previously connected function + template< typename... XArgs > + void KTSignal< XArgs... >::Disconnect( KTSlotBase* slot ) const + { + auto connectionIt = fSlots.find( slot ); + if( connectionIt != fSlots.end() ) + { + connectionIt->second.disconnect(); + connectionIt->first->RemoveConnection( this ); + } + return; + } + + // disconnects all previously connected functions + template< typename... XArgs > + void KTSignal< XArgs... >::DisconnectAll() const + { + for( auto connection : fSlots ) + { + connection.second.disconnect(); + connection.first->RemoveConnection( this ); + } + return; + } + + // calls all connected functions + template< typename... XArgs > + void KTSignal< XArgs... >::Emit( XArgs... args ) + { + (*this)( args ); + return; + } + + template< typename... XArgs > + void KTSignal< XArgs... >::operator()( XArgs... args ) + { + fSignal( args ); + } + +/* + template< class... XSignalArguments > + KTSignal< XSignalArguments... >::KTSignal( const std::string& name, KTProcessor* proc ) : + fSignal(), + fName( name ) + { + proc->RegisterSignal(name, &fSignal); + } + + template< class... XSignalArguments > + KTSignal< XSignalArguments... >::KTSignal() : + fSignal(), + fName("none") + {} + + template< class... XSignalArguments > + KTSignal< XSignalArguments... >::KTSignal( const KTSignal& signal ) : + fSignal(), + fName( signal.fName ) + {} + + template< class... XSignalArguments > + KTSignal< XSignalArguments... >::~KTSignal() + { + } + + template< class... XSignalArguments > + inline void KTSignal< XSignalArguments... >::operator()( XSignalArguments... args ) + { + fSignal( args... ); + } + + template< class... XSignalArguments > + inline typename KTSignal< XSignalArguments... >::boost_signal* KTSignal< XSignalArguments... >::Signal() + { + return &fSignal; + } + + template< class... XSignalArguments > + inline const std::string& KTSignal< XSignalArguments... >::GetName() const + { + return fName; + } +*/ +} /* namespace Nymph */ +#endif /* KTSIGNAL_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTSignalSlotBase.hh b/Cpp/Library_v1/Processor/KTSignalSlotBase.hh new file mode 100644 index 00000000..35a7db17 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTSignalSlotBase.hh @@ -0,0 +1,52 @@ +/* + * KTSignalSlotBase.hh + * + * Created on: Feb 25, 2019 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTSIGNALSLOTBASE_HH_ +#define NYMPH_KTSIGNALSLOTBASE_HH_ + +#include "KTMemberVariable.hh" + +#include + +namespace Nymph +{ + class KTSignalBase; + + class KTSlotBase + { + public: + KTSlotBase( const std::string& name ); + template< typename XOwner > + KTSlotBase( const std::string& name, XOwner* owner ); + virtual ~KTSlotBase(); + + virtual void AddConnection( KTSignalBase* ) const = 0; + + virtual void RemoveConnection( KTSignalBase* ) const = 0; + + MEMBERVARIABLE_REF( std::string, Name ); + }; + + + class KTSignalBase + { + public: + KTSignalBase( const std::string& name ); + template< typename x_owner > + KTSignalBase( const std::string& name, x_owner* owner ); + virtual ~KTSignalBase(); + + virtual void Connect( KTSlotBase* slot, int group ) = 0; + + virtual void Disconnect( KTSlotBase* slot ) const = 0; + + MEMBERVARIABLE_REF( std::string, Name ); + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_KTSIGNALSLOTBASE_HH_ */ diff --git a/Library/Processor/KTSignalWrapper.cc b/Cpp/Library_v1/Processor/KTSignalWrapper.cc similarity index 76% rename from Library/Processor/KTSignalWrapper.cc rename to Cpp/Library_v1/Processor/KTSignalWrapper.cc index 42c629c5..795fc2c8 100644 --- a/Library/Processor/KTSignalWrapper.cc +++ b/Cpp/Library_v1/Processor/KTSignalWrapper.cc @@ -9,9 +9,6 @@ namespace Nymph { - SignalException::SignalException (std::string const& why) - : std::logic_error(why) - {} KTSignalWrapper::KTSignalWrapper() : fSignalWrapper(NULL) diff --git a/Library/Processor/KTSignalWrapper.hh b/Cpp/Library_v1/Processor/KTSignalWrapper.hh similarity index 93% rename from Library/Processor/KTSignalWrapper.hh rename to Cpp/Library_v1/Processor/KTSignalWrapper.hh index 4b0382ee..aa4f658e 100644 --- a/Library/Processor/KTSignalWrapper.hh +++ b/Cpp/Library_v1/Processor/KTSignalWrapper.hh @@ -23,12 +23,6 @@ namespace Nymph typedef typename boost::signals2::signal< Signature >::slot_type slot_type; }; - class SignalException : public std::logic_error - { - public: - SignalException(std::string const& why); - }; - class KTSignalWrapper : public boost::noncopyable { public: @@ -55,7 +49,7 @@ namespace Nymph return fSignal; } private: - XSignature* fSignal; //not owned by this KTSignalWrapper + XSignature* fSignal; //not owned by this KTSpecifiedInternalSignalWrapper }; public: diff --git a/Cpp/Library_v1/Processor/KTSlot.hh b/Cpp/Library_v1/Processor/KTSlot.hh new file mode 100644 index 00000000..0729eb0e --- /dev/null +++ b/Cpp/Library_v1/Processor/KTSlot.hh @@ -0,0 +1,236 @@ +/* + * KTSlot.hh + * + * Created on: Jan 13, 2013 + * Author: nsoblath + */ + +#ifndef KTSLOT_HH_ +#define KTSLOT_HH_ + +#include "KTSignalSlotBase.hh" + +//#include "KTException.hh" +#include "KTLogger.hh" +#include "KTThreadReference.hh" + +#include +#include + +namespace Nymph +{ + KTLOGGER(slotlog, "KTSlot"); + + + // Type XOwner is the class that owns the KTSlot object + template< typename... XArgs > + class KTSlot : public KTSlotBase + { + public: + using signature = void( XArgs... ); + using signal_list = std::initializer_list< std::string >; + + public: + template< typename XOwner > + KTSlot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ), signal_list signals = {} ); + template< typename XOwner > + KTSlot( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) const, signal_list signals = {} ); + template< typename XOwner > + KTSlot( const std::string& name, XOwner* owner, const boost::function< signature >& func, signal_list signals = {} ); + template< typename XOwner, typename XFuncClass > + KTSlot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ), signal_list signals = {} ); + template< typename XOwner, typename XFuncClass > + KTSlot( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) const, signal_list signals = {} ); + KTSlot( const KTSlot& ) = delete; + KTSlot( KTSlot&& ) = delete; + virtual ~KTSlot(); + + void DisconnectAll() const; + + void AddConnection( KTSignalBase* ) const; + + void RemoveConnection( KTSignalBase* ) const; + + MEMBERVARIABLE_REF( boost::function< signature >, Function ); + + typedef std::set< KTSignalBase* > signal_connections; // to get around the problem of having a comma inside a macro function argument + MEMBERVARIABLE_REF_MUTABLE( signal_connections, Connections ); + + // TODO: move fDoBreakpoint to KTSlot + // TODO: KTThreadReference should be a templated object, KTThreadReference< XArgs... > + // TODO: KTThreadReference< XArgs... > inherits from KTThreadReferenceBase + MEMBERVARIABLE( bool, DoBreakpoint ); + MEMBERVARIABLE_SHARED_PTR_CONST( KTThreadReference, ThreadRef ); + + }; + + + /*! + @class KTSlot + @author N. S. Oblath + + @brief Creates a slot that calls a member function of the func_owner_type object, taking 0 or more arguments. + + @details + Usage: + To use this slot type the function to be called by the slot must exist in an object of type FuncOwnerType. + The function should have the signature void (Args). + + In your Processor's header add a member variable of type KTSlot< ProcessorType, ReturnType, Args >. + The variable may be private. + + Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. + Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. + *//* + template< typename... Args > + class KTSlot + { + public: + /// Constructor for the case where the processor has the function that will be called by the slot + template< class XFuncOwnerType > + KTSlot( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( Args... ), std::initializer_list< std::string > signals = {} ); + + /// Constructor for the case where the processor and the object with the function that will be called are different + template< class XFuncOwnerType > + KTSlot( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( Args... ), std::initializer_list< std::string > signals = {} ); + + virtual ~KTSlot(); + + const std::string& GetName() const; + + KTSlotWrapper* GetSlotWrapper(); + + protected: + std::string fName; + KTSlotWrapper* fSlotWrapper; + }; +*/ + + + //******************* + // Implementations + //******************* + + // KTSlot + + template< typename... XArgs > + template< typename XOwner > + KTSlot< XArgs... >::KTSlot< XOwner >( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ), signal_list signals ) : + fFunction( [func, owner]( XArgs... args ){ return (owner->*func)(args...);} ), + fConnections(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + owner->RegisterSlot( name, this, signals ); + } + + template< typename... XArgs > + template< typename XOwner > + KTSlot< XArgs... >::KTSlot< XOwner >( const std::string& name, XOwner* owner, void (XOwner::*func)( XArgs... ) const, signal_list signals ) : + fFunction( [func, owner]( XArgs... args ){ return (owner->*func)(args...);} ), + fConnections(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + owner->RegisterSlot( name, this, signals ); + } + + template< typename... XArgs > + template< typename XOwner > + KTSlot< XArgs... >::KTSlot< XOwner >( const std::string& name, XOwner* owner, const boost::function< signature >& func, signal_list signals ) : + fFunction( func ), + fConnections(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + owner->RegisterSlot( name, this, signals ); + } + + template< typename... XArgs > + template< typename XOwner, typename XFuncClass > + KTSlot< XArgs... >::KTSlot< XOwner, XFuncClass >( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ), signal_list signals ) : + fFunction( [func, inst]( XArgs... args ){ return (inst->*func)(args...);} ), + fConnections(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + owner->RegisterSlot( name, this, signals ); + } + + template< typename... XArgs > + template< typename XOwner, typename XFuncClass > + KTSlot< XArgs... >::KTSlot< XOwner, XFuncClass >( const std::string& name, XOwner* owner, XFuncClass *inst, void (XFuncClass::*func)( XArgs... ) const, signal_list signals ) : + fFunction( [func, inst]( XArgs... args ){ return (inst->*func)(args...);} ), + fConnections(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + owner->RegisterSlot( name, this, signals ); + } + + template< typename... XArgs > + KTSlot< XArgs... >::~KTSlot() + { + DisconnectAll(); + } + + template< typename... XArgs > + void KTSlot< XArgs... >::DisconnectAll() const + { + for( auto connection : fConnections ) + { + connection->Disconnect( this ); + } + return; + } + + template< typename... XArgs > + void KTSlot< XArgs... >::AddConnection( KTSignalBase* signal ) const + { + fConnections.insert( signal ); + return; + } + + template< typename... XArgs > + void KTSlot< XArgs... >::RemoveConnection( KTSignalBase* signal ) const + { + fConnections.erase( signal ); + return; + } + +/* + template< typename... Args > + template< class XFuncOwnerType > + KTSlot< Args... >::KTSlot( const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( Args... ), std::initializer_list< std::string > signals ): + fName( name ) + { + fSlotWrapper = owner->RegisterSlot( name, owner, func, signals ); + } + + template< typename... Args > + template< class XFuncOwnerType > + KTSlot< Args... >::KTSlot( const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)( Args... ), std::initializer_list< std::string > signals ) : + fName( name ) + { + fSlotWrapper = proc->RegisterSlot( name, owner, func, signals ); + } + + template< typename... Args > + KTSlot< Args... >::~KTSlot() + { + } + + template< typename... Args > + inline const std::string& KTSlot< Args... >::GetName() const + { + return fName; + } + + template< typename... Args > + inline KTSlotWrapper* KTSlot< Args... >::GetSlotWrapper() + { + return fSlotWrapper; + } +*/ +} /* namespace Nymph */ +#endif /* KTSLOT_HH_ */ diff --git a/Library/Processor/KTSlotWrapper.cc b/Cpp/Library_v1/Processor/KTSlotWrapper.cc similarity index 74% rename from Library/Processor/KTSlotWrapper.cc rename to Cpp/Library_v1/Processor/KTSlotWrapper.cc index 2eb5f4f9..66e12f16 100644 --- a/Library/Processor/KTSlotWrapper.cc +++ b/Cpp/Library_v1/Processor/KTSlotWrapper.cc @@ -9,13 +9,12 @@ namespace Nymph { - SlotException::SlotException (std::string const& why) - : std::logic_error(why) - {} KTSlotWrapper::KTSlotWrapper() : fSlotWrapper(NULL), - fConnection() + fConnection(), + fThreadRef(), + fDoBreakpoint(false) { } diff --git a/Library/Processor/KTSlotWrapper.hh b/Cpp/Library_v1/Processor/KTSlotWrapper.hh similarity index 65% rename from Library/Processor/KTSlotWrapper.hh rename to Cpp/Library_v1/Processor/KTSlotWrapper.hh index 3139143c..70d3f3f3 100644 --- a/Library/Processor/KTSlotWrapper.hh +++ b/Cpp/Library_v1/Processor/KTSlotWrapper.hh @@ -9,18 +9,16 @@ #define KTSLOTWRAPPER_HH_ #include "KTConnection.hh" +#include "KTException.hh" #include "KTSignalWrapper.hh" +#include "KTThreadReference.hh" -#include #include namespace Nymph { - class SlotException : public std::logic_error - { - public: - SlotException(std::string const& why); - }; + struct KTConnectionException : public KTException + {}; class KTSlotWrapper : public boost::noncopyable { @@ -38,14 +36,13 @@ namespace Nymph class KTSpecifiedInternalSlotWrapper : public KTInternalSlotWrapper, public boost::noncopyable { public: - KTSpecifiedInternalSlotWrapper(XSignature* funcPtr, XTypeContainer* typeCont=NULL) : + KTSpecifiedInternalSlotWrapper(XSignature funcPtr, XTypeContainer* typeCont=NULL) : fSlot(funcPtr) { (void)typeCont; // to suppress warnings } virtual ~KTSpecifiedInternalSlotWrapper() { - delete fSlot; } virtual KTConnection Connect(KTSignalWrapper* signalWrap, int groupNum=-1) @@ -57,22 +54,22 @@ namespace Nymph SignalWrapper* derivedSignalWrapper = dynamic_cast< SignalWrapper* >(internalSignalWrap); if (derivedSignalWrapper == NULL) { - throw SignalException("In KTSpecifiedInternalSlotWrapper::Connect:\nUnable to cast from KTInternalSignalWrapper* to derived type."); + BOOST_THROW_EXCEPTION( KTConnectionException() << "Cannot make connection: unable to cast from KTInternalSignalWrapper* to this slot's derived type." << eom ); } if (groupNum >= 0) { - return derivedSignalWrapper->GetSignal()->connect(groupNum, *fSlot); + return derivedSignalWrapper->GetSignal()->connect(groupNum, fSlot); } - return derivedSignalWrapper->GetSignal()->connect(*fSlot); + return derivedSignalWrapper->GetSignal()->connect(fSlot); } private: - XSignature* fSlot; // is owned by this KTSlot + XSignature fSlot; }; public: template< typename XSignature, typename XTypeContainer > - KTSlotWrapper(XSignature* signalPtr, XTypeContainer* typeCont); + KTSlotWrapper(XSignature signalPtr, XTypeContainer* typeCont); ~KTSlotWrapper(); private: @@ -82,19 +79,33 @@ namespace Nymph public: void SetConnection(KTConnection conn); - void SetConnection(KTSignalWrapper* signalWrap, int groupNum=-1); + void SetConnection(KTSignalWrapper* signalWrap, int groupNum=-1); // can throw KTConnectionException void Disconnect(); private: KTConnection fConnection; + public: + std::shared_ptr< KTThreadReference > GetThreadRef() const; + void SetThreadRef(std::shared_ptr< KTThreadReference > ref); + + bool GetDoBreakpoint() const; + void SetDoBreakpoint(bool flag); + + private: + std::shared_ptr< KTThreadReference > fThreadRef; + bool fDoBreakpoint; + }; template< typename XSignature, typename XTypeContainer > - KTSlotWrapper::KTSlotWrapper(XSignature* signalPtr, XTypeContainer* typeCont) : + KTSlotWrapper::KTSlotWrapper(XSignature signalPtr, XTypeContainer* typeCont) : fSlotWrapper(new KTSpecifiedInternalSlotWrapper< XSignature, XTypeContainer >(signalPtr, typeCont)), - fConnection() - {} + fConnection(), + fThreadRef( std::make_shared< KTThreadReference >() ), + fDoBreakpoint(false) + { + } inline void KTSlotWrapper::SetConnection(KTConnection conn) { @@ -114,5 +125,28 @@ namespace Nymph return; } + inline std::shared_ptr< KTThreadReference > KTSlotWrapper::GetThreadRef() const + { + return fThreadRef; + } + + inline void KTSlotWrapper::SetThreadRef(std::shared_ptr< KTThreadReference > ref) + { + fThreadRef = ref; + return; + } + + inline bool KTSlotWrapper::GetDoBreakpoint() const + { + return fDoBreakpoint; + } + + inline void KTSlotWrapper::SetDoBreakpoint(bool flag) + { + fDoBreakpoint = flag; + return; + } + + } /* namespace Nymph */ #endif /* KTSLOTWRAPPER_HH_ */ diff --git a/Cpp/Library_v1/Processor/KTThreadReference.cc b/Cpp/Library_v1/Processor/KTThreadReference.cc new file mode 100644 index 00000000..e42a16c5 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTThreadReference.cc @@ -0,0 +1,86 @@ +/* + * KTThreadReference.cc + * + * Created on: May 2, 2017 + * Author: obla999 + */ + +#include "KTThreadReference.hh" + +#include "KTException.hh" +#include "KTLogger.hh" + +KTLOGGER( trlog, "KTThreadReference" ); + +namespace Nymph +{ + + KTThreadReference::KTThreadReference() : + fName(), + fBreakFlag( false ), + fCanceled( false ), + fDataPtrRet(), + fDataPtrRetFuture( fDataPtrRet.get_future() ), + fInitiateBreakFunc( [](){return;} ), + fWaitForContinueFunc( []( boost_unique_lock& ){return;} ), + fMutex() + { + if( ! fDataPtrRetFuture.valid() ) + { + KTERROR( trlog, "Invalid data-pointer-return future created" ); + BOOST_THROW_EXCEPTION( KTException() << "Invalid data-pointer-return future created" << eom ); + } + } + + KTThreadReference::KTThreadReference( KTThreadReference&& orig ) : + fName( std::move( orig.fName ) ), + fBreakFlag( orig.fBreakFlag ), + fCanceled( orig.fCanceled ), + fDataPtrRet( std::move( orig.fDataPtrRet ) ), + fDataPtrRetFuture( std::move( orig.fDataPtrRetFuture ) ), + fInitiateBreakFunc( std::move( orig.fInitiateBreakFunc ) ), + fWaitForContinueFunc( std::move( orig.fWaitForContinueFunc ) ), + fMutex() + { + orig.fBreakFlag = false; + orig.fCanceled = false; + } + + KTThreadReference& KTThreadReference::operator=( KTThreadReference&& orig ) + { + fName = std::move( orig.fName ); + fBreakFlag = orig.fBreakFlag; + fCanceled = orig.fCanceled; + fDataPtrRet = std::move( orig.fDataPtrRet ); + fDataPtrRetFuture = std::move( orig.fDataPtrRetFuture ); + fInitiateBreakFunc = std::move( orig.fInitiateBreakFunc ); + fWaitForContinueFunc = std::move( orig.fWaitForContinueFunc ); + + orig.fBreakFlag = false; + orig.fCanceled = false; + + return *this; + } + + void KTThreadReference::Break( const KTDataHandle& dataHandle, bool doBreakpoint ) + { + if( doBreakpoint ) + { + KTDEBUG( trlog, "Initiating break (" << fName << ")" ); + fInitiateBreakFunc(); + } + boost_unique_lock lock( fMutex ); + if( fBreakFlag || doBreakpoint ) + { + KTDEBUG( trlog, "Reacting to break (" << fName << ")" ); + // set the return for this thread + KTWARN( trlog, "Setting value of data-ptr-ret promise (" << fName << ")" ); + fDataPtrRet.set_value( dataHandle ); + // wait for continue signal + fWaitForContinueFunc( lock ); + } + return; + } + + +} /* namespace Nymph */ diff --git a/Cpp/Library_v1/Processor/KTThreadReference.hh b/Cpp/Library_v1/Processor/KTThreadReference.hh new file mode 100644 index 00000000..b792c3f0 --- /dev/null +++ b/Cpp/Library_v1/Processor/KTThreadReference.hh @@ -0,0 +1,168 @@ +/* + * KTThreadReference.hh + * + * Created on: May 2, 2017 + * Author: obla999 + */ + +#ifndef KTTHREADREFERENCE_HH_ +#define KTTHREADREFERENCE_HH_ + +#include "KTCoreData.hh" + +#include + +#include + +namespace Nymph +{ + typedef boost::promise< KTDataHandle > KTDataPtrReturn; + + class KTThreadReferenceBase + { + protected: + typedef boost::unique_lock< boost::mutex > boost_unique_lock; + + public: + KTThreadReferenceBase(); + KTThreadReferenceBase( const KTThreadReferenceBase& ) = delete; + KTThreadReferenceBase( KTThreadReferenceBase&& orig ); + + KTThreadReferenceBase& operator=( const KTThreadReferenceBase& ) = delete; + KTThreadReferenceBase& operator=( KTThreadReferenceBase&& ); + + public: + //************************** + // for use within the thread + //************************** + + //void SetReturnException( boost::exception_ptr excPtr ); + + public: + //****************************** + // for use outside of the thread + //****************************** + + void SetInitiateBreakFunc( const std::function< void() >& initBreakFunc ); + void SetWaitForContinueFunc( const std::function< void( boost_unique_lock& ) >& waitForContFunc ); + + public: + MEMBERVARIABLE_REF( std::string, Name ); + MEMBERVARIABLE( bool, BreakFlag ); + MEMBERVARIABLE( bool, Canceled ); + MEMBERVARIABLE_REF( boost::mutex, Mutex ); + + private: + std::function< void() > fInitiateBreakFunc; + std::function< void( boost_unique_lock& ) > fWaitForContinueFunc; + }; + + template< typename... XArgs > + class KTThreadReference : public KTThreadReferenceBase + { + private: + typedef KTThreadReferenceBase::boost_unique_lock boost_unique_lock; + + public: + KTThreadReference(); + KTThreadReference( const KTThreadReference& ) = delete; + KTThreadReference( KTThreadReference&& orig ); + + KTThreadReference& operator=( const KTThreadReference& ) = delete; + KTThreadReference& operator=( KTThreadReference&& ); + + public: + //************************** + // for use within the thread + //************************** + + void Break( bool doBreakpoint, const XArgs&... dataHandle ); + + void SetReturnValue( XArgs... dataHandle ); + + public: + //****************************** + // for use outside of the thread + //****************************** + + void SetReturnException( boost::exception_ptr excPtr ); + + KTDataHandle GetReturnValue(); + boost::unique_future< KTDataHandle >& GetDataPtrRetFuture(); + const boost::unique_future< KTDataHandle >& GetDataPtrRetFuture() const; + + void RefreshDataPtrRet(); + + private: + KTDataPtrReturn fDataPtrRet; + boost::unique_future< KTDataHandle > fDataPtrRetFuture; + }; + + template< typename... XArgs > + void KTThreadReference< XArgs... >::SetReturnException( boost::exception_ptr excPtr ) + { + fDataPtrRet.set_exception( excPtr ); + return; + } + + template< typename... XArgs > + void KTThreadReference< XArgs... >::SetReturnValue( XArgs... dataHandle ) + { + fDataPtrRet.set_value( dataHandle... ); + return; + } + + template< typename... XArgs > + KTDataHandle KTThreadReference< XArgs... >::GetReturnValue() + { + return fDataPtrRetFuture.get(); + } + + template< typename... XArgs > + boost::unique_future< KTDataHandle >& KTThreadReference< XArgs... >::GetDataPtrRetFuture() + { + return fDataPtrRetFuture; + } + + template< typename... XArgs > + const boost::unique_future< KTDataHandle >& KTThreadReference< XArgs... >::GetDataPtrRetFuture() const + { + return fDataPtrRetFuture; + } + + template< typename... XArgs > + void KTThreadReference< XArgs... >::RefreshDataPtrRet() + { + fDataPtrRet = KTDataPtrReturn(); + fDataPtrRetFuture = fDataPtrRet.get_future(); + return; + } + + inline void KTThreadReferenceBase::SetInitiateBreakFunc( const std::function< void() >& initBreakFunc ) + { + fInitiateBreakFunc = initBreakFunc; + return; + } + + inline void KTThreadReferenceBase::SetWaitForContinueFunc( const std::function< void( boost_unique_lock& ) >& waitForContFunc ) + { + fWaitForContinueFunc = waitForContFunc; + return; + } + + +#ifndef THROW_THREADREF_EXCEPTION +#define THROW_THREADREF_EXCEPTION( ref, exc ) \ + try \ + { \ + BOOST_THROW_EXCEPTION( exc ); \ + } \ + catch( ::boost::exception& e ) \ + { \ + ref->SetReturnException( ::boost::current_exception() ); \ + } +#endif // THROW_THREADREF_EXCEPTION + +} /* namespace Nymph */ + +#endif /* KTTHREADREFERENCE_HH_ */ diff --git a/Library/Utility/KTCacheDirectory.cc b/Cpp/Library_v1/Utility/KTCacheDirectory.cc similarity index 100% rename from Library/Utility/KTCacheDirectory.cc rename to Cpp/Library_v1/Utility/KTCacheDirectory.cc diff --git a/Library/Utility/KTCacheDirectory.hh b/Cpp/Library_v1/Utility/KTCacheDirectory.hh similarity index 100% rename from Library/Utility/KTCacheDirectory.hh rename to Cpp/Library_v1/Utility/KTCacheDirectory.hh diff --git a/Library/Utility/KTConcurrentQueue.hh b/Cpp/Library_v1/Utility/KTConcurrentQueue.hh similarity index 100% rename from Library/Utility/KTConcurrentQueue.hh rename to Cpp/Library_v1/Utility/KTConcurrentQueue.hh diff --git a/Library/Utility/KTConfigurable.cc b/Cpp/Library_v1/Utility/KTConfigurable.cc similarity index 83% rename from Library/Utility/KTConfigurable.cc rename to Cpp/Library_v1/Utility/KTConfigurable.cc index 4b494134..7fae5212 100644 --- a/Library/Utility/KTConfigurable.cc +++ b/Cpp/Library_v1/Utility/KTConfigurable.cc @@ -38,7 +38,11 @@ namespace Nymph { scarab::param_translator translator; scarab::param_node optNode; +<<<<<<< HEAD:Library_v1/Utility/KTConfigurable.cc + optNode.add( "encoding", "json" ); +======= optNode.add( "encoding", new scarab::param_value( "json" ) ); +>>>>>>> develop:Library/Utility/KTConfigurable.cc return Configure( translator.read_string( config, optNode )->as_node() );; } @@ -61,8 +65,12 @@ namespace Nymph { if (fIsConfigured) return true; +<<<<<<< HEAD:Library_v1/Utility/KTConfigurable.cc + if (! this->Configure(KTConfigurator::get_instance()->Config())) +======= const scarab::param_node& node = KTConfigurator::get_instance()->Config(); if (! this->Configure(node)) +>>>>>>> develop:Library/Utility/KTConfigurable.cc { KTERROR(conflog, "An error occurred while configuring <" << fConfigName << ">"); return false; diff --git a/Library/Utility/KTConfigurable.hh b/Cpp/Library_v1/Utility/KTConfigurable.hh similarity index 100% rename from Library/Utility/KTConfigurable.hh rename to Cpp/Library_v1/Utility/KTConfigurable.hh diff --git a/Cpp/Library_v1/Utility/KTConfigurablePybind.hh b/Cpp/Library_v1/Utility/KTConfigurablePybind.hh new file mode 100644 index 00000000..585a33f9 --- /dev/null +++ b/Cpp/Library_v1/Utility/KTConfigurablePybind.hh @@ -0,0 +1,29 @@ +/* + * KTConfigurablePybind.cc + * + * Created on: Jan 27, 2018 + * Author: N. Oblath + */ + +#include "KTConfigurable.hh" + +#include "pybind11/pybind11.h" + +namespace Nymph +{ + + void ExportKTConfigurable( pybind11::module& mod ) + { +/* pybind11::class_< KTConfigurable >( mod, "KTConfigurable" ) + .def( pybind11::init< const std::string & >() ) + + .def( "Configure", (bool (KTConfigurable::*)(const std::string&)) &KTConfigurable::Configure ) + //.def( "Configure", (void (KTConfigurable::*)(const scarab::param_node&)) &KTConfigurable::Configure ) + + .def( "GetConfigName", &KTConfigurable::GetConfigName ) + .def( "SetConfigName", &KTConfigurable::SetConfigName ) + ; +*/ + } + +} /* namespace Nymph */ diff --git a/Library/Utility/KTDirectory.cc b/Cpp/Library_v1/Utility/KTDirectory.cc similarity index 100% rename from Library/Utility/KTDirectory.cc rename to Cpp/Library_v1/Utility/KTDirectory.cc diff --git a/Library/Utility/KTDirectory.hh b/Cpp/Library_v1/Utility/KTDirectory.hh similarity index 100% rename from Library/Utility/KTDirectory.hh rename to Cpp/Library_v1/Utility/KTDirectory.hh diff --git a/Library/Utility/KTEventLoop.cc b/Cpp/Library_v1/Utility/KTEventLoop.cc similarity index 100% rename from Library/Utility/KTEventLoop.cc rename to Cpp/Library_v1/Utility/KTEventLoop.cc diff --git a/Library/Utility/KTEventLoop.hh b/Cpp/Library_v1/Utility/KTEventLoop.hh similarity index 100% rename from Library/Utility/KTEventLoop.hh rename to Cpp/Library_v1/Utility/KTEventLoop.hh diff --git a/Library/Utility/KTException.cc b/Cpp/Library_v1/Utility/KTException.cc similarity index 61% rename from Library/Utility/KTException.cc rename to Cpp/Library_v1/Utility/KTException.cc index 55a7a5f0..96e8df5c 100644 --- a/Library/Utility/KTException.cc +++ b/Cpp/Library_v1/Utility/KTException.cc @@ -11,13 +11,15 @@ namespace Nymph { KTException::KTException() : + boost::exception(), std::exception(), - fException( "" ) + fMsgBuffer() { } KTException::KTException( const KTException& an_exception ) : - std::exception(), - fException( an_exception.fException ) + boost::exception( an_exception ), + std::exception( an_exception ), + fMsgBuffer( an_exception.fMsgBuffer ) { } @@ -25,9 +27,4 @@ namespace Nymph { } - const char* KTException::what() const throw () - { - return fException.c_str(); - } - } diff --git a/Cpp/Library_v1/Utility/KTException.hh b/Cpp/Library_v1/Utility/KTException.hh new file mode 100644 index 00000000..10ae9660 --- /dev/null +++ b/Cpp/Library_v1/Utility/KTException.hh @@ -0,0 +1,127 @@ +/* + * KTException.hh + * + * Created on: Feb 25, 2014 + * Author: nsoblath + */ + + +#ifndef KTEXCEPTION_HH_ +#define KTEXCEPTION_HH_ + +#include "macros.hh" + +#include + +#include +#include +#include + +namespace Nymph +{ + struct KTMessageEnd {}; + static const KTMessageEnd eom = KTMessageEnd(); + + //typedef boost::error_info KTErrorMsgInfo; + + template< class XLabel > + using KTErrorMsgInfo = boost::error_info< XLabel, std::string>; + + /*! + @class KTException + @author N.S. Oblath + + @brief Exception base class for Nymph and Nymph-based projects + + @details + + How to throw: + + BOOST_THROW_EXCEPTION( KTException() << "[error message here]" << eom ); + + Why do it like this? + - BOOST_THROW_EXCEPTION will record the location that the exception was thrown. This is incredibly useful in diagnosing problems. + - eom ensure that your error message is handled properly; until that's processed the message is stored in a buffer, and the eom takes that buffer and adds it to the boost::exception base class's storage. + - KTException can be replaced with whatever derived exception class you're using. + + How to catch, add information, and re-throw: + + try + { + // code that throws a KTException + } + catch( boost::exception& e ) + { + e << KTErrorMsgInfo< struct UniqueLocationTag >( "Here's some information about the context in which the exception was thrown" ); + throw; + } + + + How to do the final catch + + try + { + // code that throws a KTException + } + catch( boost::exception& e ) + { + KTERROR( my_log, "An exception was caught: " << diagnostic_information( e ) ): + } + */ + + class KTException : virtual public boost::exception, virtual public std::exception + { + public: + KTException(); + KTException( const KTException& ); + ~KTException() throw (); + + // adds to the message buffer + template< class XStreamable > + KTException& operator<<( XStreamable a_fragment ); + // adds to the message buffer + KTException& operator<<( const std::string& a_fragment ); + // adds to the message buffer + KTException& operator<<( const char* a_fragment ); + + // adds the current contents of the message buffer as a KTErrorMsgInfo + KTException& operator<<( const KTMessageEnd& eom ); + + private: + mutable std::string fMsgBuffer; + }; + + template< class XStreamable > + KTException& KTException::operator<<( XStreamable a_fragment ) + { + std::stringstream stream; + stream << a_fragment; + stream >> fMsgBuffer; + return *this; + } + + inline KTException& KTException::operator<<( const std::string& a_fragment ) + { + fMsgBuffer += a_fragment; + return *this; + } + + inline KTException& KTException::operator<<( const char* a_fragment ) + { + fMsgBuffer += std::string( a_fragment ); + return *this; + } + + inline KTException& KTException::operator<<( const KTMessageEnd& eom ) + { + if( ! fMsgBuffer.empty() ) + { + boost::operator<<( *this, KTErrorMsgInfo< struct ExcpMsg >{ fMsgBuffer } ); + fMsgBuffer.clear(); + } + return *this; + } + +} + +#endif /* KTEXCEPTION_HH_ */ diff --git a/Cpp/Library_v1/Utility/KTExtensible.hh b/Cpp/Library_v1/Utility/KTExtensible.hh new file mode 100644 index 00000000..73fe3315 --- /dev/null +++ b/Cpp/Library_v1/Utility/KTExtensible.hh @@ -0,0 +1,393 @@ +/* + * KTExtensible.hh + * + * Created on: Feb 11, 2018 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_KTEXTENSIBLE_HH_ +#define NYMPH_KTEXTENSIBLE_HH_ + +#include "KTException.hh" + +#include + +#include + +namespace Nymph +{ + + template< class XBaseType > + class KTExtensibleCore : public XBaseType, public std::enable_shared_from_this< KTExtensibleCore< XBaseType > > + { + public: + typedef KTExtensibleCore< XBaseType > ExtCoreType; + typedef std::shared_ptr< ExtCoreType > BasePtrType; + + private: + typedef std::enable_shared_from_this< KTExtensibleCore< XBaseType > > ESFTBaseType; + + public: + KTExtensibleCore(); + KTExtensibleCore( const ExtCoreType& orig ) = delete; // deleted because copy of KTExtensibleCore is done using operator=() + virtual ~KTExtensibleCore(); + + /// Copies extended object + ExtCoreType& operator=( const ExtCoreType& orig ); + + /// Copies the current object and only extended fields already present + virtual ExtCoreType& Pull( const ExtCoreType& orig ) = 0; // implemented in KTExtensible because it requires knowing the extended field type + + /// Clones the extended object + virtual BasePtrType Clone() const = 0; // implemented in KTExtensible because it requires knowing the extended field type + + /// Returns the size of the extended object, including this field and any extended below it + unsigned size() const; + + /// Returns the object of the requested type or creates a new one if it's not present + template< class XRequestedType > + XRequestedType& Of(); + + /// Returns the object of the requested type or throws KTException if it's not present + template< class XRequestedType > + const XRequestedType& Of() const; + + /// Returns a pointer to the object of the requested type or creates a new one if it's not present + template< class XRequestedType > + std::shared_ptr< XRequestedType > Share(); + + /// Returns a pointer to the object of the requested type or throws KTException if it's not present + template< class XRequestedType > + const std::shared_ptr< XRequestedType > Share() const; + + /// Checks whether the requested type is present + template< class XRequestedType > + bool Has() const; + + /// Extracts the data object of the requested type if it's present; returns an empty pointer if not + /// Note that if you detatch the first field without holding a pointer to any other extended fields, those fields will be destroyed. + template< class XRequestedType > + std::shared_ptr< XRequestedType > Detatch(); + + /// Returns a pointer to the next extended field + BasePtrType Next(); + + protected: + template< class XRequestedType > + std::shared_ptr< XRequestedType > _Detatch( BasePtrType prev = BasePtrType() ); + + BasePtrType fNext; + + bool fDisableExtendedCopy; // internal variable used to determine whether operator= copies extended fields + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); + }; + + template< class XInstanceType, class XBaseType > + class KTExtensible : public KTExtensibleCore< XBaseType > + { + public: + typedef KTExtensible< XInstanceType, XBaseType > ExtType; + typedef KTExtensibleCore< XBaseType > ExtCoreType; + typedef std::shared_ptr< KTExtensibleCore< XBaseType > > BasePtrType; + + public: + KTExtensible(); + KTExtensible( const ExtType& orig ); + virtual ~KTExtensible(); + + /// Copies the extended object + ExtType& operator=( const ExtType& orig ); + + /// Copies the current object and only extended fields already present + virtual ExtCoreType& Pull( const ExtCoreType& orig ); + + /// Clones the extended object + virtual BasePtrType Clone() const; + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); + }; + + + + //************** + // KTExtensibleCore implementation + //************** + + template< class XBaseType > + KTExtensibleCore< XBaseType >::KTExtensibleCore() : + XBaseType(), + fNext(), + fDisableExtendedCopy( false ) + { + } + + template< class XBaseType > + KTExtensibleCore< XBaseType >::~KTExtensibleCore() + { + } + + template< class XBaseType > + KTExtensibleCore< XBaseType >& KTExtensibleCore< XBaseType >::operator=( const KTExtensibleCore< XBaseType >& orig ) + { + // must not call virtual functions on this because it's used in the copy constructor + + if( this == &orig ) return *this; + + fDisableExtendedCopy = false; + + // remove any extended fields + fNext.reset(); + + // copy extended fields + // only do this if orig.fDisableExtendedCopy is false + if( ! orig.fDisableExtendedCopy && orig.fNext ) + { + // duplicate extended fields with Clone() + fNext = orig.fNext->Clone(); // this's fNext points to the copy of orig's fNext + } + + // duplicate base class + this->XBaseType::operator=( *this ); + + return *this; + } + + template< class XBaseType > + unsigned KTExtensibleCore< XBaseType >::size() const + { + return fNext.operator bool() ? 1 + fNext->size() : 1; + } + + template< class XBaseType > + template< class XRequestedType > + XRequestedType& KTExtensibleCore< XBaseType >::Of() + { + XRequestedType* requested = dynamic_cast< XRequestedType* >( this ); + if( requested != nullptr ) + { + return *requested; + } + + if( ! fNext ) + { + std::shared_ptr< XRequestedType > requestedShared = std::make_shared< XRequestedType >(); + fNext = requestedShared; + return *requestedShared; + } + return fNext->template Of< XRequestedType >(); + } + + template< class XBaseType > + template< class XRequestedType > + const XRequestedType& KTExtensibleCore< XBaseType >::Of() const + { + const XRequestedType* requested = dynamic_cast< const XRequestedType* >( this ); + if( requested != nullptr ) + { + return *requested; + } + + if( ! fNext ) + { + throw KTException() << "Cannot add to a const extensible object"; + } + return fNext->template Of< XRequestedType >(); + } + + template< class XBaseType > + template< class XRequestedType > + std::shared_ptr< XRequestedType > KTExtensibleCore< XBaseType >::Share() + { + std::shared_ptr< XRequestedType > requested = std::dynamic_pointer_cast< XRequestedType >( ESFTBaseType::shared_from_this() ); + if( requested ) + { + std::cout << "type match" << std::endl; + return requested; + } + + if( ! fNext ) + { + requested = std::make_shared< XRequestedType >(); + fNext = requested; + return requested; + } + return fNext->template Share< XRequestedType >(); + } + + template< class XBaseType > + template< class XRequestedType > + const std::shared_ptr< XRequestedType > KTExtensibleCore< XBaseType >::Share() const + { + const std::shared_ptr< XRequestedType > requested = std::dynamic_pointer_cast< const XRequestedType >( ESFTBaseType::shared_from_this() ); + if( requested ) + { + return requested; + } + + if( ! fNext ) + { + throw KTException() << "Cannot add to a const extensible object"; + } + return fNext->template Share< XRequestedType >(); + } + + template< class XBaseType > + template< class XRequestedType > + bool KTExtensibleCore< XBaseType >::Has() const + { + if( dynamic_cast< const XRequestedType* >( this ) ) + { + return true; + } + if( fNext ) + { + return fNext->template Has< XRequestedType >(); + } + return false; + } + + template< class XBaseType > + template< class XRequestedType > + std::shared_ptr< XRequestedType > KTExtensibleCore< XBaseType >::Detatch() + { + return _Detatch< XRequestedType >(); + } + + template< class XBaseType > + std::shared_ptr< KTExtensibleCore< XBaseType > > KTExtensibleCore< XBaseType >::Next() + { + return fNext; + } + + template< class XBaseType > + template< class XRequestedType > + std::shared_ptr< XRequestedType > KTExtensibleCore< XBaseType >::_Detatch( KTExtensibleCore< XBaseType >::BasePtrType prev ) + { + if( dynamic_cast< XRequestedType* >( this ) ) + { + // create a shared pointer to this here, so that when we reset prev.fNext (if it exists), that won't be the last reference to this + std::shared_ptr< XRequestedType > detatched = std::static_pointer_cast< XRequestedType >( ESFTBaseType::shared_from_this() ); + if( prev ) + { + // link prev and fNext if fNext exists; otherwise resets prev.fNext + prev->fNext = this->fNext; + } + fNext.reset(); + return detatched; + } + + if( fNext ) + { + return fNext->template _Detatch< XRequestedType >( ESFTBaseType::shared_from_this() ); + } + + return std::shared_ptr< XRequestedType >(); + } + + template< class XBaseType > + template< class Archive > + void KTExtensibleCore< XBaseType >::serialize( Archive& ar ) + { + std::cout << "### serialize for " << typeid(KTExtensibleCore< XBaseType >).name() << std::endl; + ar( cereal::base_class< XBaseType >( this ), fNext ); + } + + + //************** + // KTExtensible implementation + //************** + + template< class XInstanceType, class XBaseType > + KTExtensible< XInstanceType, XBaseType >::KTExtensible() : + KTExtensibleCore< XBaseType >() + {} + + template< class XInstanceType, class XBaseType > + KTExtensible< XInstanceType, XBaseType >::KTExtensible( const KTExtensible< XInstanceType, XBaseType >& orig ) : + KTExtensibleCore< XBaseType >() // copy of KTExtensibleCore is done from operator=() + { + *this = orig; + } + + template< class XInstanceType, class XBaseType > + KTExtensible< XInstanceType, XBaseType >::~KTExtensible() + {} + + template< class XInstanceType, class XBaseType > + KTExtensible< XInstanceType, XBaseType >& KTExtensible< XInstanceType, XBaseType >::operator=( const KTExtensible< XInstanceType, XBaseType >& orig ) + { + // must not call any virtual functions on `this` because it's called from the copy constructor + + if( this == &orig ) return *this; + + this->KTExtensibleCore< XBaseType >::operator=( orig ); + + return *this; + } + + template< class XInstanceType, class XBaseType > + KTExtensibleCore< XBaseType >& KTExtensible< XInstanceType, XBaseType >::Pull( const KTExtensibleCore< XBaseType >& orig ) + { + // copies information out from orig based on this's instance type and any extended fields present + + if( this == &orig ) return *this; + + this->fDisableExtendedCopy = true; // we're going to use operator=, so we need to disable copying of extended fields to only get the instance type + XInstanceType* instance = static_cast< XInstanceType* >( this ); // guaranteed to be ok by CRTP + // orig is const, so Of() will throw an exception if XInstanceType is not present + // in that case, make a copy of a new object + try + { + instance->operator=( orig.template Of< XInstanceType >() ); + } + catch( KTException& ) + { + instance->operator=( XInstanceType() ); + } + this->fDisableExtendedCopy = false; + + // continue the pull process for any extended fields present in this + if( this->fNext ) + { + this->fNext->Pull( orig ); + } + + return *this; + } + + template< class XInstanceType, class XBaseType > + std::shared_ptr< KTExtensibleCore< XBaseType > > KTExtensible< XInstanceType, XBaseType >::Clone() const + { + std::shared_ptr< XInstanceType > instancePtr = std::make_shared< XInstanceType >(); + instancePtr->Pull(*this); // extracts just XInstanceType from this because instancePtr doesn't have any extended fields + + // if there are extended fields, clone them + if( this->fNext ) + { + instancePtr->fNext = this->fNext->Clone(); // instancePtr's fNext is a clone of this's fNext + } + + return instancePtr; + } + + template< class XInstanceType, class XBaseType > + template< class Archive > + void KTExtensible< XInstanceType, XBaseType >::serialize( Archive& ar ) + { + std::cout << "### serialize for " << typeid(KTExtensible< XInstanceType, XBaseType >).name() << std::endl; + ar( cereal::base_class< ExtCoreType >( this ) ); + return; + } + +} /* namespace Nymph */ + +#endif /* NYMPH_KTEXTENSIBLE_HH_ */ diff --git a/Library/Utility/KTExtensibleStruct.hh b/Cpp/Library_v1/Utility/KTExtensibleStruct.hh similarity index 53% rename from Library/Utility/KTExtensibleStruct.hh rename to Cpp/Library_v1/Utility/KTExtensibleStruct.hh index 557f83de..63deb644 100644 --- a/Library/Utility/KTExtensibleStruct.hh +++ b/Cpp/Library_v1/Utility/KTExtensibleStruct.hh @@ -9,6 +9,12 @@ #ifndef KTEXTENSIBLESTRUCT_HH_ #define KTEXTENSIBLESTRUCT_HH_ +#include "KTException.hh" + +#include + +#include + namespace Nymph { @@ -19,42 +25,60 @@ namespace Nymph */ template< class XBaseType > - struct KTExtensibleStructCore : public XBaseType + struct KTExtensibleStructCore : public XBaseType, public std::enable_shared_from_this< KTExtensibleStructCore< XBaseType > > { + public: + typedef KTExtensibleStructCore< XBaseType > BasePtrType; + typedef std::shared_ptr< BasePtrType > BasePtr; + typedef std::weak_ptr< BasePtrType > WeakBasePtr; + + private: + typedef std::enable_shared_from_this< KTExtensibleStructCore< XBaseType > > ESFTType; + public: /// Default constructor - KTExtensibleStructCore(void); + KTExtensibleStructCore(); /// Copy constructor; duplicates the extended object - KTExtensibleStructCore(const KTExtensibleStructCore&); + KTExtensibleStructCore( const KTExtensibleStructCore& ); virtual ~KTExtensibleStructCore(); /// Duplicates the extended object - KTExtensibleStructCore& operator=(const KTExtensibleStructCore&); + KTExtensibleStructCore& operator=( const KTExtensibleStructCore& ); /// Removes extended fields - virtual void Clear(void); + virtual void Clear(); /// Returns a reference to the object of type XStructType; creates that object if it doesn't exist - template< class XStructType > inline XStructType& Of(void); + template< class XStructType > + inline XStructType& Of(); /// Returns a const reference to the object of type XStructType; creates that object if it doesn't exist - template< class XStructType > inline const XStructType& Of(void) const; + template< class XStructType > + inline const XStructType& Of() const; /// Returns true if XStructType is or is below this object - template< class XStructType > inline bool Has(void) const; + template< class XStructType > + inline bool Has() const; /// Extracts object of type XStructType - template< class XStructType > inline XStructType* Detatch(void); + template< class XStructType > + inline std::shared_ptr< XStructType > Detatch(); /// Duplicates the extended object - virtual KTExtensibleStructCore* Clone(void) const = 0; + virtual BasePtr Clone() const = 0; /// Duplicates object only - virtual void Pull(const KTExtensibleStructCore< XBaseType >& object) = 0; + virtual void Pull( const BasePtrType& object ) = 0; /// Returns the pointer to the next field - KTExtensibleStructCore* Next() const; + BasePtr Next() const; /// Returns the pointer to the previous field - KTExtensibleStructCore* Prev() const; + BasePtr Prev() const; /// Returns the pointer to the last field - KTExtensibleStructCore* Last() const; + BasePtr Last() const; /// Returns the pointer to the first field - KTExtensibleStructCore* First() const; + BasePtr First() const; protected: void SetPrevPtrInNext(); - mutable KTExtensibleStructCore* fNext; - mutable KTExtensibleStructCore* fPrev; + mutable BasePtr fNext; + mutable WeakBasePtr fPrev; + + private: + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); }; @@ -62,63 +86,68 @@ namespace Nymph template< class XInstanceType, class XBaseType > struct KTExtensibleStruct : KTExtensibleStructCore< XBaseType > { + public: + typedef typename KTExtensibleStructCore< XBaseType >::BasePtrType BasePtrType; + typedef typename KTExtensibleStructCore< XBaseType >::BasePtr BasePtr; + public: /// Default constructor - KTExtensibleStruct(void); + KTExtensibleStruct(); /// Copy constructor; duplicates the extended object - KTExtensibleStruct(const KTExtensibleStruct& object); + KTExtensibleStruct( const KTExtensibleStruct& object ); virtual ~KTExtensibleStruct(); /// Duplicates the extended object - KTExtensibleStruct& operator=(const KTExtensibleStruct& object); + KTExtensibleStruct& operator=( const KTExtensibleStruct& object ); /// Duplicates the extended object - virtual KTExtensibleStructCore< XBaseType >* Clone(void) const; + virtual BasePtr Clone() const; /// Duplicates object only - virtual void Pull(const KTExtensibleStructCore< XBaseType >& object); - void SetIsCopyDisabled(bool flag); + virtual void Pull( const BasePtrType& object ); + void SetIsCopyDisabled( bool flag ); private: bool fIsCopyDisabled; + friend class cereal::access; + + template< class Archive > + void serialize( Archive& ar ); }; template - KTExtensibleStructCore::KTExtensibleStructCore(void) + KTExtensibleStructCore::KTExtensibleStructCore(void) : + fNext(), + fPrev() { - fPrev = 0; - fNext = 0; } template - KTExtensibleStructCore::KTExtensibleStructCore(const KTExtensibleStructCore&) + KTExtensibleStructCore::KTExtensibleStructCore( const KTExtensibleStructCore& ) : + fNext(), + fPrev() { - fPrev = 0; - fNext = 0; } template KTExtensibleStructCore::~KTExtensibleStructCore() { - delete fNext; - fNext = 0; } template - KTExtensibleStructCore& KTExtensibleStructCore::operator=(const KTExtensibleStructCore&) + KTExtensibleStructCore& KTExtensibleStructCore::operator=( const KTExtensibleStructCore& ) { - fNext = 0; + fNext.reset(); return *this; } template - void KTExtensibleStructCore::Clear(void) + void KTExtensibleStructCore::Clear() { - delete fNext; - fNext = 0; + fNext.reset(); } template template - inline XStructType& KTExtensibleStructCore::Of(void) + inline XStructType& KTExtensibleStructCore::Of() { XStructType* target = dynamic_cast(this); if (target) @@ -128,16 +157,16 @@ namespace Nymph if (! fNext) { - fNext = new XStructType(); - fNext->fPrev = this; + fNext = std::make_shared< XStructType >(); + fNext->fPrev = ESFTType::shared_from_this(); } - return fNext->Of(); + return fNext->template Of(); } template template - inline const XStructType& KTExtensibleStructCore::Of(void) const + inline const XStructType& KTExtensibleStructCore::Of() const { const XStructType* target = dynamic_cast(this); if (target) @@ -145,20 +174,19 @@ namespace Nymph return *target; } - if (fNext == 0) + if (! fNext) { - fNext = new XStructType(); - fNext->fPrev = const_cast< KTExtensibleStructCore< XBaseType >* >(this); + throw KTException() << "Requested component of (const) extensible struct not present"; } - return fNext->Of(); + return fNext->template Of(); } template template - inline bool KTExtensibleStructCore::Has(void) const + inline bool KTExtensibleStructCore::Has() const { if (dynamic_cast(this)) { @@ -166,7 +194,7 @@ namespace Nymph } if (fNext) { - return fNext->Has(); + return fNext->template Has(); } return false; @@ -176,67 +204,90 @@ namespace Nymph template template - inline XStructType* KTExtensibleStructCore::Detatch(void) + inline std::shared_ptr< XStructType > KTExtensibleStructCore::Detatch() { if (! fNext) { - return 0; + return std::shared_ptr< XStructType >(); } - XStructType* next = dynamic_cast(fNext); + std::shared_ptr< XStructType > next = std::dynamic_pointer_cast< XStructType >(fNext); if (next) { if (next->fNext) { fNext = next->fNext; - fNext->fPrev = this; - next->fNext = 0; + fNext->fPrev = ESFTType::shared_from_this(); + next->fNext.reset(); } - next->fPrev = 0; + next->fPrev.reset(); return next; } - return fNext->Detatch(); + return fNext->template Detatch(); } template - inline KTExtensibleStructCore* KTExtensibleStructCore::Next() const + inline typename KTExtensibleStructCore::BasePtr KTExtensibleStructCore::Next() const { return fNext; } template - inline KTExtensibleStructCore* KTExtensibleStructCore::Prev() const + inline typename KTExtensibleStructCore::BasePtr KTExtensibleStructCore::Prev() const { - return fPrev; + return fPrev.lock(); } template - inline KTExtensibleStructCore* KTExtensibleStructCore::Last() const + inline typename KTExtensibleStructCore::BasePtr KTExtensibleStructCore::Last() const { - if (fNext == 0) return this; + if (fNext) return this; return fNext->Last(); } template - inline KTExtensibleStructCore* KTExtensibleStructCore::First() const + inline typename KTExtensibleStructCore::BasePtr KTExtensibleStructCore::First() const { - if (fPrev == 0) return this; - return fPrev->First(); + if (! fPrev) return BasePtr(this); + return fPrev.lock()->First(); } template inline void KTExtensibleStructCore::SetPrevPtrInNext() { - fNext->fPrev = this; + fNext->fPrev = ESFTType::shared_from_this(); return; } + template< class XBaseType > + template< class Archive > + void KTExtensibleStructCore::serialize( Archive& ar ) + { + std::cout << "### serialize for " << typeid(XBaseType).name() << std::endl; + ar( this ); + if( fNext == 0 ){ return; } + + // archive pointer ( fNext )? + } + + + + template< class XInstanceType, class XBaseType > + template< class Archive > + void KTExtensibleStruct::serialize( Archive& ar ) + { + std::cout << "### serialize for " << typeid(XBaseType).name() << std::endl; + ar( cereal::base_class< BasePtrType >( this ), fIsCopyDisabled ); + + return; + } template - KTExtensibleStruct::KTExtensibleStruct(void) + KTExtensibleStruct::KTExtensibleStruct() : + KTExtensibleStructCore< XBaseType >(), + fIsCopyDisabled( false ) { - fIsCopyDisabled = false; } template @@ -267,13 +318,12 @@ namespace Nymph return *this; } - delete this->fNext; - this->fNext = 0; + this->fNext.reset(); if (object.fNext) { this->fNext = object.fNext->Clone(); - this->KTExtensibleStructCore< XBaseType >::SetPrevPtrInNext(); + this->BasePtrType::SetPrevPtrInNext(); //this->fNext->fPrev = this; } @@ -281,22 +331,24 @@ namespace Nymph } template - KTExtensibleStructCore* KTExtensibleStruct::Clone(void) const + typename KTExtensibleStruct::BasePtr KTExtensibleStruct::Clone() const { // assume CRTP is used properly, // otherwise compiling fails here (intended behavior) - XInstanceType* instance = new XInstanceType(dynamic_cast(*this)); + typedef KTExtensibleStruct< XInstanceType, XBaseType > InstancePtrType; + typedef std::shared_ptr< InstancePtrType > InstancePtr; + InstancePtr instance = std::make_shared< InstancePtrType >( XInstanceType(dynamic_cast(*this)) ); if (this->fNext) { instance->fNext = this->fNext->Clone(); instance->SetPrevPtrInNext(); //instance->fNext->fPrev = instance->fNext; } - return instance; + return std::static_pointer_cast< BasePtrType, InstancePtrType >( instance ); } template - void KTExtensibleStruct::Pull(const KTExtensibleStructCore& object) + void KTExtensibleStruct::Pull(const BasePtrType& object) { if (&object == this) { diff --git a/Library/Utility/KTExtensibleStructFactory.hh b/Cpp/Library_v1/Utility/KTExtensibleStructFactory.hh similarity index 100% rename from Library/Utility/KTExtensibleStructFactory.hh rename to Cpp/Library_v1/Utility/KTExtensibleStructFactory.hh diff --git a/Cpp/Library_v1/Utility/KTLogger.hh b/Cpp/Library_v1/Utility/KTLogger.hh new file mode 100644 index 00000000..adeee63e --- /dev/null +++ b/Cpp/Library_v1/Utility/KTLogger.hh @@ -0,0 +1,51 @@ +/* + * KTLogger.hh + * Based on KLogger.h, from KATRIN's Kasper + * + * Created on: Jan 23, 2014 + * Author: nsoblath + */ + +#ifndef KTLOGGER_HH_ +#define KTLOGGER_HH_ + +#include "logger.hh" + +/* + * The Nymph logger is now a simple rebranding of the Scarab logger + */ + +// PUBLIC MACROS + +#define KTLOGGER(I,K) static ::scarab::logger I(K); + +#define KTLOG(...) macro_dispatcher(__LOG_LOG_, __VA_ARGS__)(__VA_ARGS__) +#ifdef NDEBUG +#define KTTRACE(...) +#define KTDEBUG(...) +#else +#define KTTRACE(...) macro_dispatcher(__LOG_TRACE_, __VA_ARGS__)(__VA_ARGS__) +#define KTDEBUG(...) macro_dispatcher(__LOG_DEBUG_, __VA_ARGS__)(__VA_ARGS__) +#endif +#define KTINFO(...) macro_dispatcher(__LOG_INFO_, __VA_ARGS__)(__VA_ARGS__) +#define KTPROG(...) macro_dispatcher(__LOG_PROG_, __VA_ARGS__)(__VA_ARGS__) +#define KTWARN(...) macro_dispatcher(__LOG_WARN_, __VA_ARGS__)(__VA_ARGS__) +#define KTERROR(...) macro_dispatcher(__LOG_ERROR_, __VA_ARGS__)(__VA_ARGS__) +#define KTFATAL(...) macro_dispatcher(__LOG_FATAL_, __VA_ARGS__)(__VA_ARGS__) +#define KTASSERT(...) macro_dispatcher(__LOG_ASSERT_, __VA_ARGS__)(__VA_ARGS__) + +#define KTLOG_ONCE(...) macro_dispatcher(__LOG_LOG_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#ifdef NDEBUG +#define KTTRACE_ONCE(...) +#define KTDEBUG_ONCE(...) +#else +#define KTTRACE_ONCE(...) macro_dispatcher(__LOG_TRACE_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#define KTDEBUG_ONCE(...) macro_dispatcher(__LOG_DEBUG_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#endif +#define KTINFO_ONCE(...) macro_dispatcher(__LOG_INFO_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#define KTPROG_ONCE(...) macro_dispatcher(__LOG_PROG_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#define KTWARN_ONCE(...) macro_dispatcher(__LOG_WARN_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#define KTERROR_ONCE(...) macro_dispatcher(__LOG_ERROR_ONCE_, __VA_ARGS__)(__VA_ARGS__) +#define KTFATAL_ONCE(...) macro_dispatcher(__LOG_FATAL_ONCE_, __VA_ARGS__)(__VA_ARGS__) + +#endif /* KTLOGGER_HH_ */ diff --git a/Cpp/Library_v1/Utility/KTMemberVariable.hh b/Cpp/Library_v1/Utility/KTMemberVariable.hh new file mode 100644 index 00000000..f9bdec14 --- /dev/null +++ b/Cpp/Library_v1/Utility/KTMemberVariable.hh @@ -0,0 +1,98 @@ +/* + * KTMemberVariable.hh + * + * Created on: Aug 5, 2014 + * Author: nsoblath + */ + +#ifndef KTMEMBERVARIABLE_HH_ +#define KTMEMBERVARIABLE_HH_ + +/** + * Macros for class member variables + * + * In all cases remember to initialize the variables! + * + * For "normal" variables + * Defines accessors [type GetMyVar() const], [void SetMyVar( type )], and member variable [type fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMBERVARIABLE + * - MEMBERVARIABLE_NOSET + * - MEMBERVARIABLE_STATIC + * - MEMBERVARIABLE_STATIC_NOSET + * + * For variables accessed by reference + * Defines accessors [const type& MyVar() const], [type& MyVar()], and member variable [type fMyVar] + * The non-const function is not available if the _CONST macros are used + * - MEMBERVARIABLE_REF + * - MEMBERVARIABLE_REF_CONST + * - MEMBERVARIABLE_REF_STATIC + * - MEMBERVARIABLE_REF_STATIC_CONST + * + * For pointer variables + * Defines accessors [type* GetMyVar() const], [void SetMyVar( type* )], and member variable [type* fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMBERVARIABLE_PTR + * - MEMBERVARIABLE_PTR_NOSET + * - MEMBERVARIABLE_PTR_STATIC + * - MEMBERVARIABLE_PTR_STATIC_NOSET + * + * For shared_ptr's + * Defines accessors [const std::shared_ptr< type > MyVar() const], [std::shared_ptr< type > MyVar()], and member variable [std::shared_ptr< type > fMyVar] + * The non-const function is not available if the _CONST macros are used + * - MEMBERVARIABLE_SHARED_PTR + * - MEMBERVARIABLE_SHARED_PTR_CONST + * - MEMBERVARIABLE_SHARED_PTR_STATIC + * - MEMBERVARIABLE_SHARED_PTR_STATIC_CONST + * + * For atomic variables + * Defines accessors [type GetMyVar() const], [void SetMyVar( type )], and member variable [std::atomic< type > fMyVar] + * The Set function is not available if the _NOSET macros are used + * - MEMBERVARIABLE_ATOMIC + * - MEMBERVARIABLE_ATOMIC_NOSET + * - MEMBERVARIABLE_ATOMIC_STATIC + * - MEMBERVARIABLE_ATOMIC_STATIC_NOSET + * + */ + +#define set_prefix Set +#define get_prefix Get +#define var_prefix f +#define static_prefix s + +#include "_camel_case_member_variables.hh" + +#define MEMBERVARIABLE camel_case_mv_accessible +#define MEMBERVARIABLE_NOSET camel_case_mv_accessible_noset +#define MEMBERVARIABLE_STATIC camel_case_mv_accessible_static +#define MEMBERVARIABLE_STATIC_NOSET camel_case_mv_accessible_static_noset +#define MEMBERVARIABLE_MUTABLE camel_case_mv_accessible_mutable +#define MEMBERVARIABLE_MUTABLE_NOSET camel_case_mv_accessible_mutable_noset + +#define MEMBERVARIABLE_REF camel_case_mv_referrable +#define MEMBERVARIABLE_REF_CONST camel_case_mv_referrable_const +#define MEMBERVARIABLE_REF_STATIC camel_case_mv_referrable_static +#define MEMBERVARIABLE_REF_MUTABLE camel_case_mv_referrable_mutable +#define MEMBERVARIABLE_REF_MUTABLE_CONST camel_case_mv_referrable_mutable + +#define MEMBERVARIABLE_PTR camel_case_mv_assignable +#define MEMBERVARIABLE_PTR_NOSET camel_case_mv_assignable_noset +#define MEMBERVARIABLE_PTR_STATIC camel_case_mv_assignable_static +#define MEMBERVARIABLE_PTR_STATIC_NOSET camel_case_mv_assignable_static_noset +#define MEMBERVARIABLE_PTR_MUTABLE camel_case_mv_assignable_mutable +#define MEMBERVARIABLE_PTR_MUTABLE_NOSET camel_case_mv_assignable_mutable_noset + +#define MEMBERVARIABLE_SHARED_PTR camel_case_mv_shared_ptr +#define MEMBERVARIABLE_SHARED_PTR_CONST camel_case_mv_shared_ptr_const +#define MEMBERVARIABLE_SHARED_PTR_STATIC camel_case_mv_shared_ptr_static +#define MEMBERVARIABLE_SHARED_PTR_MUTABLE camel_case_mv_shared_ptr_mutable +#define MEMBERVARIABLE_SHARED_PTR_MUTABLE_CONST camel_case_mv_shared_ptr_mutable + +#define MEMBERVARIABLE_ATOMIC camel_case_mv_atomic +#define MEMBERVARIABLE_ATOMIC_NOSET camel_case_mv_atomic_noset +#define MEMBERVARIABLE_ATOMIC_STATIC camel_case_mv_atomic_static +#define MEMBERVARIABLE_ATOMIC_STATIC_NOSET camel_case_mv_atomic_static_noset +#define MEMBERVARIABLE_ATOMIC_MUTABLE camel_case_mv_atomic_mutable +#define MEMBERVARIABLE_ATOMIC_MUTABLE_NOSET camel_case_mv_atomic_mutable_noset + +#endif /* KTMEMBERVARIABLE_HH_ */ diff --git a/Cpp/Library_v1/Utility/KTParamPybind.hh b/Cpp/Library_v1/Utility/KTParamPybind.hh new file mode 100644 index 00000000..dbbf917e --- /dev/null +++ b/Cpp/Library_v1/Utility/KTParamPybind.hh @@ -0,0 +1,101 @@ +/* + * KTPyNymph.cc + * + * Created on: Feb 1, 2018 + * Author: N. Oblath, L. Gladstone + */ + +#include "param.hh" + +#include "pybind11/pybind11.h" + +namespace Nymph +{ + + void ExportParamPybind( pybind11::module& mod ) + { + // param + pybind11::class_< scarab::param >( mod, "param" ); + + // param_value + pybind11::class_< scarab::param_value, scarab::param >( mod, "param_value" ) + .def( pybind11::init< bool >() ) + .def( pybind11::init< unsigned >() ) + .def( pybind11::init< int >() ) + .def( pybind11::init< double >() ) + .def( pybind11::init< const std::string& >() ) + .def( pybind11::init< const char* >() ) + + .def( "is_bool", (bool (scarab::param_value::*)() const) &scarab::param_value::is_bool, "Return whether the param_value stores a boolean value" ) + .def( "is_uint", (bool (scarab::param_value::*)() const) &scarab::param_value::is_uint, "Return whether the param_value stores an unsigned integer value" ) + .def( "is_int", (bool (scarab::param_value::*)() const) &scarab::param_value::is_int, "Return whether the param_value stores a signed integer value" ) + .def( "is_double", (bool (scarab::param_value::*)() const) &scarab::param_value::is_double, "Return whether the param_value stores a double value" ) + .def( "is_string", (bool (scarab::param_value::*)() const) &scarab::param_value::is_string, "Return whether the param_value stores a string value" ) + + .def( "as_bool", (bool (scarab::param_value::*)() const) &scarab::param_value::as_bool, "Get parameter value as a bool" ) + .def( "as_uint", (unsigned (scarab::param_value::*)() const) &scarab::param_value::as_uint, "Get parameter value as an unsigned integer" ) + .def( "as_int", (int (scarab::param_value::*)() const) &scarab::param_value::as_int, "Get parameter value as a signed integer" ) + .def( "as_double", (double (scarab::param_value::*)() const) &scarab::param_value::as_double, "Get parameter value as a float" ) + .def( "as_string", (const std::string& (scarab::param_value::*)() const) &scarab::param_value::as_string, "Get parameter value as a string" ) + + .def( "set", (void (scarab::param_value::*)(bool)) &scarab::param_value::set, "Set a bool value" ) + .def( "set", (void (scarab::param_value::*)(unsigned)) &scarab::param_value::set, "Set an unsigned integer value" ) + .def( "set", (void (scarab::param_value::*)(int)) &scarab::param_value::set, "Set a signed integer value" ) + .def( "set", (void (scarab::param_value::*)(double)) &scarab::param_value::set, "Set an float value" ) + .def( "set", (void (scarab::param_value::*)(std::string)) &scarab::param_value::set, "Set an string value" ) + ; + + // param_node + pybind11::class_< scarab::param_node, scarab::param >( mod, "param_node" ) + .def( pybind11::init<>() ) + + .def( "add",(bool (scarab::param_node::*)(const std::string&, const scarab::param&)) &scarab::param_node::add, + "Add a param object to a node" ) + + .def( "at", (scarab::param& (scarab::param_node::*)(const std::string&)) &scarab::param_node::operator[], + "Get the param object for a given key" ) + + // Get value of the parameter, bringing along the default value + .def( "get_value", (bool (scarab::param_node::*)(const std::string&, bool) const) &scarab::param_node::get_value, + "Get parameter node value as a bool" ) + .def( "get_value", (unsigned (scarab::param_node::*)(const std::string&, unsigned) const) &scarab::param_node::get_value, + "Get parameter node value as an unsigned integer" ) + .def( "get_value", (int (scarab::param_node::*)(const std::string&, int) const) &scarab::param_node::get_value, + "Get parameter node value as a signed integer" ) + .def( "get_value", (double (scarab::param_node::*)(const std::string&, double) const) &scarab::param_node::get_value, + "Get parameter node value as a float" ) + .def( "get_value", (std::string (scarab::param_node::*)(const std::string&, const std::string& ) const) &scarab::param_node::get_value, + "Get parameter node value as a string" ) + ; + + // param_array + pybind11::class_< scarab::param_array, scarab::param >( mod, "param_array" ) + .def( pybind11::init<>() ) + + .def( "size", (unsigned (scarab::param_array::*)() const) &scarab::param_array::size, + "Returns the size of the array" ) + + .def( "resize", (void (scarab::param_array::*)(unsigned)) &scarab::param_array::resize, + "Sets the size of the array; if smaller than the current size, the extra elements are deleted") + + .def( "assign", (void (scarab::param_array::*)(unsigned, const scarab::param&)) &scarab::param_array::assign, + "Add a param object to the specified index in a array" ) + + .def( "at", (scarab::param& (scarab::param_array::*)(unsigned)) &scarab::param_array::operator[], + "Get the param object for a given index" ) + + // Get value of the parameter, bringing along the default value + .def( "get_value", (bool (scarab::param_array::*)(unsigned, bool) const) &scarab::param_array::get_value, + "Get parameter array value as a bool" ) + .def( "get_value", (unsigned (scarab::param_array::*)(unsigned, unsigned) const) &scarab::param_array::get_value, + "Get parameter array value as an unsigned integer" ) + .def( "get_value", (int (scarab::param_array::*)(unsigned, int) const) &scarab::param_array::get_value, + "Get parameter array value as a signed integer" ) + .def( "get_value", (double (scarab::param_array::*)(unsigned, double) const) &scarab::param_array::get_value, + "Get parameter array value as a float" ) + .def( "get_value", (std::string (scarab::param_array::*)(unsigned, const std::string& ) const) &scarab::param_array::get_value, + "Get parameter array value as a string" ) + ; + } + +} /* namespace Nymph */ diff --git a/Library/Utility/KTPythonMacros.hh b/Cpp/Library_v1/Utility/KTPythonMacros.hh similarity index 100% rename from Library/Utility/KTPythonMacros.hh rename to Cpp/Library_v1/Utility/KTPythonMacros.hh diff --git a/Cpp/Library_v1/Utility/KTTIFactory.hh b/Cpp/Library_v1/Utility/KTTIFactory.hh new file mode 100644 index 00000000..294bafe9 --- /dev/null +++ b/Cpp/Library_v1/Utility/KTTIFactory.hh @@ -0,0 +1,364 @@ +/* + * KTTIFactory.hh + * + * Created on: Jan 2, 2013 + * Author: nsoblath + * + * Type-Indexed Factory and Registrars + */ + +#ifndef KTTIFACTORY_HH_ +#define KTTIFACTORY_HH_ + +#include "KTLogger.hh" + +#include "singleton.hh" + +#include +#include + +namespace Nymph +{ + KTLOGGER(utillog_ti_factory, "KTTIFactory"); + + //******************** + // Class definitions + //******************** + + template< class XBaseType, typename ... XArgs > + class KTTIFactory; + + template< class XBaseType, typename ... XArgs > + class KTTIRegistrarBase + { + public: + KTTIRegistrarBase() {} + virtual ~KTTIRegistrarBase() {} + + public: + friend class KTTIFactory< XBaseType, XArgs... >; + + protected: + virtual XBaseType* Create(XArgs ... args) const = 0; + + }; + + template< class XBaseType, class XDerivedType, typename ... XArgs > + class KTTIRegistrar : public KTTIRegistrarBase< XBaseType, XArgs... > + { + public: + KTTIRegistrar(); + virtual ~KTTIRegistrar(); + + protected: + void Register() const; + + XBaseType* Create(XArgs ... args) const; + + }; + + + template< class XBaseType, typename ... XArgs > + class KTTIFactory : public scarab::singleton< KTTIFactory< XBaseType, XArgs... > > + { + public: + struct CompareTypeInfo + { + bool operator() (const std::type_info* lhs, const std::type_info* rhs) + { + return lhs->before(*rhs); + } + }; + + public: + typedef std::map< const std::type_info*, const KTTIRegistrarBase< XBaseType, XArgs... >* > FactoryMap; + typedef typename FactoryMap::value_type FactoryEntry; + typedef typename FactoryMap::iterator FactoryIt; + typedef typename FactoryMap::const_iterator FactoryCIt; + + public: + template< class XDerivedType > + XBaseType* Create(XArgs ... args); + + XBaseType* Create(const FactoryCIt& iter, XArgs ... args); + + template< class XDerivedType > + void Register(const KTTIRegistrarBase< XBaseType, XArgs... >* registrar); + + FactoryCIt GetFactoryMapBegin() const; + FactoryCIt GetFactoryMapEnd() const; + + protected: + FactoryMap* fMap; + + + protected: + friend class scarab::singleton< KTTIFactory >; + friend class scarab::destroyer< KTTIFactory >; + KTTIFactory(); + ~KTTIFactory(); + }; + + //***************************************** + // Partial specialization for empty XArgs + //***************************************** + + template< class XBaseType > + class KTTIRegistrarBase< XBaseType, void > + { + public: + KTTIRegistrarBase() {} + virtual ~KTTIRegistrarBase() {} + + public: + friend class KTTIFactory< XBaseType >; + + protected: + virtual XBaseType* Create() const = 0; + + }; + + template< class XBaseType, class XDerivedType > + class KTTIRegistrar< XBaseType, XDerivedType, void > : public KTTIRegistrarBase< XBaseType, void > + { + public: + KTTIRegistrar(); + virtual ~KTTIRegistrar(); + + protected: + void Register() const; + + XBaseType* Create() const; + + }; + + + template< class XBaseType > + class KTTIFactory< XBaseType, void > : public scarab::singleton< KTTIFactory< XBaseType, void > > + { + public: + struct CompareTypeInfo + { + bool operator() (const std::type_info* lhs, const std::type_info* rhs) + { + return lhs->before(*rhs); + } + }; + + public: + typedef std::map< const std::type_info*, const KTTIRegistrarBase< XBaseType, void >* > FactoryMap; + typedef typename FactoryMap::value_type FactoryEntry; + typedef typename FactoryMap::iterator FactoryIt; + typedef typename FactoryMap::const_iterator FactoryCIt; + + public: + template< class XDerivedType > + XBaseType* Create(); + + XBaseType* Create(const FactoryCIt& iter); + + template< class XDerivedType > + void Register(const KTTIRegistrarBase< XBaseType, void >* registrar); + + FactoryCIt GetFactoryMapBegin() const; + FactoryCIt GetFactoryMapEnd() const; + + protected: + FactoryMap* fMap; + + + protected: + friend class scarab::singleton< KTTIFactory >; + friend class scarab::destroyer< KTTIFactory >; + KTTIFactory(); + ~KTTIFactory(); + }; + + + //****************** + // Implementations + //****************** + + // Factory + + template< class XBaseType, typename ... XArgs > + template< class XDerivedType > + XBaseType* KTTIFactory< XBaseType, XArgs... >::Create(XArgs ... args) + { + FactoryCIt it = fMap->find(&typeid(XDerivedType)); + if (it == fMap->end()) + { + KTERROR(utillog_ti_factory, "Did not find factory with type <" << typeid(XDerivedType).name() << ">."); + return NULL; + } + + return it->second->Create(args...); + } + + template< class XBaseType, typename ... XArgs > + XBaseType* KTTIFactory< XBaseType, XArgs... >::Create(const FactoryCIt& iter, XArgs ... args) + { + return iter->second->Create(args...); + } + + template< class XBaseType, typename ... XArgs > + template< class XDerivedType > + void KTTIFactory< XBaseType, XArgs... >::Register(const KTTIRegistrarBase< XBaseType, XArgs... >* registrar) + { + // A local (static) logger is created inside this function to avoid static initialization order problems + KTLOGGER(utillog_ti_factory_reg, "KTTIFactory-Register"); + + FactoryCIt it = fMap->find(&typeid(XDerivedType)); + if (it != fMap->end()) + { + KTERROR(utillog_ti_factory_reg, "Already have factory registered for type <" << typeid(XDerivedType).name() << ">."); + return; + } + fMap->insert(std::pair< const std::type_info*, const KTTIRegistrarBase< XBaseType, XArgs... >* >(&typeid(XDerivedType), registrar)); + KTDEBUG(utillog_ti_factory_reg, "Registered a factory for class type " << typeid(XDerivedType).name() << ", factory #" << fMap->size()-1); + } + + template< class XBaseType, typename ... XArgs > + KTTIFactory< XBaseType, XArgs... >::KTTIFactory() : + fMap(new FactoryMap()) + {} + + template< class XBaseType, typename ... XArgs > + KTTIFactory< XBaseType, XArgs... >::~KTTIFactory() + { + delete fMap; + } + + template< class XBaseType, typename ... XArgs > + typename KTTIFactory< XBaseType, XArgs... >::FactoryCIt KTTIFactory< XBaseType, XArgs... >::GetFactoryMapBegin() const + { + return fMap->begin(); + } + + template< class XBaseType, typename ... XArgs > + typename KTTIFactory< XBaseType, XArgs... >::FactoryCIt KTTIFactory< XBaseType, XArgs... >::GetFactoryMapEnd() const + { + return fMap->end(); + } + + // Registrar + + template< class XBaseType, class XDerivedType, typename ... XArgs > + KTTIRegistrar< XBaseType, XDerivedType, XArgs... >::KTTIRegistrar() : + KTTIRegistrarBase< XBaseType, XArgs... >() + { + Register(); + } + + template< class XBaseType, class XDerivedType, typename ... XArgs > + KTTIRegistrar< XBaseType, XDerivedType, XArgs... >::~KTTIRegistrar() + {} + + template< class XBaseType, class XDerivedType, typename ... XArgs > + void KTTIRegistrar< XBaseType, XDerivedType, XArgs... >::Register() const + { + KTTIFactory< XBaseType, XArgs... >::get_instance()->template Register(this); + return; + } + + template< class XBaseType, class XDerivedType, typename ... XArgs > + XBaseType* KTTIRegistrar< XBaseType, XDerivedType, XArgs... >::Create(XArgs ... args) const + { + return dynamic_cast< XBaseType* >(new XDerivedType(args...)); + } + + //******************************************* + // Implementations (partial specialization) + //******************************************* + + // Factory + + template< class XBaseType > + template< class XDerivedType > + XBaseType* KTTIFactory< XBaseType, void >::Create() + { + FactoryCIt it = fMap->find(&typeid(XDerivedType)); + if (it == fMap->end()) + { + KTERROR(utillog_ti_factory, "Did not find factory with type <" << typeid(XDerivedType).name() << ">."); + return NULL; + } + + return it->second->Create(); + } + + template< class XBaseType > + XBaseType* KTTIFactory< XBaseType, void >::Create(const FactoryCIt& iter) + { + return iter->second->Create(); + } + + template< class XBaseType > + template< class XDerivedType > + void KTTIFactory< XBaseType, void >::Register(const KTTIRegistrarBase< XBaseType, void >* registrar) + { + // A local (static) logger is created inside this function to avoid static initialization order problems + KTLOGGER(utillog_ti_factory_reg, "KTTIFactory-Register"); + + FactoryCIt it = fMap->find(&typeid(XDerivedType)); + if (it != fMap->end()) + { + KTERROR(utillog_ti_factory_reg, "Already have factory registered for type <" << typeid(XDerivedType).name() << ">."); + return; + } + fMap->insert(std::pair< const std::type_info*, const KTTIRegistrarBase< XBaseType, void >* >(&typeid(XDerivedType), registrar)); + KTDEBUG(utillog_ti_factory_reg, "Registered a factory for class type " << typeid(XDerivedType).name() << ", factory #" << fMap->size()-1); + } + + template< class XBaseType > + KTTIFactory< XBaseType, void >::KTTIFactory() : + fMap(new FactoryMap()) + {} + + template< class XBaseType > + KTTIFactory< XBaseType, void >::~KTTIFactory() + { + delete fMap; + } + + template< class XBaseType > + typename KTTIFactory< XBaseType, void >::FactoryCIt KTTIFactory< XBaseType, void >::GetFactoryMapBegin() const + { + return fMap->begin(); + } + + template< class XBaseType > + typename KTTIFactory< XBaseType, void >::FactoryCIt KTTIFactory< XBaseType, void >::GetFactoryMapEnd() const + { + return fMap->end(); + } + + // Registrar + + template< class XBaseType, class XDerivedType > + KTTIRegistrar< XBaseType, XDerivedType, void >::KTTIRegistrar() : + KTTIRegistrarBase< XBaseType, void >() + { + Register(); + } + + template< class XBaseType, class XDerivedType > + KTTIRegistrar< XBaseType, XDerivedType, void >::~KTTIRegistrar() + {} + + template< class XBaseType, class XDerivedType > + void KTTIRegistrar< XBaseType, XDerivedType, void >::Register() const + { + KTTIFactory< XBaseType, void >::get_instance()->template Register(this); + return; + } + + template< class XBaseType, class XDerivedType > + XBaseType* KTTIRegistrar< XBaseType, XDerivedType, void >::Create() const + { + return dynamic_cast< XBaseType* >(new XDerivedType()); + } + + +} /* namespace Nymph */ +#endif /* KTTIFACTORY_HH_ */ diff --git a/Library/Utility/KTTime.cc b/Cpp/Library_v1/Utility/KTTime.cc similarity index 100% rename from Library/Utility/KTTime.cc rename to Cpp/Library_v1/Utility/KTTime.cc diff --git a/Library/Utility/KTTime.hh b/Cpp/Library_v1/Utility/KTTime.hh similarity index 100% rename from Library/Utility/KTTime.hh rename to Cpp/Library_v1/Utility/KTTime.hh diff --git a/Templates/KTProcessorTemplate.cc b/Cpp/Templates/KTProcessorTemplate.cc similarity index 100% rename from Templates/KTProcessorTemplate.cc rename to Cpp/Templates/KTProcessorTemplate.cc diff --git a/Templates/KTProcessorTemplate.hh b/Cpp/Templates/KTProcessorTemplate.hh similarity index 100% rename from Templates/KTProcessorTemplate.hh rename to Cpp/Templates/KTProcessorTemplate.hh diff --git a/Templates/MainCMakeLists.txt b/Cpp/Templates/MainCMakeLists.txt similarity index 100% rename from Templates/MainCMakeLists.txt rename to Cpp/Templates/MainCMakeLists.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..54a04245 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,116 @@ +ARG base_img_repo=python +ARG base_img_tag=3.12.1-slim-bookworm + +# This FROM line includes a label so that the dependencies can be built by themselves by using the `--target` argument of `docker build` +FROM ${base_img_repo}:${base_img_tag} AS base + +ARG build_type=Release +ARG build_tests_exe=FALSE +ARG narg=2 +ARG nymph_tag=dev + +ENV NYMPH_BUILD_TYPE=$build_type +ENV NYMPH_BUILD_TESTS_EXE=$build_tests_exe + +ENV NYMPH_TAG=$nymph_tag +ENV NYMPH_PREFIX=/usr/local/p8/nymph/$NYMPH_TAG + +# Set bash as the default shell +SHELL ["/bin/bash", "-c"] + +RUN apt-get update && \ + apt-get clean && \ + apt-get --fix-missing -y install \ + build-essential \ + cmake \ +# gdb \ + git \ + libboost-chrono-dev \ + libboost-filesystem-dev \ + libboost-date-time-dev \ + libboost-system-dev \ + libboost-thread-dev \ + libyaml-cpp-dev \ + rapidjson-dev && \ +# pybind11-dev \ +# wget && \ + rm -rf /var/lib/apt/lists/* + +# use pybind11_checkout to specify a tag or branch name to checkout +ARG pybind11_checkout=v3.0.0 +ARG pybind11_repo=https://github.com/pybind/pybind11.git +ARG pybind11_name=pybind11 +RUN cd /usr/local && \ + git clone ${pybind11_repo} && \ + cd ${pybind11_name} && \ + git checkout ${pybind11_checkout} && \ + mkdir build && \ + cd build && \ + cmake -DPYBIND11_TEST=FALSE .. && \ + make -j${narg} install && \ + cd / && \ + rm -rf /usr/local/${pybind11_name} + +RUN mkdir -p $NYMPH_PREFIX &&\ + chmod -R 777 $NYMPH_PREFIX/.. &&\ + cd $NYMPH_PREFIX &&\ + echo "export NYMPH_TAG=${NYMPH_TAG}" >> setup.sh &&\ + echo "export NYMPH_PREFIX=${NYMPH_PREFIX}" >> setup.sh &&\ + echo 'ln -sfT $NYMPH_PREFIX $NYMPH_PREFIX/../current' >> setup.sh &&\ + echo 'export PATH=$NYMPH_PREFIX/bin:$PATH' >> setup.sh &&\ + echo 'export LD_LIBRARY_PATH=$NYMPH_PREFIX/lib:$LD_LIBRARY_PATH' >> setup.sh &&\ + /bin/true +# echo "source ${COMMON_BUILD_PREFIX}/setup.sh" > setup.sh &&\ + + +######################## +######################## +FROM base AS dev + +RUN apt-get update && \ + apt-get clean && \ + apt-get --fix-missing -y install \ + cmake-curses-gui \ + gdb \ + nano && \ + rm -rf /var/lib/apt/lists/* + +######################## +######################## + + +######################## +FROM base AS build + +COPY cmake /tmp_source/cmake +COPY Python /tmp_source/Python +COPY Cpp /tmp_source/Cpp +#COPY Executables /tmp_source/cpp/Executables +COPY External /tmp_source/External +#COPY Library /tmp_source/cpp/Library +COPY Scarab /tmp_source/Scarab +COPY Testing /tmp_source/Testing +COPY CMakeLists.txt /tmp_source/CMakeLists.txt +COPY NymphConfig.cmake.in /tmp_source/NymphConfig.cmake.in +COPY pyproject.toml /tmp_source/pyproject.toml +COPY VERSION /tmp_source/VERSION +#COPY .git /tmp_source/.git + +# repeat the cmake command to get the change of install prefix to set correctly (a package_builder known issue) +RUN source $NYMPH_PREFIX/setup.sh &&\ + cd /tmp_source &&\ + mkdir build &&\ + cd build &&\ + cmake -D CMAKE_BUILD_TYPE=$NYMPH_BUILD_TYPE \ + -D CMAKE_INSTALL_PREFIX:PATH=$NYMPH_PREFIX \ + -D Nymph_ENABLE_TESTING:BOOL=$NYMPH_BUILD_TESTS_EXE .. &&\ + cmake -D CMAKE_BUILD_TYPE=$NYMPH_BUILD_TYPE \ + -D CMAKE_INSTALL_PREFIX:PATH=$NYMPH_PREFIX \ + -D Nymph_ENABLE_TESTING:BOOL=$NYMPH_BUILD_TESTS_EXE .. &&\ + make -j${narg} install &&\ + /bin/true + +######################## +FROM base + +COPY --from=build $NYMPH_PREFIX $NYMPH_PREFIX diff --git a/Examples/CppRepo/BUILD_ME.sh b/Examples/CppRepo/BUILD_ME.sh new file mode 100755 index 00000000..eae3109f --- /dev/null +++ b/Examples/CppRepo/BUILD_ME.sh @@ -0,0 +1,20 @@ +#! /bin/bash + +# This script builds the CppRepo example of a Nymph-based C++-only package. +# The CMake-based build downloads Nymph using CMake's FetchContent module, +# rather than including it as a submodule. Nymph is built using +# the Scarab/PackageBuilder framework included with Nymph. + +mkdir build +cd build + +cmake -DCMAKE_BUILD_TYPE=${Nymph_BUILD_TYPE:=Debug} \ + -DNymph_BUILD_NYMPH_EXE=${Nymph_BUILD_NYMPH_EXE:=FALSE} \ + -DNymph_ENABLE_EXECUTABLES=${Nymph_ENABLE_EXECUTABLES:=TRUE} \ + -DNymph_ENABLE_PYTHON=${Nymph_ENABLE_PYTHON:=FALSE} \ + -DNymph_ENABLE_TESTING=${Nymph_ENABLE_TESTING:=FALSE} \ + -DNymph_SINGLETHREADED=${Nymph_SINGLETHREADED:=FALSE} \ + -DNymph_TAG=${Nymph_TAG:=nymph2_2/develop} \ + .. + +make -j${NARG:=} install diff --git a/Examples/CppRepo/CMakeLists.txt b/Examples/CppRepo/CMakeLists.txt new file mode 100644 index 00000000..9e13dd5a --- /dev/null +++ b/Examples/CppRepo/CMakeLists.txt @@ -0,0 +1,91 @@ +# Minimum cmake verison 3.12 required for Scarab +cmake_minimum_required (VERSION 3.12) + +# Define the project +cmake_policy( SET CMP0048 NEW ) # version in project() +project( CppRepo VERSION 0.0.0 ) + +include( FetchContent ) +# Get Nymph with FetchContent +# If Nymph is a submodule, then set the SOURCE_DIR option to point to it; also in that case, remove any download specifications +message( STATUS "Getting Nymph" ) +option( Nymph_TAG "Version of Nymph to use" nymph2_2/develop ) +message( "$$$$$$ tag: ${Nymph_TAG}" ) +set( Nymph_DIR nymph_src ) +FetchContent_Populate( nymph + SOURCE_DIR ${Nymph_DIR} + GIT_REPOSITORY https://github.com/project8/nymph + GIT_TAG ${Nymph_TAG} + GIT_SHALLOW TRUE +) + +# If nymph were a submodule in ${PROJECT_SOURCE_DIR}/nymph, that would be used in place of ${Nymph_DIR} +list( APPEND CMAKE_MODULE_PATH + ${CMAKE_BINARY_DIR}/${Nymph_DIR}/Scarab/cmake + ${CMAKE_BINARY_DIR}/${Nymph_DIR}/cmake +) +include( PackageBuilder ) +include( Nymph ) + +######### +# flags # +######### + +set( CMAKE_CXX_STANDARD 17 ) + + +################ +# dependencies # +################ + +set( PUBLIC_EXT_LIBS ) +set( PRIVATE_EXT_LIBS ) + + + + +##################### +# prepare for build # +##################### + +pbuilder_prepare_project() + + +############## +# submodules # +############## + +# Nymph +pbuilder_add_submodule( Nymph ${CMAKE_BINARY_DIR}/${Nymph_DIR} ) + +pbuilder_use_sm_library( Nymph Nymph ) + +##################### +# build the project # +##################### + +# add include directories +include_directories( BEFORE + Library/Processors + Library/Data +) + +# build this project +add_subdirectory( Library ) + +if( tettigoniidae_ENABLE_EXECUTABLES ) + #add_subdirectory( Executables ) +endif() + +if( tettigoniidae_ENABLE_TESTING ) + #add_subdirectory( Testing ) +endif() + + +################## +# package config # +################## + +#configure_file( ${PROJECT_SOURCE_DIR}/tettigoniidaeConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/tettigoniidaeConfig.cmake @ONLY ) + +#pbuilder_do_package_config() \ No newline at end of file diff --git a/Examples/CppRepo/Library/CMakeLists.txt b/Examples/CppRepo/Library/CMakeLists.txt new file mode 100644 index 00000000..ea7dff20 --- /dev/null +++ b/Examples/CppRepo/Library/CMakeLists.txt @@ -0,0 +1,5 @@ +# CMakeLists for nymph/Examples/CppRepo/Library +# Author: N.S. Oblath + +add_subdirectory( Data ) +add_subdirectory( Processors ) diff --git a/Examples/CppRepo/Library/Data/CMakeLists.txt b/Examples/CppRepo/Library/Data/CMakeLists.txt new file mode 100644 index 00000000..c4f007f1 --- /dev/null +++ b/Examples/CppRepo/Library/Data/CMakeLists.txt @@ -0,0 +1,32 @@ +# CMakeLists for nymph/Examples/CppRepo/Library/Data +# Author: N.S. Oblath + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set( CppRepoData_HEADERFILES + DoubleData.hh + IntData.hh +) + +set( CppRepoData_SOURCEFILES + DoubleData.cc + IntData.cc +) + + +################################################## + +pbuilder_library( + TARGET CppRepoData + SOURCES ${CppRepoData_SOURCEFILES} + PUBLIC_EXTERNAL_LIBRARIES ${PUBLIC_EXT_LIBS} +) + +pbuilder_component_install_and_export( + COMPONENT Library + LIBTARGETS CppRepoData +) + +pbuilder_install_headers( ${CppRepoData_HEADERFILES} ) diff --git a/Examples/CppRepo/Library/Data/DoubleData.cc b/Examples/CppRepo/Library/Data/DoubleData.cc new file mode 100644 index 00000000..493eb787 --- /dev/null +++ b/Examples/CppRepo/Library/Data/DoubleData.cc @@ -0,0 +1,14 @@ + +#include "DoubleData.hh" + +namespace tettigoniidae +{ + DoubleData::DoubleData() : + Nymph::Data(), + fDValue1( 0. ), + fDValue2( 10. ) + {} + + DoubleData::~DoubleData() + {} +} diff --git a/Examples/CppRepo/Library/Data/DoubleData.hh b/Examples/CppRepo/Library/Data/DoubleData.hh new file mode 100644 index 00000000..ee88e021 --- /dev/null +++ b/Examples/CppRepo/Library/Data/DoubleData.hh @@ -0,0 +1,31 @@ +/* + * TestData2.hh + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#ifndef TTGD_TESTING_TESTDATA2 +#define TTGD_TESTING_TESTDATA2 + +#include "Data.hh" +#include "MemberVariable.hh" + +namespace tettigoniidae +{ + + class DoubleData : public Nymph::Data + { + public: + DoubleData(); + + virtual ~DoubleData(); + + MEMVAR( double, DValue1 ); + MEMVAR( double, DValue2 ); + }; + + +} /* namespace tettigoniidae */ + +#endif /* TTGD_TESTING_TESTDATA2 */ diff --git a/Examples/CppRepo/Library/Data/IntData.cc b/Examples/CppRepo/Library/Data/IntData.cc new file mode 100644 index 00000000..8fef364f --- /dev/null +++ b/Examples/CppRepo/Library/Data/IntData.cc @@ -0,0 +1,14 @@ + +#include "IntData.hh" + +namespace tettigoniidae +{ + IntData::IntData() : + Nymph::Data(), + fIValue1( 0 ), + fIValue2( 5 ) + {} + + IntData::~IntData() + {} +} diff --git a/Examples/CppRepo/Library/Data/IntData.hh b/Examples/CppRepo/Library/Data/IntData.hh new file mode 100644 index 00000000..3cbe8eef --- /dev/null +++ b/Examples/CppRepo/Library/Data/IntData.hh @@ -0,0 +1,35 @@ +/* + * IntData.hh + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#ifndef TTGD_DATA_INTDATA +#define TTGD_DATA_INTDATA + +#include "Data.hh" +#include "MemberVariable.hh" + +namespace tettigoniidae +{ + + /*! + @class IntData + @author N. S. Oblath + @brief Two integer values + */ + class IntData : public Nymph::Data + { + public: + IntData(); + + virtual ~IntData(); + + MEMVAR( int, IValue1 ); + MEMVAR( int, IValue2 ); + }; + +} /* namespace tettigoniidae */ + +#endif /* TTGD_DATA_INTDATA */ diff --git a/Examples/CppRepo/Library/Processors/Adder.cc b/Examples/CppRepo/Library/Processors/Adder.cc new file mode 100644 index 00000000..9fed0ca4 --- /dev/null +++ b/Examples/CppRepo/Library/Processors/Adder.cc @@ -0,0 +1,35 @@ + +#include "Adder.hh" + +#include "IntData.hh" + + +REGISTER_PROCESSOR( tettigoniidae, Adder, "adder" ) + +namespace tettigoniidae +{ + Adder::Adder( const std::string& name ) : + Processor( name ), + fIntValue( 0 ), + fIntSignal( "int", this ), + fAddIntSlot( "add-int", this, &Adder::Add, &fIntSignal ) + {} + + Adder::~Adder() + {} + + void Adder::Configure( const scarab::param_node& node ) + { + fIntValue = node.get_value( "int-value", fIntValue ); + + return; + } + + void Adder::Add( IntData& data ) + { + data.SetIValue1( data.GetIValue1() + fIntValue ); + data.SetIValue2( data.GetIValue2() + fIntValue ); + return; + } + +} /* namespace tettigoniidae */ diff --git a/Examples/CppRepo/Library/Processors/Adder.hh b/Examples/CppRepo/Library/Processors/Adder.hh new file mode 100644 index 00000000..93ab1d8f --- /dev/null +++ b/Examples/CppRepo/Library/Processors/Adder.hh @@ -0,0 +1,64 @@ +/*! + * @file Adder.hh + * + * Created on: Nov 25, 2021 + * Author: N.S. Oblath + */ + +#ifndef TTGD_PROCESSORS_ADDER +#define TTGD_PROCESSORS_ADDER + +#include "Processor.hh" +#include "SignalData.hh" +#include "SlotData.hh" + +namespace tettigoniidae +{ + class IntData; + + /*! + @class Adder + @author N. S. Oblath + + @brief Adds a value to data + + @procdetails + Does addition in place + + @proctype adder + + @config + @configparam{int-value,int} Value to add to IntData + + @slots + @slot{add-int,void (DataHandle)} Adds a value to IntData; Requires IntData; Adds no data; Emits signal "int" + + @signals + @signal{int,void (DataHandle)} Emitted after adding to IntData + */ + class Adder : public Nymph::Processor + { + public: + Adder( const std::string& name = "adder" ); + + virtual ~Adder(); + + /// Processor configuration + void Configure( const scarab::param_node& node ); + + MEMVAR( int, IntValue ); + + public: + /// Add an integer to integer data + void Add( IntData& data ); + + public: + Nymph::SignalData fIntSignal; + + Nymph::SlotData< Nymph::In, Nymph::Out<> > fAddIntSlot; + + }; + +} /* namespace tettigoniidae */ + +#endif /* TTGD_PROCESSORS_ADDER */ diff --git a/Examples/CppRepo/Library/Processors/CMakeLists.txt b/Examples/CppRepo/Library/Processors/CMakeLists.txt new file mode 100644 index 00000000..1bd998c1 --- /dev/null +++ b/Examples/CppRepo/Library/Processors/CMakeLists.txt @@ -0,0 +1,35 @@ +# CMakeLists for nymph/Examples/CppRepo/Library/Processors +# Author: N.S. Oblath + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set( CppRepo_LIBS + CppRepoData +) + +set( CppRepoProcs_HEADERFILES + Adder.hh +) + +set( CppRepoProcs_SOURCEFILES + Adder.cc +) + + +################################################## + +pbuilder_library( + TARGET CppRepoProcs + SOURCES ${CppRepoProcs_SOURCEFILES} + PUBLIC_EXTERNAL_LIBRARIES ${PUBLIC_EXT_LIBS} + PROJECT_LIBRARIES ${CppRepo_LIBS} +) + +pbuilder_component_install_and_export( + COMPONENT Library + LIBTARGETS CppRepoProcs +) + +pbuilder_install_headers( ${CppRepoProcs_HEADERFILES} ) diff --git a/Examples/PyRepo/BUILD_ME.sh b/Examples/PyRepo/BUILD_ME.sh new file mode 100755 index 00000000..6682c999 --- /dev/null +++ b/Examples/PyRepo/BUILD_ME.sh @@ -0,0 +1,35 @@ +#! /bin/bash + +# This script builds the PyRepo example of a Nymph-based Python-only package. +# We first have to build the C++ Nymph library, and then we can install the Python Nymph package. +# Finally, we install the PyRepo package. + +# This assumes that if the _nymph directory exists, then _nymph has been built and installed +if [[ ! -d _nymph ]]; then + git clone --recurse-submodules -b ${Nymph_TAG:=nymph2_2/develop} https://github.com/project8/nymph _nymph + cd _nymph + + mkdir build + cd build + + cmake -DCMAKE_BUILD_TYPE=${Nymph_BUILD_TYPE:=Debug} \ + -DNymph_BUILD_NYMPH_EXE=${Nymph_BUILD_NYMPH_EXE:=TRUE} \ + -DNymph_ENABLE_EXECUTABLES=${Nymph_ENABLE_EXECUTABLES:=TRUE} \ + -DNymph_ENABLE_PYTHON=${Nymph_ENABLE_PYTHON:=TRUE} \ + -DNymph_ENABLE_TESTING=${Nymph_ENABLE_TESTING:=FALSE} \ + -DNymph_SINGLETHREADED=${Nymph_SINGLETHREADED:=FALSE} \ + .. + + make -j${NARG:=} install + source bin/add_lib_python_path.sh + + cd .. + + pip install . + + cd .. +else + source _nymph/build/bin/add_lib_python_path.sh +fi + +pip install -e . diff --git a/Examples/PyRepo/pyproject.toml b/Examples/PyRepo/pyproject.toml new file mode 100644 index 00000000..1724f769 --- /dev/null +++ b/Examples/PyRepo/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = [ + "setuptools>=42", +] +build-backend = "setuptools.build_meta" + +[project] +name = "PyRepo" +#dependencies = [ +# "nymph_bindings @ file:///src/build-ny22py-debug-docker/lib", +#] +version = "1.0.0" + +[project.scripts] +hello-world = "pyrepo:hello_world" + +#[tool.setuptools.packages.find] +#where = ["Python"] diff --git a/Examples/PyRepo/pyrepo/__init__.py b/Examples/PyRepo/pyrepo/__init__.py new file mode 100644 index 00000000..ddd37b42 --- /dev/null +++ b/Examples/PyRepo/pyrepo/__init__.py @@ -0,0 +1,3 @@ +__all__ = [] + +from .hello_world import hello_world diff --git a/Examples/PyRepo/pyrepo/hello_world.py b/Examples/PyRepo/pyrepo/hello_world.py new file mode 100644 index 00000000..925976cc --- /dev/null +++ b/Examples/PyRepo/pyrepo/hello_world.py @@ -0,0 +1,6 @@ + +def hello_world(): + print("Hello, world") + +if __name__ == "__main__": + hello_world() diff --git a/Executables/Validation/KTTestCuts.hh b/Executables/Validation/KTTestCuts.hh deleted file mode 100644 index 4b8d9b00..00000000 --- a/Executables/Validation/KTTestCuts.hh +++ /dev/null @@ -1,71 +0,0 @@ -/* - * KTTestCuts.hh - * - * Created on: Nov 2, 2016 - * Author: obla999 - */ - -#ifndef NYMPH_KTTESTCUTS_HH_ -#define NYMPH_KTTESTCUTS_HH_ - -#include "KTCut.hh" -#include "KTData.hh" -#include "KTMemberVariable.hh" - - -namespace Nymph -{ - - class KTTestData : public KTExtensibleData< KTTestData > - { - public: - KTTestData(); - virtual ~KTTestData(); - - MEMBERVARIABLE(bool, IsAwesome); - - public: - static const std::string sName; - - }; - - // Cuts data that is NOT awesome - class KTAwesomeCut : public KTCutOneArg< KTTestData > - { - public: - struct Result : KTExtensibleCutResult< Result > - { - static const std::string sName; - }; - - public: - KTAwesomeCut(const std::string& name = "default-awesome-cut"); - virtual ~KTAwesomeCut(); - - bool Configure(const scarab::param_node& node); - - bool Apply(KTData& data, KTTestData& testData); - }; - - // Cuts data that is IS awesome - class KTNotAwesomeCut : public KTCutOneArg< KTTestData > - { - public: - struct Result : KTExtensibleCutResult< Result > - { - static const std::string sName; - }; - - public: - KTNotAwesomeCut(const std::string& name = "default-not-awesome-cut"); - virtual ~KTNotAwesomeCut(); - - bool Configure(const scarab::param_node& node); - - bool Apply(KTData& data, KTTestData& testData); - }; - -} - - -#endif /* NYMPH_KTTESTCUTS_HH_ */ diff --git a/Executables/Validation/KTTestProcessor.cc b/Executables/Validation/KTTestProcessor.cc deleted file mode 100644 index 8c07a290..00000000 --- a/Executables/Validation/KTTestProcessor.cc +++ /dev/null @@ -1,69 +0,0 @@ -/* - * KTTestProcessor.cc - * - * Created on: Aug 15, 2012 - * Author: nsoblath - */ - -#include "KTTestProcessor.hh" - -#include "KTLogger.hh" - -namespace Nymph -{ - KTLOGGER(testsiglog, "KTTestProcessor") - - KTTestProcessorA::KTTestProcessorA() : - fTheSignal() - { - RegisterSignal("the_signal", &fTheSignal); - } - - KTTestProcessorA::~KTTestProcessorA() - { - } - - bool KTTestProcessorA::Configure(const scarab::param_node&) - { - return true; - } - - void KTTestProcessorA::EmitSignals(int value) - { - fTheSignal(value); - return; - } - - - - - KTTestProcessorB::KTTestProcessorB() - { - RegisterSlot("first_slot", this, &KTTestProcessorB::Slot1); - RegisterSlot("second_slot", this, &KTTestProcessorB::Slot2); - } - - KTTestProcessorB::~KTTestProcessorB() - { - } - - bool KTTestProcessorB::Configure(const scarab::param_node&) - { - return true; - } - - void KTTestProcessorB::Slot1(int input) - { - KTINFO(testsiglog, "Slot1: input is " << input); - return; - } - - void KTTestProcessorB::Slot2(int input) - { - KTINFO(testsiglog, "Slot2: twice input is " << 2*input); - return; - } - - - -} /* namespace Nymph */ diff --git a/Executables/Validation/KTTestProcessor.hh b/Executables/Validation/KTTestProcessor.hh deleted file mode 100644 index 07f066ba..00000000 --- a/Executables/Validation/KTTestProcessor.hh +++ /dev/null @@ -1,48 +0,0 @@ -/* - * KTTestProcessor.hh - * - * Created on: Aug 15, 2012 - * Author: nsoblath - */ - -#ifndef KTTESTPROCESSOR_HH_ -#define KTTESTPROCESSOR_HH_ - -#include "KTProcessor.hh" - -namespace Nymph -{ - - class KTTestProcessorA : public KTProcessor - { - public: - typedef KTSignalConcept< void (int) >::signal TheSignal; - - public: - KTTestProcessorA(); - virtual ~KTTestProcessorA(); - - bool Configure(const scarab::param_node& node); - - void EmitSignals(int); - - //private: - TheSignal fTheSignal; - }; - - - class KTTestProcessorB : public KTProcessor - { - public: - KTTestProcessorB(); - virtual ~KTTestProcessorB(); - - bool Configure(const scarab::param_node& node); - - void Slot1(int); - void Slot2(int); - }; - - -} /* namespace Nymph */ -#endif /* KTTESTPROCESSOR_HH_ */ diff --git a/Executables/Validation/TestApplyCut.cc b/Executables/Validation/TestApplyCut.cc deleted file mode 100644 index e808aebc..00000000 --- a/Executables/Validation/TestApplyCut.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * TestApplyCut.cc - * - * Created on: Oct 07, 2014 - * Author: nsoblath - */ - -#include "KTTestCuts.hh" - -#include "KTApplyCut.hh" -#include "KTLogger.hh" - -KTLOGGER(testlog, "TestApplyCut"); - -using namespace Nymph; -using namespace std; - -int main() -{ - KTDataPtr dataPtr(new KTData()); - KTTestData& testData = dataPtr->Of< KTTestData >(); - - KTCutStatus& cutStatus = dataPtr->GetCutStatus(); - KTINFO(testlog, "Initial cut state: " << cutStatus.IsCut()); - - KTApplyCut applyCut; - - KTINFO(testlog, "Applying awesome cut"); - applyCut.SetCut(new KTAwesomeCut()); - applyCut.ApplyCut(dataPtr); - - KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); - KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); - KTINFO(testlog, "Is cut (with mask \"0\")? " << cutStatus.IsCut("0")); - - KTINFO(testlog, "Applying not-awesome cut"); - applyCut.SelectCut("not-awesome-cut"); - applyCut.ApplyCut(dataPtr); - - KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); - KTINFO(testlog, "Has cut result \"not-awesome-cut\"? " << cutStatus.HasCutResult("not-awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTNotAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"not-awesome-cut\" is: " << cutStatus.GetCutState("not-awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTNotAwesomeCut::Result >()); - KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); - KTINFO(testlog, "Is cut with mask \"00\"? " << cutStatus.IsCut("00")); - KTINFO(testlog, "Is cut with mask \"01\"? " << cutStatus.IsCut("01")); - KTINFO(testlog, "Is cut with mask \"10\"? " << cutStatus.IsCut("10")); - KTINFO(testlog, "Is cut with mask \"11\"? " << cutStatus.IsCut("11")); - KTINFO(testlog, "Is cut with mask 0? " << cutStatus.IsCut(0)); - KTINFO(testlog, "Is cut with mask 1? " << cutStatus.IsCut(1)); - KTINFO(testlog, "Is cut with mask 2? " << cutStatus.IsCut(2)); - KTINFO(testlog, "Is cut with mask 3? " << cutStatus.IsCut(3)); - - return 0; -} diff --git a/Executables/Validation/TestCut.cc b/Executables/Validation/TestCut.cc deleted file mode 100644 index a5077282..00000000 --- a/Executables/Validation/TestCut.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* - * TestCut.cc - * - * Created on: Sep 29, 2014 - * Author: nsoblath - */ - -#include "KTTestCuts.hh" - -#include "KTLogger.hh" - -KTLOGGER(testlog, "TestCut"); - -using namespace Nymph; -using namespace std; - -int main() -{ - KTData data; - KTTestData& testData = data.Of< KTTestData >(); - - KTCutStatus& cutStatus = data.GetCutStatus(); - KTINFO(testlog, "Initial cut state: " << cutStatus.IsCut()); - - KTINFO(testlog, "Applying awesome cut"); - KTAwesomeCut cut; - cut.Apply(data, testData); - - KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); - KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); - KTINFO(testlog, "Is cut (with mask \"0\")? " << cutStatus.IsCut("0")); - - KTINFO(testlog, "Applying not-awesome cut"); - KTNotAwesomeCut naCut; - naCut.Apply(data, testData); - - KTINFO(testlog, "Cuts present: " << cutStatus.CutResultsPresent()) - KTINFO(testlog, "Has cut result \"awesome-cut\"? " << cutStatus.HasCutResult("awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"awesome-cut\" is: " << cutStatus.GetCutState("awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTAwesomeCut::Result >()); - KTINFO(testlog, "Has cut result \"not-awesome-cut\"? " << cutStatus.HasCutResult("not-awesome-cut")); - KTINFO(testlog, "Has cut result ? " << cutStatus.HasCutResult< KTNotAwesomeCut::Result >()); - KTINFO(testlog, "Cut state of \"not-awesome-cut\" is: " << cutStatus.GetCutState("not-awesome-cut")); - KTINFO(testlog, "Cut state of is: " << cutStatus.GetCutState< KTNotAwesomeCut::Result >()); - KTINFO(testlog, "Is cut (all results)? " << cutStatus.IsCut()); - KTINFO(testlog, "Is cut with mask \"00\"? " << cutStatus.IsCut("00")); - KTINFO(testlog, "Is cut with mask \"01\"? " << cutStatus.IsCut("01")); - KTINFO(testlog, "Is cut with mask \"10\"? " << cutStatus.IsCut("10")); - KTINFO(testlog, "Is cut with mask \"11\"? " << cutStatus.IsCut("11")); - KTINFO(testlog, "Is cut with mask 0? " << cutStatus.IsCut(0)); - KTINFO(testlog, "Is cut with mask 1? " << cutStatus.IsCut(1)); - KTINFO(testlog, "Is cut with mask 2? " << cutStatus.IsCut(2)); - KTINFO(testlog, "Is cut with mask 3? " << cutStatus.IsCut(3)); - - return 0; -} diff --git a/Executables/Validation/TestSignalsAndSlots.cc b/Executables/Validation/TestSignalsAndSlots.cc deleted file mode 100644 index f81b6bcd..00000000 --- a/Executables/Validation/TestSignalsAndSlots.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* - * TestSignalsAndSlots.cc - * - * Created on: Aug 15, 2012 - * Author: nsoblath - */ - - -#include "KTTestProcessor.hh" -#include "KTLogger.hh" - -using namespace Nymph; - -KTLOGGER(testsiglog, "TestSignalsAndSlots") - -int main() -{ - - KTTestProcessorA tpA; - KTTestProcessorB tpB; - - /* Basic Test - KTSignalWrapper* signalPtr = tpA.GetSignal("the_signal"); - KTSlotWrapper* slot1Ptr = tpB.GetSlot("first_slot"); - - slot1Ptr->SetConnection(signalPtr); - - tpA.EmitSignals(5); - - slot1Ptr->Disconnect(); - - return 0; - */ - - /* More Complicated Test */ - KTINFO(testsiglog, "Connecting the_signal to first_slot and second_slot"); - try - { - tpA.ConnectASlot("the_signal", &tpB, "first_slot", 20); - tpB.ConnectASignal(&tpA, "the_signal", "second_slot", 10); - } - catch(std::exception& e) - { - KTERROR(testsiglog, "A problem occurred while connecting the signal and slots:\n" << e.what()); - return -1; - } - - KTINFO(testsiglog, "Emitting signals"); - KTINFO(testsiglog, "First test signal: 5"); - tpA.EmitSignals(5); - KTINFO(testsiglog, "Second test signal: 18"); - tpA.EmitSignals(18); - - KTINFO(testsiglog, "Tests complete"); - return 0; - /**/ -} - diff --git a/External/cereal b/External/cereal new file mode 160000 index 00000000..51cbda5f --- /dev/null +++ b/External/cereal @@ -0,0 +1 @@ +Subproject commit 51cbda5f30e56c801c07fe3d3aba5d7fb9e6cca4 diff --git a/Library/Application/KTPrintDataStructure.cc b/Library/Application/KTPrintDataStructure.cc deleted file mode 100644 index 339efb24..00000000 --- a/Library/Application/KTPrintDataStructure.cc +++ /dev/null @@ -1,111 +0,0 @@ -/* - * KTPrintDataStructure.cc - * - * Created on: Sept 12, 2014 - * Author: N.S. Oblath - */ - -#include "KTPrintDataStructure.hh" - -#include "KTLogger.hh" - -#include - -using boost::shared_ptr; - -namespace Nymph -{ - KTLOGGER(datalog, "KTPrintDataStructure"); - - // Register the processor - KT_REGISTER_PROCESSOR(KTPrintDataStructure, "print-data-structure"); - - KTPrintDataStructure::KTPrintDataStructure(const std::string& name) : - KTProcessor(name), - fDataSignal("data", this), - fDataStructSlot("print-data", this, &KTPrintDataStructure::PrintDataStructure), - fCutStructSlot("print-cuts", this, &KTPrintDataStructure::PrintCutStructure), - fDataAndCutStructSlot("print-data-and-cuts", this, &KTPrintDataStructure::PrintDataAndCutStructure) - { - } - - KTPrintDataStructure::~KTPrintDataStructure() - { - } - - bool KTPrintDataStructure::Configure(const scarab::param_node&) - { - return true; - } - - void KTPrintDataStructure::PrintDataStructure(KTDataPtr dataPtr) - { - DoPrintDataStructure(dataPtr); - - fDataSignal(dataPtr); - - return; - } - - void KTPrintDataStructure::PrintCutStructure(KTDataPtr dataPtr) - { - DoPrintCutStructure(dataPtr); - - fDataSignal(dataPtr); - - return; - } - - - void KTPrintDataStructure::PrintDataAndCutStructure(KTDataPtr dataPtr) - { - DoPrintDataStructure(dataPtr); - DoPrintCutStructure(dataPtr); - - fDataSignal(dataPtr); - - return; - } - - void KTPrintDataStructure::DoPrintDataStructure(KTDataPtr dataPtr) - { - std::stringstream printbuf; - - printbuf << "\nData Structure:\n"; - printbuf << "\t- " << dataPtr->Name() << '\n'; - KTDEBUG(datalog, "Found data type " << dataPtr->Name()); - KTExtensibleStructCore< KTDataCore >* nextData = dataPtr->Next(); - while (nextData != NULL) - { - printbuf << "\t- " << nextData->Name() << '\n'; - KTDEBUG(datalog, "Found data type " << nextData->Name()); - nextData = nextData->Next(); - } - - KTINFO(datalog, printbuf.str()); - - return; - } - - void KTPrintDataStructure::DoPrintCutStructure(KTDataPtr dataPtr) - { - std::stringstream printbuf; - - KTCutStatus& cutStatus = dataPtr->GetCutStatus(); - printbuf << "\n" << cutStatus; - - const KTCutResult* cutResult = cutStatus.CutResults(); - printbuf << "Cut Structure:\n"; - while (cutResult != NULL) - { - printbuf << "\t- " << cutResult->Name() << " -- is cut: " << cutResult->GetState() << '\n'; - KTDEBUG(datalog, "Found cut type " << cutResult->Name()); - cutResult = cutResult->Next(); - } - - KTINFO(datalog, printbuf.str()); - - return; - } - -} /* namespace Nymph */ diff --git a/Library/Application/KTProcessorToolbox.cc b/Library/Application/KTProcessorToolbox.cc deleted file mode 100644 index ec0eac64..00000000 --- a/Library/Application/KTProcessorToolbox.cc +++ /dev/null @@ -1,534 +0,0 @@ -/* - * KTProcessorToolbox.cc - * - * Created on: Sep 27, 2012 - * Author: nsoblath - */ - -#include "KTProcessorToolbox.hh" - -#include "KTLogger.hh" -#include "KTPrimaryProcessor.hh" - -#include "factory.hh" -#include "param_codec.hh" - -#include - -#ifndef SINGLETHREADED -#include -#endif - -using std::deque; -using std::set; -using std::string; -using std::vector; - -namespace Nymph -{ - KTLOGGER(proclog, "KTProcessorToolbox"); - - KTProcessorToolbox::KTProcessorToolbox(const std::string& name) : - KTConfigurable(name), - fRunQueue(), - fProcMap() - { - } - - KTProcessorToolbox::~KTProcessorToolbox() - { - ClearProcessors(); - } - - bool KTProcessorToolbox::Configure(const scarab::param_node& node) - { - auto tProcFactory = scarab::factory< KTProcessor, const std::string& >::get_instance(); - - KTPROG(proclog, "Configuring . . ."); - // Deal with "processor" blocks first - if (! node.has("processors")) - { - KTWARN(proclog, "No processors were specified"); - } - else - { - const scarab::param_array& procArray = node["processors"].as_array(); - for( scarab::param_array::const_iterator procIt = procArray.begin(); procIt != procArray.end(); ++procIt ) - { - if( ! procIt->is_node() ) - { - KTERROR( proclog, "Invalid processor entry: not a node" ); - return false; - } - const scarab::param_node& procNode = procIt->as_node(); - - if (! procNode.has("type")) - { - KTERROR(proclog, "Unable to create processor: no processor type given"); - return false; - } - string procType = procNode["type"]().as_string(); - - string procName; - if (! procNode.has("name")) - { - KTINFO(proclog, "No name given for processor of type <" << procType << ">; using type as name."); - procName = procType; - } - else - { - procName = procNode["name"]().as_string(); - } - KTProcessor* newProc = tProcFactory->create(procType, procType); - if (newProc == NULL) - { - KTERROR(proclog, "Unable to create processor of type <" << procType << ">"); - return false; - } - - if (! AddProcessor(procName, newProc)) - { - KTERROR(proclog, "Unable to add processor <" << procName << ">"); - delete newProc; - return false; - } - } - } - - - // Then deal with connections" - if (! node.has("connections")) - { - KTWARN(proclog, "No connections were specified"); - } - else - { - const scarab::param_array& connArray = node["connections"].as_array(); - for( scarab::param_array::const_iterator connIt = connArray.begin(); connIt != connArray.end(); ++connIt ) - { - if( ! connIt->is_node() ) - { - KTERROR( proclog, "Invalid connection entry: not a node" ); - return false; - } - const scarab::param_node& connNode = connIt->as_node(); - - if ( ! connNode.has("signal") || ! connNode.has("slot") ) - { - KTERROR(proclog, "Signal/Slot connection information is incomplete!"); - if (connNode.has("signal")) - { - KTWARN(proclog, "signal = " << connNode["signal"]()); - } - else - { - KTERROR(proclog, "signal = MISSING"); - } - - if (connNode.has("slot")) - { - KTWARN(proclog, "slot = " << connNode["slot"]()); - } - else - { - KTERROR(proclog, "slot = MISSING"); - } - return false; - } - - bool connReturn = false; - if (connNode.has("order")) - { - connReturn = MakeConnection(connNode["signal"]().as_string(), connNode["slot"]().as_string(), connNode["order"]().as_int()); - } - else - { - connReturn = MakeConnection(connNode["signal"]().as_string(), connNode["slot"]().as_string()); - } - if (! connReturn) - { - KTERROR(proclog, "Unable to make connection <" << connNode["signal"]().as_string() << "> --> <" << connNode["slot"]().as_string() << ">"); - return false; - } - - KTINFO(proclog, "Signal <" << connNode["signal"]().as_string() << "> connected to slot <" << connNode["slot"]().as_string() << ">"); - } - } - - - // Finally, deal with processor-run specifications - // The run queue is an array of processor names, or groups of names, which will be run sequentially. - // If names are grouped (in another array), those in that group will be run in parallel. - // In single threaded mode all threads will be run sequentially in the order they were specified. - if (! node.has("run-queue") || ! node["run-queue"].is_array()) - { - KTWARN(proclog, "Run queue was not specified"); - } - else - { - const scarab::param_array& rqArray = node["run-queue"].as_array(); - for (scarab::param_array::const_iterator rqIt = rqArray.begin(); rqIt != rqArray.end(); ++rqIt) - { - if (rqIt->is_value()) - { - if (! PushBackToRunQueue((*rqIt)().as_string())) - { - KTERROR(proclog, "Unable to process run-queue entry: could not add processor to the queue"); - return false; - } - } - else if (rqIt->is_array()) - { - const scarab::param_array& rqNode = rqIt->as_array(); - std::vector< std::string > names; - - for (scarab::param_array::const_iterator rqArrayIt = rqNode.begin(); rqArrayIt != rqNode.end(); ++rqArrayIt) - { - if (! rqArrayIt->is_value()) - { - KTERROR(proclog, "Invalid run-queue array entry: not a value"); - return false; - } - names.push_back((*rqArrayIt)().as_string()); - } - - if (! PushBackToRunQueue(names)) - { - KTERROR(proclog, "Unable to process run-queue entry: could not add list of processors to the queue"); - return false; - } - } - else - { - KTERROR(proclog, "Invalid run-queue entry: not a value or array"); - return false; - } - } - } - - return true; - } - - bool KTProcessorToolbox::ConfigureProcessors(const scarab::param_node& node) - { - for (ProcMapIt iter = fProcMap.begin(); iter != fProcMap.end(); iter++) - { - KTDEBUG(proclog, "Attempting to configure processor <" << iter->first << ">"); - string procName = iter->first; - string nameUsed; - if (node.has(procName)) - { - nameUsed = procName; - } - else - { - string configName = iter->second.fProc->GetConfigName(); - KTWARN(proclog, "Did not find a parameter node <" << procName << ">\n" - "\tWill check using the generic name of the processor, <" << configName << ">."); - if (node.has(configName)) - { - nameUsed = configName; - KTWARN(proclog, "Configuring processor <" << procName << "> with configuration found using the generic name of the processor: <" << configName << ">"); - } - else - { - KTWARN(proclog, "Did not find configuration information for processor <" << procName << "> (type <" << configName << ">)\n" - "\tProcessor <" << procName << "> was not configured."); - continue; - } - } - - const scarab::param_node& subNode = node[nameUsed].as_node(); - - if (! iter->second.fProc->Configure(subNode)) - { - KTERROR(proclog, "An error occurred while configuring processor <" << iter->first << "> with parameter node <" << nameUsed << ">"); - return false; - } - } - return true; - } - - bool KTProcessorToolbox::ConfigureProcessors(const std::string& config) - { - scarab::param_translator translator; - scarab::param_node optNode; - optNode.add( "encoding", new scarab::param_value( "json" ) ); - return ConfigureProcessors( translator.read_string( config, optNode )->as_node() ); - } - - bool KTProcessorToolbox::Run() - { - KTPROG(proclog, "Beginning processing . . ."); -#ifndef SINGLETHREADED - unsigned iGroup = 0; -#endif - for (RunQueue::const_iterator rqIter = fRunQueue.begin(); rqIter != fRunQueue.end(); ++rqIter) - { -#ifdef SINGLETHREADED - for (ThreadGroup::const_iterator tgIter = rqIter->begin(); tgIter != rqIter->end(); ++tgIter) - { - if (! tgIter->fProc->Run()) - { - return false; - } - } -#else - KTDEBUG(proclog, "Starting thread group " << iGroup); - boost::thread_group parallelThreads; - unsigned iThread = 0; - for (ThreadGroup::const_iterator tgIter = rqIter->begin(); tgIter != rqIter->end(); ++tgIter) - { - // create a boost::thread object to launch the thread - // use boost::ref to avoid copying the processor - KTDEBUG(proclog, "Starting thread " << iThread << ": " << tgIter->fName); - parallelThreads.create_thread(boost::ref(*(tgIter->fProc))); - //parallelThreads.create_thread(boost::ref(**tgIter)); - iThread++; - } - // wait for execution to complete - parallelThreads.join_all(); - iGroup++; -#endif - } - KTPROG(proclog, ". . . processing complete."); - return true; - } - - - KTProcessor* KTProcessorToolbox::GetProcessor(const std::string& procName) - { - ProcMapIt it = fProcMap.find(procName); - if (it == fProcMap.end()) - { - KTWARN(proclog, "Processor <" << procName << "> was not found."); - return NULL; - } - return it->second.fProc; - } - - const KTProcessor* KTProcessorToolbox::GetProcessor(const std::string& procName) const - { - ProcMapCIt it = fProcMap.find(procName); - if (it == fProcMap.end()) - { - KTWARN(proclog, "Processor <" << procName << "> was not found."); - return NULL; - } - return it->second.fProc; - } - - bool KTProcessorToolbox::AddProcessor(const std::string& procName, KTProcessor* proc) - { - ProcMapIt it = fProcMap.find(procName); - if (it == fProcMap.end()) - { - ProcessorInfo pInfo; - pInfo.fProc = proc; - fProcMap.insert(ProcMapValue(procName, pInfo)); - KTDEBUG(proclog, "Added processor <" << procName << "> (a.k.a. " << proc->GetConfigName() << ")"); - return true; - } - KTWARN(proclog, "Processor <" << procName << "> already exists; new processor was not added."); - return false; - } - - bool KTProcessorToolbox::AddProcessor(const std::string& procType, const std::string& procName) - { - auto tProcFactory = scarab::factory< KTProcessor, const std::string& >::get_instance(); - - ProcMapIt it = fProcMap.find(procName); - if (it == fProcMap.end()) - { - KTProcessor* newProc = tProcFactory->create(procType, procType); - if (newProc == NULL) - { - KTERROR(proclog, "Unable to create processor of type <" << procType << ">"); - return false; - } - if (! AddProcessor(procName, newProc)) - { - KTERROR(proclog, "Unable to add processor <" << procName << ">"); - delete newProc; - return false; - } - return true; - } - KTWARN(proclog, "Processor <" << procName << "> already exists; new processor was not added."); - return false; - } - - bool KTProcessorToolbox::RemoveProcessor(const std::string& procName) - { - KTProcessor* procToRemove = ReleaseProcessor(procName); - if (procToRemove == NULL) - { - return false; - } - delete procToRemove; - KTDEBUG(proclog, "Processor <" << procName << "> deleted."); - return true; - } - - KTProcessor* KTProcessorToolbox::ReleaseProcessor(const std::string& procName) - { - ProcMapIt it = fProcMap.find(procName); - if (it == fProcMap.end()) - { - KTWARN(proclog, "Processor <" << procName << "> was not found."); - return NULL; - } - KTProcessor* procToRelease = it->second.fProc; - fProcMap.erase(it); - return procToRelease; - } - - void KTProcessorToolbox::ClearProcessors() - { - for (ProcMapIt it = fProcMap.begin(); it != fProcMap.end(); it++) - { - delete it->second.fProc; - } - fProcMap.clear(); - fRunQueue.clear(); - return; - } - - - bool KTProcessorToolbox::MakeConnection(const std::string& signal, const std::string& slot, int order) - { - string signalProcName, signalName; - if (! ParseSignalSlotName(signal, signalProcName, signalName)) - { - KTERROR(proclog, "Unable to parse signal name: <" << signal << ">"); - return false; - } - - string slotProcName, slotName; - if (! ParseSignalSlotName(slot, slotProcName, slotName)) - { - KTERROR(proclog, "Unable to parse slot name: <" << slot << ">"); - return false; - } - - return MakeConnection(signalProcName, signalName, slotProcName, slotName, order); - } - - bool KTProcessorToolbox::MakeConnection(const std::string& signalProcName, const std::string& signalName, const std::string& slotProcName, const std::string& slotName, int order) - { - KTProcessor* signalProc = GetProcessor(signalProcName); - if (signalProc == NULL) - { - KTERROR(proclog, "Processor named <" << signalProcName << "> was not found!"); - return false; - } - - KTProcessor* slotProc = GetProcessor(slotProcName); - if (slotProc == NULL) - { - KTERROR(proclog, "Processor named <" << slotProcName << "> was not found!"); - return false; - } - - try - { - if (order != std::numeric_limits< int >::min()) - { - signalProc->ConnectASlot(signalName, slotProc, slotName, order); - } - else - { - signalProc->ConnectASlot(signalName, slotProc, slotName); - } - } - catch (std::exception& e) - { - KTERROR(proclog, "An error occurred while connecting signals and slots:\n" - << "\tSignal " << signalName << " from processor " << signalProcName << " (a.k.a. " << signalProc->GetConfigName() << ")" << '\n' - << "\tSlot " << slotName << " from processor " << slotProcName << " (a.k.a. " << slotProc->GetConfigName() << ")" << '\n' - << '\t' << e.what()); - return false; - } - - return true; - } - - bool KTProcessorToolbox::ParseSignalSlotName(const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot) const - { - size_t sepPos = toParse.find_first_of(fSigSlotNameSep); - if (sepPos == string::npos) - { - KTERROR(proclog, "Unable to find separator between processor and signal/slot name in <" << toParse << ">"); - return false; - } - nameOfProc = toParse.substr(0, sepPos); - nameOfSigSlot = toParse.substr(sepPos + 1); - return true; - } - - - bool KTProcessorToolbox::PushBackToRunQueue(const std::string& name) - { - ThreadGroup threadGroup; - - if (! AddProcessorToThreadGroup( name, threadGroup)) - { - KTERROR(proclog, "Unable to add processor <" << name << "> to thread group"); - return false; - } - - fRunQueue.push_back(threadGroup); - - KTINFO(proclog, "Added processor <" << name << "> to the run queue"); - return true; - } - - bool KTProcessorToolbox::PushBackToRunQueue(std::initializer_list< std::string > names) - { - return PushBackToRunQueue(std::vector< std::string >(names)); - } - - bool KTProcessorToolbox::PushBackToRunQueue(std::vector< std::string > names) - { - ThreadGroup threadGroup; - - std::stringstream toPrint; - for (const std::string& name : names) - { - if (! AddProcessorToThreadGroup(name, threadGroup)) - { - KTERROR(proclog, "Unable to add processor <" << name << "> to thread group"); - return false; - } - toPrint << name << ", "; // the extra comma at the end is removed below - } - - fRunQueue.push_back(threadGroup); - std::string toPrintString = toPrint.str(); - toPrintString.resize(toPrintString.size()-2); - KTINFO(proclog, "Added processors <" << toPrintString << "> to the run queue"); - return true; - } - - bool KTProcessorToolbox::AddProcessorToThreadGroup(const std::string& name, ThreadGroup& group) - { - KTProcessor* procForRunQueue = GetProcessor(name); - KTDEBUG(proclog, "Attempting to add processor <" << name << "> to the run queue"); - if (procForRunQueue == NULL) - { - KTERROR(proclog, "Unable to find processor <" << name << "> requested for the run queue"); - return false; - } - - KTPrimaryProcessor* primaryProc = dynamic_cast< KTPrimaryProcessor* >(procForRunQueue); - if (primaryProc == NULL) - { - KTERROR(proclog, "Processor <" << name << "> is not a primary processor."); - return false; - } - //group.insert(primaryProc); - group.insert(Thread(primaryProc, name)); - return true; - } - -} /* namespace Nymph */ diff --git a/Library/Application/KTProcessorToolboxPy.hh b/Library/Application/KTProcessorToolboxPy.hh deleted file mode 100644 index 19404d51..00000000 --- a/Library/Application/KTProcessorToolboxPy.hh +++ /dev/null @@ -1,67 +0,0 @@ -/** - @file KTProcessorToolboxPy.hh - @brief Contains KTProcessorToolbox python bindings - @details Manages processors requested by the user at run time. - @author: B. H. LaRoque - @date: April 20, 2017 - */ - -#ifndef KTPROCESSORTOOLBOXPY_HH_ -#define KTPROCESSORTOOLBOXPY_HH_ - -#include "KTProcessorToolbox.hh" - -// Make connection overloads - /* default values here? */ -bool (Nymph::KTProcessorToolbox::*MakeConnection_3args)(const std::string&, const std::string&, int order) = &Nymph::KTProcessorToolbox::MakeConnection; -bool (Nymph::KTProcessorToolbox::*MakeConnection_4args)(const std::string&, const std::string&, const std::string&, const std::string&, int order) = &Nymph::KTProcessorToolbox::MakeConnection; - -// Run queue pushback overloads -bool (Nymph::KTProcessorToolbox::*PushBackToRunQueue_string)(const std::string& name) = &Nymph::KTProcessorToolbox::PushBackToRunQueue; -bool (Nymph::KTProcessorToolbox::*PushBackToRunQueue_init_list)(std::initializer_list< std::string >) = &Nymph::KTProcessorToolbox::PushBackToRunQueue; -bool (Nymph::KTProcessorToolbox::*PushBackToRunQueue_vector)(std::vector< std::string >) = &Nymph::KTProcessorToolbox::PushBackToRunQueue; - -// Add Processor overloads -bool (Nymph::KTProcessorToolbox::*AddProcessor_Ref)(const std::string&, Nymph::KTProcessor*) = &Nymph::KTProcessorToolbox::AddProcessor; -bool (Nymph::KTProcessorToolbox::*AddProcessor_TypeStr)(const std::string&, const std::string&) = &Nymph::KTProcessorToolbox::AddProcessor; - -// Get Processor overloads -Nymph::KTProcessor* (Nymph::KTProcessorToolbox::*GetProcessor_wrap)(const std::string&) = &Nymph::KTProcessorToolbox::GetProcessor; - -// Configure Processor overloads -bool (Nymph::KTProcessorToolbox::*ConfigureProcessors_JsonStr)(const std::string&) = &Nymph::KTProcessorToolbox::ConfigureProcessors; - -void export_ProcessorToolbox() -{ - using namespace Nymph; - using namespace boost::python; - class_("KTProcessorToolbox", init()) - .def("Run", &KTProcessorToolbox::Run, "Call Run() on all processors in the run queue") - - .def("GetProcessor", GetProcessor_wrap, return_value_policy(), "Get a pointer to a processor in the toolbox") - .def("ConfigureProcessors", ConfigureProcessors_JsonStr, "Configure processors from a json dictionary. Top-level keys are processor names, values are dictionaries with their configurations") - .def("AddProcessor", AddProcessor_Ref, "add a processor to the toolbox, toolbox takes ownership") - .def("AddProcessor", AddProcessor_TypeStr, "add a processor to the toolbox, toolbox takes ownership") - .def("RemoveProcessor", &KTProcessorToolbox::RemoveProcessor, "remove a processor from the toolbox") - - //TODO: Not 100% certain that the reference count for this processor is now correct, given the return_value_policy - .def("ReleaseProcessor", &KTProcessorToolbox::ReleaseProcessor, return_value_policy(), "Remove a processor from the toolbox and return it to the user, ownership is passed") - - .def("ClearProcessors", &KTProcessorToolbox::ClearProcessors, "Remove all processors and clear run queue") - - // make signal-slot connection - .def("MakeConnection", MakeConnection_3args, "Make a signal-slot connection") - .def("MakeConnection", MakeConnection_4args) - - // Push new processor(s) to back of run queue - .def("PushBackToRunQueue", PushBackToRunQueue_string, "Push processor(s) to the back of the run queue") - .def("PushBackToRunQueue", PushBackToRunQueue_init_list) - .def("PushBackToRunQueue", PushBackToRunQueue_vector) - - // Remove items from run queue - .def("PopBackOfRunQueue", &KTProcessorToolbox::PopBackOfRunQueue, "Remove the last item in the run queue, whether it's a single processor or a group of processors") - .def("ClearRunQueue", &KTProcessorToolbox::ClearRunQueue, "Clear the run queue") - ; -} - -#endif /* KTPROCESSORTOOLBOXPY_HH_ */ diff --git a/Library/CMakeLists.txt b/Library/CMakeLists.txt deleted file mode 100644 index c3341e6b..00000000 --- a/Library/CMakeLists.txt +++ /dev/null @@ -1,115 +0,0 @@ -# CMakeLists for Nymph/Library -# Author: N. Oblath - -#include( PythonPackage ) - -set( UTIL_DIR Utility ) -set( DATA_DIR Data ) -set( PROC_DIR Processor ) -set( IO_DIR IO ) -set( APPL_DIR Application ) - -include_directories( BEFORE - ${CMAKE_CURRENT_SOURCE_DIR}/${UTIL_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/${DATA_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/${PROC_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/${IO_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/${APPL_DIR} -) - -set( NYMPH_HEADERFILES - ${UTIL_DIR}/KTCacheDirectory.hh - ${UTIL_DIR}/KTConcurrentQueue.hh - ${UTIL_DIR}/KTConfigurable.hh - ${UTIL_DIR}/KTDirectory.hh - ${UTIL_DIR}/KTEventLoop.hh - ${UTIL_DIR}/KTException.hh - ${UTIL_DIR}/KTExtensibleStruct.hh - ${UTIL_DIR}/KTExtensibleStructFactory.hh - ${UTIL_DIR}/KTLogger.hh - ${UTIL_DIR}/KTMemberVariable.hh - ${UTIL_DIR}/KTTIFactory.hh - ${UTIL_DIR}/KTTime.hh - ${DATA_DIR}/KTApplyCut.hh - ${DATA_DIR}/KTCut.hh - ${DATA_DIR}/KTCutFilter.hh - ${DATA_DIR}/KTCutResult.hh - ${DATA_DIR}/KTCutStatus.hh - ${DATA_DIR}/KTData.hh - ${PROC_DIR}/KTConnection.hh - ${PROC_DIR}/KTPrimaryProcessor.hh - ${PROC_DIR}/KTProcessor.hh - ${PROC_DIR}/KTSignal.hh - ${PROC_DIR}/KTSignalWrapper.hh - ${PROC_DIR}/KTSlot.hh - ${PROC_DIR}/KTSlotWrapper.hh - ${IO_DIR}/KTReader.hh - ${IO_DIR}/KTWriter.hh - ${APPL_DIR}/KTApplication.hh - ${APPL_DIR}/KTCommandLineHandler.hh - ${APPL_DIR}/KTCommandLineOption.hh - ${APPL_DIR}/KTConfigurator.hh - ${APPL_DIR}/KTDataQueueProcessor.hh - ${APPL_DIR}/KTFilenameParsers.hh - ${APPL_DIR}/KTPrintDataStructure.hh - ${APPL_DIR}/KTProcessorToolbox.hh - ${APPL_DIR}/KTRunNymph.hh - ${APPL_DIR}/KTThroughputProfiler.hh -) - -set( NYMPH_SOURCEFILES - ${UTIL_DIR}/KTCacheDirectory.cc - ${UTIL_DIR}/KTConfigurable.cc - ${UTIL_DIR}/KTDirectory.cc - ${UTIL_DIR}/KTEventLoop.cc - ${UTIL_DIR}/KTException.cc - ${UTIL_DIR}/KTLogger.cc - ${UTIL_DIR}/KTTime.cc - ${DATA_DIR}/KTApplyCut.cc - ${DATA_DIR}/KTCut.cc - ${DATA_DIR}/KTCutFilter.cc - ${DATA_DIR}/KTCutStatus.cc - ${DATA_DIR}/KTData.cc - ${PROC_DIR}/KTPrimaryProcessor.cc - ${PROC_DIR}/KTProcessor.cc - ${PROC_DIR}/KTSignal.cc - ${PROC_DIR}/KTSignalWrapper.cc - ${PROC_DIR}/KTSlotWrapper.cc - ${IO_DIR}/KTReader.cc - ${IO_DIR}/KTWriter.cc - ${APPL_DIR}/KTApplication.cc - ${APPL_DIR}/KTCommandLineHandler.cc - ${APPL_DIR}/KTConfigurator.cc - ${APPL_DIR}/KTDataQueueProcessor.cc - ${APPL_DIR}/KTFilenameParsers.cc - ${APPL_DIR}/KTPrintDataStructure.cc - ${APPL_DIR}/KTProcessorToolbox.cc - ${APPL_DIR}/KTRunNymph.cc - ${APPL_DIR}/KTThroughputProfiler.cc -) - -#set( NYMPH_PY_HEADERFILES -# ${DATA_DIR}/KTDataPy.hh -# ${PROC_DIR}/KTProcessorPy.hh -# ${APPL_DIR}/KTProcessorToolboxPy.hh -#) - -################################################## - -pbuilder_library( - TARGET Nymph - SOURCES ${NYMPH_SOURCEFILES} - PUBLIC_EXTERNAL_LIBRARIES ${PUBLIC_EXT_LIBS} -) - -pbuilder_component_install_and_export( - COMPONENT Library - LIBTARGETS Nymph -) - -pbuilder_install_headers( ${NYMPH_HEADERFILES} ) - - -#if (Nymph_ENABLE_PYTHON) -# python_package_add_module(NymphPy.cc) -#endif (Nymph_ENABLE_PYTHON) diff --git a/Library/Data/KTCut.hh b/Library/Data/KTCut.hh deleted file mode 100644 index 6b26dc96..00000000 --- a/Library/Data/KTCut.hh +++ /dev/null @@ -1,247 +0,0 @@ -/* - * KTCut.hh - * - * Created on: Sept 19, 2014 - * Author: nsoblath - */ - -#ifndef KTCUT_HH_ -#define KTCUT_HH_ - -#include "KTConfigurable.hh" -#include "KTCutResult.hh" -#include "KTData.hh" -#include "KTExtensibleStructFactory.hh" -#include "KTLogger.hh" -#include "KTMemberVariable.hh" - -#include "factory.hh" -#include "typename.hh" - -namespace Nymph -{ - KTLOGGER(cutlog_h, "KTCut.h"); - - /*! - @class KTCut - @author N. S. Oblath - - @brief Base class for a cut that gets applied to data objects. - - @details - A fully implemented cut MUST have the following: - - Public nested class called Result, inheriting from KTExtensibleCutResult< Result >, and containing a public static std::string name sName. - - Cut registration using the macro KT_REGISTER_CUT([class name]) - - Implementation of bool Configure(const scarab::param_node&) - - Implementation of bool Apply(KTData&, ) - - Your cut class should inherit from KTCutOneArg or KTCutTwoArgs, depending on the number of data types involved in your cut. - - The existence of [class name]::Result and [class name]::Result::sName are enforces at compile time by the KT_REGISTER_CUT macro. - - The functions bool Configure(const scarab::param_node&) and void Apply(KTData&, ) are abstract in the base classes, and therefore must be implemented. - - Boolean return value interpretation: - - TRUE means the cut was failed - - FALSE means the cut was passed - - -------------------------------------- - ------- Example Cut Definition ------- - -------------------------------------- - - class KTSomeData; - - // Data must be at least as awesome as fAwesomenessThreshold to pass this cut - class KTAwesomenessCut : public KTCutOneArg< KTSomeData > - { - public: - struct Result : KTExtensibleCutResult< Result > - { - static const std::string sName; - }; - - public: - KTAwesomenessCut(const std::string& name = "default-example-cut"); - virtual ~KTAwesomenessCut(); - - bool Configure(const scarab::param_node* node); - - MEMBERVARIABLE(double, AwesomenessThreshold); - - public: - bool Apply(KTData& data, KTSomeData& data); - - }; - - -------------------------------------- - ------- Example Implementation ------- - -------------------------------------- - - const std::string KTExampleCut::Result::sName = "awesomeness-cut"; - - KT_REGISTER_CUT(KTExampleCut); - - KTAwesomenessCut::KTAwesomenessCut(const std::string& name) : - KTCutOneArg(name), - fAwesomenessThreshold(1000000.) - {} - - KTAwesomenessCut::~KTExampleCut() - {} - - bool KTAwesomenessCut::Configure(const scarab::param_node* node) - { - if (node == NULL) return true; - SetAwesomenessThreshold(node->get_value("awesomeness", GetAwesomenessThreshold())); - return true; - } - - bool KTAwesomenessCut::Apply(KTData& data, KTSomeData& someData) - { - bool isCut = someData.Awesomeness() < fAwesomenessThreshold; - data.GetCutStatus().AddCutResult< KTAwesomenessCut::Result >(isCut); - return isCut; - } - - */ - - //************************************ - // KTCut -- base class for all cuts - //************************************ - - class KTCut : public KTConfigurable - { - public: - KTCut(const std::string& name = "default-cut-name"); - virtual ~KTCut(); - - virtual bool Apply(KTDataPtr) = 0; - }; - - - //***************************************************************** - // KTCutOneArg -- base class for cuts operating on one data type - //***************************************************************** - - template< class XDataType > - class KTCutOneArg : public KTCut - { - public: - KTCutOneArg(const std::string& name = "default-cut-name"); - virtual ~KTCutOneArg(); - - virtual bool Apply(KTData& data, XDataType& dataType) = 0; - - virtual bool Apply(KTDataPtr dataPtr); - }; - - - //******************************************************************* - // KTCutTwoArgs -- base class for cuts operating on two data types - //******************************************************************* - - template< class XDataType1, class XDataType2 > - class KTCutTwoArgs : public KTCut - { - public: - KTCutTwoArgs(const std::string& name = "default-cut-name"); - virtual ~KTCutTwoArgs(); - - virtual bool Apply(KTData& data, XDataType1& dataType1, XDataType2& dataType2) = 0; - - virtual bool Apply(KTDataPtr dataPtr); - }; - - - //******************* - // Implementations - //******************* - - template< class XDataType > - KTCutOneArg< XDataType >::KTCutOneArg(const std::string& name) : - KTCut(name) - { - } - - template< class XDataType > - KTCutOneArg< XDataType >::~KTCutOneArg() - {} - - template< class XDataType > - bool KTCutOneArg< XDataType >::Apply(KTDataPtr dataPtr) - { - if (! dataPtr->Has< XDataType >()) - { - KTERROR(cutlog_h, "Data type <" << scarab::type(XDataType()) << "> was not present"); - return false; - } - return Apply(dataPtr->Of< KTData >(), dataPtr->Of< XDataType >()); - } - - - template< class XDataType1, class XDataType2 > - KTCutTwoArgs< XDataType1, XDataType2 >::KTCutTwoArgs(const std::string& name) : - KTCut(name) - { - } - - template< class XDataType1, class XDataType2 > - KTCutTwoArgs< XDataType1, XDataType2 >::~KTCutTwoArgs() - {} - - template< class XDataType1, class XDataType2 > - bool KTCutTwoArgs< XDataType1, XDataType2 >::Apply(KTDataPtr dataPtr) - { - if (! dataPtr->Has< XDataType1 >()) - { - KTERROR(cutlog_h, "Data type <" << scarab::type(XDataType1()) << "> was not present"); - return false; - } - if (! dataPtr->Has< XDataType2 >()) - { - KTERROR(cutlog_h, "Data type <" << scarab::type(XDataType2()) << "> was not present"); - return false; - } - return Apply(dataPtr->Of< KTData >(), dataPtr->Of< XDataType1 >(), dataPtr->Of< XDataType2 >()); - } - -/* Playing around: wouldn't it be cool if this could be done with variadic tmeplates? - * Unfortunately we'll need to be able to iterate over the types in the template pack in the Apply(KTDataPtr) function. - * - template< class ... DataTypes > - class KTCutOnData : public KTCut - { - public: - KTCutOnData(const std::string& name = "default-cut-name"); - virtual ~KTCutOnData(); - - virtual bool Apply(DataTypes ...) = 0; - - virtual bool Apply(KTDataPtr dataPtr); - }; - - template< class ... DataTypes > - KTCutOnData< DataTypes... >::KTCutOnData(const std::string& name) : - KTCut(name) - { - } - - template< class ... DataTypes > - KTCutOnData< DataTypes... >::~KTCutOnData() - {} - - template< class ... DataTypes > - bool KTCutOnData< DataTypes... >::Apply(KTDataPtr dataPtr) - { - - } -*/ - - // this macro enforces the existence of cut_class::Result and cut_class::Result::sName at compile time -#define KT_REGISTER_CUT(cut_class) \ - static ::scarab::registrar< ::Nymph::KTCut, cut_class, const std::string& > sCut##cut_class##Registrar( cut_class::Result::sName ); \ - static ::Nymph::KTExtensibleStructRegistrar< ::Nymph::KTCutResultCore, cut_class::Result > sCut##cut_class##ResultRegistrar( cut_class::Result::sName ); - -} /* namespace Nymph */ - -#endif /* KTCUT_HH_ */ diff --git a/Library/Data/KTCutResult.hh b/Library/Data/KTCutResult.hh deleted file mode 100644 index e89f5f30..00000000 --- a/Library/Data/KTCutResult.hh +++ /dev/null @@ -1,52 +0,0 @@ -/* - * KTCutResult.hh - * - * Created on: Sept 19, 2014 - * Author: nsoblath - */ - -#ifndef KTCUTRESULT_HH_ -#define KTCUTRESULT_HH_ - -#include "KTExtensibleStruct.hh" - -#include "KTMemberVariable.hh" - -#include - -namespace Nymph -{ - class KTCutResultCore - { - public: - KTCutResultCore() : - fState(false) - {} - virtual ~KTCutResultCore() {} - - virtual const std::string& Name() const = 0; - - MEMBERVARIABLE_PROTECTED(bool, State); - }; - - typedef KTExtensibleStructCore< KTCutResultCore > KTCutResult; - - template< class XDerivedType > - class KTExtensibleCutResult : public KTExtensibleStruct< XDerivedType, KTCutResultCore > - { - public: - KTExtensibleCutResult() {} - virtual ~KTExtensibleCutResult() {} - - const std::string& Name() const; - }; - - template< class XDerivedType > - inline const std::string& KTExtensibleCutResult< XDerivedType >::Name() const - { - return XDerivedType::sName; - } - -} /* namespace Nymph */ - -#endif /* KTCUTRESULT_HH_ */ diff --git a/Library/Data/KTCutStatus.cc b/Library/Data/KTCutStatus.cc deleted file mode 100644 index 788eed7e..00000000 --- a/Library/Data/KTCutStatus.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* - * KTCut.cc - * - * Created on: Aug 24, 2012 - * Author: nsoblath - */ - -#include "KTCutStatus.hh" - -#include "KTExtensibleStructFactory.hh" -#include "KTLogger.hh" - -namespace Nymph -{ - KTLOGGER(cutlog, "KTCut"); - - KTCutStatus::KTCutStatus() : - fCutResults(new KTCutResultHandle()), - fSummary() - { - } - - KTCutStatus::KTCutStatus(const KTCutStatus& orig) : - fCutResults(dynamic_cast< KTCutResultHandle* >(orig.fCutResults->Clone())), - fSummary() - { - UpdateStatus(); - } - - KTCutStatus::~KTCutStatus() - {} - - KTCutStatus& KTCutStatus::operator=(const KTCutStatus& rhs) - { - fCutResults.reset(dynamic_cast< KTCutResultHandle* >(rhs.fCutResults->Clone())); - UpdateStatus(); - return *this; - } - - void KTCutStatus::UpdateStatus() - { - KTDEBUG(cutlog, "Updating cut summary"); - KTCutResult* cut = fCutResults.get()->Next(); // skip over KTCutResultHandle - if (cut == NULL) - { - KTDEBUG(cutlog, "No cuts"); - fSummary.resize(1, false); - return; - } - - // loop through once to count cuts - unsigned nCuts = 0; - while (cut != NULL) - { - ++nCuts; - cut = cut->Next(); - } - KTDEBUG(cutlog, nCuts << " cuts"); - fSummary.resize(nCuts, false); - // loop through again to set cuts - cut = fCutResults.get()->Next(); // skip over KTCutResultHandle - for (unsigned iCut = 0; iCut < nCuts; ++iCut) - { - fSummary[iCut] = cut->GetState(); - cut = cut->Next(); - } - KTDEBUG(cutlog, "Cut summary bitset: " << fSummary); - return; - } - - bool KTCutStatus::AddCutResult(const std::string& cutName, bool state, bool doUpdateStatus) - { - if (! HasCutResult(cutName)) - { - KTExtensibleStructFactory< KTCutResultCore >* factory = KTExtensibleStructFactory< KTCutResultCore >::get_instance(); - KTCutResult* newCut = factory->Create(cutName, fCutResults.get()); - if (newCut == NULL) - { - KTERROR(cutlog, "Could not create cut of type <" << cutName << ">"); - return false; - } - newCut->SetState(state); - - if (doUpdateStatus) UpdateStatus(); - return true; - } - return false; - - } - - bool KTCutStatus::HasCutResult(const std::string& cutName) const - { - if (GetCutResult(cutName) == NULL) return false; - return true; - - } - - bool KTCutStatus::GetCutState(const std::string& cutName) const - { - const KTCutResult* cut = GetCutResult(cutName); - if (cut == NULL) return false; - return cut->GetState(); - } - - const KTCutResult* KTCutStatus::GetCutResult(const std::string& cutName) const - { - const KTCutResult* cut = fCutResults.get()->Next(); // skip over KTCutResultHandle - while (cut != NULL) - { - if (cut->Name() == cutName) return cut; - cut = cut->Next(); - } - return NULL; - } - - KTCutResult* KTCutStatus::GetCutResult(const std::string& cutName) - { - KTCutResult* cut = fCutResults.get()->Next(); // skip over KTCutResultHandle - while (cut != NULL) - { - if (cut->Name() == cutName) return cut; - cut = cut->Next(); - } - return NULL; - } - - bool KTCutStatus::SetCutState(const std::string& cutName, bool state, bool doUpdateStatus) - { - KTCutResult* cut = GetCutResult(cutName); - if (cut == NULL) - { - KTWARN(cutlog, "Cut <" << cutName << "> not found"); - return false; - } - cut->SetState(state); - - if (doUpdateStatus) UpdateStatus(); - return true; - } - - /* - void KTCutStatus::RemoveCutResult(const std::string& cutName, bool doUpdateStatus) - { - KTCutResult* cut = fCutResults.get(); // don't skip over KTCutResultHandle - KTCutResult* nextCut = cut->Next(); - while (nextCut != NULL) - { - if (nextCut->Name() == cutName) - { - // problem: can't pass nextCut->Next() to cut->Next() - if (doUpdateStatus) UpdateStatus(); - } - } - return; - } - */ - - std::string KTCutStatus::CutResultsPresent() const - { - KTCutResult* cut = fCutResults.get()->Next(); // skip over KTCutResultHandle - if (cut == NULL ) return ""; - - std::string cutsPresent; - while (true) - { - cutsPresent = cut->Name() + cutsPresent; - cut = cut->Next(); - if (cut != NULL) cutsPresent = " " + cutsPresent; - else break; - } - return cutsPresent; - } - - // private class KTCutStatus::KTCutResultHandle - // purposefully not registered with the cut factory - KTCutStatus::KTCutResultHandle::KTCutResultHandle() : - KTExtensibleCutResult< KTCutStatus::KTCutResultHandle >() - { - fState = false; - } - KTCutStatus::KTCutResultHandle::~KTCutResultHandle() - {} - - const std::string KTCutStatus::KTCutResultHandle::sName("top"); - - - std::ostream& operator<<(std::ostream& out, const KTCutStatus& status) - { - out << "Cut summary: " << status.fSummary << '\n'; - return out; - } - - -} /* namespace Nymph */ diff --git a/Library/Data/KTCutStatus.hh b/Library/Data/KTCutStatus.hh deleted file mode 100644 index 24d31bbf..00000000 --- a/Library/Data/KTCutStatus.hh +++ /dev/null @@ -1,255 +0,0 @@ -/* - * KTCutStatus.hh - * - * Created on: Sept 19, 2014 - * Author: nsoblath - */ - -#ifndef KTCUTSTATUS_HH_ -#define KTCUTSTATUS_HH_ - - -#include "KTCutResult.hh" - -#include -#include - -#include - -namespace Nymph -{ - /*! - @class KTCutStatus - @author N. S. Oblath - - @brief Provides easy access to cut information. - - @details - KTCutStatus is typically used as a member variable of KTData, the top-level data object. - - KTCutStatus owns the set of cut results that have been added to a data object. - It also owns a summary of those cuts (implemented with boost::dynamic_bitset). - - You can check if the data has been cut with the IsCut functions. - - IsCut() returns true if any cut results are true; - - IsCut(const bitset_type& mask), IsCut(unsigned int mask), and IsCut(const std::string& mask) allow you to specify - a cut mask, and return true if any of the cut results specified by the mask are true. - - When specifying a cut mask, bits set to true specify cuts that should be used: - - bitset_type is boost::dynamic_bitset; - - unsigned integer masks use the bits of the integer; - - std::string masks are strings with each character either a 0 or 1. - - With KTCutStatus you can interact with individual cut results in the following ways: - - Add cut results to a data object with AddCutResult, - - Check to see if a particular cut result is present using HasCutResult, - - Get the value of a cut result with GetCutState, - - Set the value of a cut result with SetCutState, - - Directly access the cut result with GetCutResult, and - - Remove a cut result with RemoveCutResult. - - For all except KTCutStatus::RemoveCutResult, the cut result can be identified by type or string name. - */ - - class KTCutStatus - { - public: - typedef boost::dynamic_bitset< > bitset_type; - - private: - // private class KTCutStatus::KTCutResultHandle - // purposefully not registered with the cut factory - class KTCutResultHandle : public KTExtensibleCutResult< KTCutResultHandle > - { - public: - KTCutResultHandle(); - ~KTCutResultHandle(); - - static const std::string sName; - }; - - public: - KTCutStatus(); - KTCutStatus(const KTCutStatus& orig); - ~KTCutStatus(); - - KTCutStatus& operator=(const KTCutStatus& rhs); - - const KTCutResult* CutResults() const; - - void UpdateStatus(); - - template< typename XCutType > - bool AddCutResult(bool state, bool doUpdateStatus=true); - bool AddCutResult(const std::string& cutName, bool state, bool doUpdateStatus=true); - // overload for const char* to avoid specializing the templated function below - bool AddCutResult(const char* cutName, bool state, bool doUpdateStatus=true); - template< typename XCutType > - bool AddCutResult(const XCutType& cut, bool doUpdateStatus=true); - - template< typename XCutType > - bool HasCutResult() const; - bool HasCutResult(const std::string& cutName) const; - - template< typename XCutType > - bool GetCutState() const; - bool GetCutState(const std::string& cutName) const; - - template< typename XCutType > - const KTCutResult* GetCutResult() const; - const KTCutResult* GetCutResult(const std::string& cutName) const; - - template< typename XCutType > - KTCutResult* GetCutResult(); - KTCutResult* GetCutResult(const std::string& cutName); - - template< typename XCutType > - bool SetCutState(bool state, bool doUpdateStatus=true); - bool SetCutState(const std::string& cutName, bool state, bool doUpdateStatus=true); - - template< typename XCutType > - void RemoveCutResult(bool doUpdateStatus=true); - // cannot currently update by cut name - //void RemoveCutResult(const std::string& cutName, bool doUpdateStatus=true); - - /// Returns a string with the names of the cuts that are present in bitset order - std::string CutResultsPresent() const; - - size_t size() const; - private: - friend std::ostream& operator<<(std::ostream& out, const KTCutStatus& status); - - boost::scoped_ptr< KTCutResultHandle > fCutResults; - - bitset_type fSummary; - - public: - bool IsCut() const; - bool IsCut(const bitset_type& mask) const; - bool IsCut(unsigned long long mask) const; - bool IsCut(const std::string& mask) const; - - bitset_type ToBitset(unsigned long long mask) const; - bitset_type ToBitset(const std::string& mask) const; - - }; - - std::ostream& operator<<(std::ostream& out, const KTCutStatus& status); - - - inline const KTCutResult* KTCutStatus::CutResults() const - { - return fCutResults.get()->Next(); - } - - template< typename XCutType > - bool KTCutStatus::AddCutResult(bool state, bool doUpdateStatus) - { - if (! HasCutResult< XCutType >()) - { - fCutResults.get()->Of< XCutType >().SetState(state); - if (doUpdateStatus) UpdateStatus(); - return true; - } - return false; - } - - inline bool KTCutStatus::AddCutResult(const char* cutName, bool state, bool doUpdateStatus) - { - return AddCutResult(std::string(cutName), state, doUpdateStatus); - } - - template< typename XCutType > - bool KTCutStatus::AddCutResult(const XCutType& cut, bool doUpdateStatus) - { - if (! HasCutResult< XCutType >()) - { - fCutResults.get()->Of< XCutType >() = cut; - if (doUpdateStatus) UpdateStatus(); - return true; - } - return false; - } - - template< typename XCutType > - inline bool KTCutStatus::HasCutResult() const - { - return fCutResults.get()->Has< XCutType >(); - } - - template< typename XCutType > - bool KTCutStatus::GetCutState() const - { - if (HasCutResult< XCutType >()) - { - return fCutResults.get()->Of< XCutType >().GetState(); - } - return false; - } - - template< typename XCutType > - const KTCutResult* KTCutStatus::GetCutResult() const - { - if (HasCutResult< XCutType >()) - { - return &(fCutResults.get()->Of< XCutType >()); - } - return NULL; - } - - template< typename XCutType > - KTCutResult* KTCutStatus::GetCutResult() - { - if (HasCutResult< XCutType >()) - { - return &(fCutResults.get()->Of< XCutType >()); - } - return NULL; - } - - template< typename XCutType > - inline void KTCutStatus::RemoveCutResult(bool doUpdateStatus) - { - delete fCutResults.get()->Detatch< XCutType >(); - if (doUpdateStatus) UpdateStatus(); - return; - } - - inline size_t KTCutStatus::size() const - { - return fSummary.size(); - } - - inline bool KTCutStatus::IsCut() const - { - return fSummary.any(); - } - - inline bool KTCutStatus::IsCut(const bitset_type& mask) const - { - return (fSummary & mask).any(); - } - - inline bool KTCutStatus::IsCut(unsigned long long mask) const - { - return IsCut(ToBitset(mask)); - } - - inline bool KTCutStatus::IsCut(const std::string& mask) const - { - return IsCut(ToBitset(mask)); - } - - inline KTCutStatus::bitset_type KTCutStatus::ToBitset(unsigned long long mask) const - { - return bitset_type(fSummary.size(), mask); - } - - inline KTCutStatus::bitset_type KTCutStatus::ToBitset(const std::string& mask) const - { - return bitset_type(mask); - } - -} /* namespace Nymph */ - -#endif /* KTCUTSTATUS_HH_ */ diff --git a/Library/Data/KTData.cc b/Library/Data/KTData.cc deleted file mode 100644 index 4dfd0791..00000000 --- a/Library/Data/KTData.cc +++ /dev/null @@ -1,32 +0,0 @@ -/* - * KTData.cc - * - * Created on: Aug 24, 2012 - * Author: nsoblath - */ - -#include "KTData.hh" - -namespace Nymph -{ - const std::string KTData::sName("data"); - - KTData::KTData() : - KTExtensibleData< KTData >(), - fCounter(0), - fLastData(false), - fCutStatus() - { - } - - KTData::KTData(const KTData& orig) : - KTExtensibleData< KTData >(orig), - fCounter(orig.fCounter), - fLastData(orig.fLastData), - fCutStatus(orig.fCutStatus) - {} - - KTData::~KTData() - {} - -} /* namespace Nymph */ diff --git a/Library/Data/KTData.hh b/Library/Data/KTData.hh deleted file mode 100644 index 850dc7fe..00000000 --- a/Library/Data/KTData.hh +++ /dev/null @@ -1,70 +0,0 @@ -/* - * KTData.hh - * - * Created on: Aug 24, 2012 - * Author: nsoblath - */ - -#ifndef KTDATA_HH_ -#define KTDATA_HH_ - -#include "KTExtensibleStruct.hh" - -#include "KTCutStatus.hh" -#include "KTMemberVariable.hh" - -#include - -#include - -namespace Nymph -{ - class KTDataCore - { - public: - KTDataCore() {} - virtual ~KTDataCore() {} - - virtual const std::string& Name() const = 0; - - }; - - template< class XDerivedType > - class KTExtensibleData : public KTExtensibleStruct< XDerivedType, KTDataCore > - { - public: - KTExtensibleData() {} - virtual ~KTExtensibleData() {} - - const std::string& Name() const; - - }; - - template< class XDerivedType > - inline const std::string& KTExtensibleData< XDerivedType >::Name() const - { - return XDerivedType::sName; - } - - - - class KTData : public KTExtensibleData< KTData > - { - public: - KTData(); - KTData(const KTData& orig); - ~KTData(); - - MEMBERVARIABLE(unsigned, Counter); - MEMBERVARIABLE(bool, LastData); - - MEMBERVARIABLEREF_NOSET(KTCutStatus, CutStatus); - - public: - static const std::string sName; - }; - - typedef boost::shared_ptr< KTData > KTDataPtr; - -} /* namespace Nymph */ -#endif /* KTDATA_HH_ */ diff --git a/Library/Data/KTDataPy.hh b/Library/Data/KTDataPy.hh deleted file mode 100644 index 2e0ae069..00000000 --- a/Library/Data/KTDataPy.hh +++ /dev/null @@ -1,23 +0,0 @@ -/* - * KTDataPy.hh - * - * Created on: April 10, 2017 - * Author: laroque - */ - - #ifndef KTDATAPY_HH_ - #define KTDATAPY_HH_ - -#include "KTData.hh" - -void export_DataPy() -{ - using namespace Nymph; - using namespace boost::python; - class_("KTData", init<>()) - .add_property("Counter", &KTData::GetCounter, &KTData::SetCounter) - .add_property("LastData", &KTData::GetLastData, &KTData::SetLastData) - ; -} - -#endif /* KTDATAPY_HH_ */ diff --git a/Library/NymphPy.cc b/Library/NymphPy.cc deleted file mode 100644 index 9fa346da..00000000 --- a/Library/NymphPy.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include - -/* include components here*/ -#include "KTDataPy.hh" -#include "KTProcessorPy.hh" -#include "KTProcessorToolboxPy.hh" - -BOOST_PYTHON_MODULE(NymphPy) -{ - using namespace boost::python; - export_DataPy(); - export_Processor(); - export_ProcessorToolbox(); -} diff --git a/Library/Processor/KTPrimaryProcessor.cc b/Library/Processor/KTPrimaryProcessor.cc deleted file mode 100644 index 6cb5749e..00000000 --- a/Library/Processor/KTPrimaryProcessor.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * KTPrimaryProcessor.cc - * - * Created on: Oct 10, 2012 - * Author: nsoblath - */ - -#include "KTPrimaryProcessor.hh" - -#include "KTLogger.hh" - -namespace Nymph -{ - KTLOGGER(proclog, "KTPrimaryProcessor"); - - KTPrimaryProcessor::KTPrimaryProcessor(const std::string& name) : - KTProcessor(name) - { - } - - KTPrimaryProcessor::~KTPrimaryProcessor() - { - } - - void KTPrimaryProcessor::operator ()() - { - if (! Run()) - { - KTERROR(proclog, "An error occurred during processor running."); - } - return; - } - -} /* namespace Nymph */ diff --git a/Library/Processor/KTPrimaryProcessor.hh b/Library/Processor/KTPrimaryProcessor.hh deleted file mode 100644 index f9120c87..00000000 --- a/Library/Processor/KTPrimaryProcessor.hh +++ /dev/null @@ -1,35 +0,0 @@ -/* - * KTPrimaryProcessor.hh - * - * Created on: Oct 10, 2012 - * Author: nsoblath - */ - -#ifndef KTPRIMARYPROCESSOR_HH_ -#define KTPRIMARYPROCESSOR_HH_ - -#include "KTProcessor.hh" - -#include "KTLogger.hh" - -namespace Nymph -{ - - class KTPrimaryProcessor : public KTProcessor - { - public: - KTPrimaryProcessor(const std::string& name = "default-primary-processor-name"); - virtual ~KTPrimaryProcessor(); - - public: - /// Callable function used by boost::thread - virtual void operator()(); - - public: - /// Starts the main action of the processor - virtual bool Run() = 0; - - }; - -} /* namespace Nymph */ -#endif /* KTPRIMARYPROCESSOR_HH_ */ diff --git a/Library/Processor/KTProcessor.cc b/Library/Processor/KTProcessor.cc deleted file mode 100644 index 72a717d6..00000000 --- a/Library/Processor/KTProcessor.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * KTProcessor.cc - * - * Created on: Jan 5, 2012 - * Author: nsoblath - */ - -#include "KTProcessor.hh" - -//#include "KTLogger.hh" - -#include - -#include - -using std::string; - -namespace Nymph -{ - //KTLOGGER(proclog, "KTProcessor"); - - ProcessorException::ProcessorException (std::string const& why) - : std::logic_error(why) - {} - - - KTProcessor::KTProcessor(const string& name) : - KTConfigurable(name), - fSignalMap(), - fSlotMap() - { - } - - KTProcessor::~KTProcessor() - { - for (SlotMapIt iter = fSlotMap.begin(); iter != fSlotMap.end(); iter++) - { - iter->second->Disconnect(); - delete iter->second; - } - for (SigMapIt iter = fSignalMap.begin(); iter != fSignalMap.end(); iter++) - { - delete iter->second; - } - } - - void KTProcessor::ConnectASlot(const std::string& signalName, KTProcessor* processor, const std::string& slotName, int groupNum) - { - KTSignalWrapper* signal = GetSignal(signalName); - KTSlotWrapper* slot = processor->GetSlot(slotName); - - try - { - ConnectSignalToSlot(signal, slot, groupNum); - } - catch (std::exception& e) - { - string errorMsg = string("Exception caught in KTProcessor::ConnectASignal; signal: ") + - signalName + string(", slot: ") + slotName + string("\n") + e.what() + string("\n") + - string("\tIf the signal wrapper cannot be cast correctly, check that the signatures of the signal and slot match exactly.\n") + - string("\tIf the signal pointer is NULL, you may have the signal name wrong.\n") + - string("\tIf the slot pointer is NULL, you may have the slot name wrong."); - throw std::logic_error(errorMsg); - } - KTDEBUG(processorlog, "Connected signal <" << signalName << "> to slot <" << slotName << ">"); - - return; - } - - void KTProcessor::ConnectASignal(KTProcessor* processor, const std::string& signalName, const std::string& slotName, int groupNum) - { - KTSignalWrapper* signal = processor->GetSignal(signalName); - KTSlotWrapper* slot = GetSlot(slotName); - - try - { - ConnectSignalToSlot(signal, slot, groupNum); - } - catch (std::exception& e) - { - string errorMsg = string("Exception caught in KTProcessor::ConnectASignal; signal: ") + - signalName + string(", slot: ") + slotName + string("\n") + e.what() + string("\n") + - string("Check that the signatures of the signal and slot match exactly."); - throw std::logic_error(errorMsg); - } - KTDEBUG(processorlog, "Connected slot <" << signalName << "> to signal <" << slotName << ">"); - - return; - } - - void KTProcessor::ConnectSignalToSlot(KTSignalWrapper* signal, KTSlotWrapper* slot, int groupNum) - { - if (signal == NULL) - { - throw ProcessorException("Signal pointer was NULL"); - } - if (slot == NULL) - { - throw ProcessorException("Slot pointer was NULL"); - } - - slot->SetConnection(signal, groupNum); - - return; - } - - KTSignalWrapper* KTProcessor::GetSignal(const std::string& name) - { - SigMapIt iter = fSignalMap.find(name); - if (iter == fSignalMap.end()) - { - return NULL; - } - return iter->second; - } - - KTSlotWrapper* KTProcessor::GetSlot(const std::string& name) - { - SlotMapIt iter = fSlotMap.find(name); - if (iter == fSlotMap.end()) - { - return NULL; - } - return iter->second; - } - - - -} /* namespace Nymph */ diff --git a/Library/Processor/KTProcessor.hh b/Library/Processor/KTProcessor.hh deleted file mode 100644 index 7aa8e6c2..00000000 --- a/Library/Processor/KTProcessor.hh +++ /dev/null @@ -1,142 +0,0 @@ -/** - @file KTProcessor.hh - @brief Contains KTProcessor - @details KTProcessor is the processor base class - @author: N. S. Oblath - @date: Jan 5, 2012 - */ - -#ifndef KTPROCESSOR_HH_ -#define KTPROCESSOR_HH_ - -#include "KTConfigurable.hh" - -#include "KTConnection.hh" -#include "KTLogger.hh" -#include "KTSignalWrapper.hh" -#include "KTSlotWrapper.hh" - -#include "factory.hh" - -#include -#include -#include - -#include -#include -#include -#include - -namespace Nymph -{ - KTLOGGER(processorlog, "KTProcessor.hh"); - - class ProcessorException : public std::logic_error - { - public: - ProcessorException(std::string const& why); - }; - - class KTProcessor : public KTConfigurable - { - protected: - typedef std::map< std::string, KTSignalWrapper* > SignalMap; - typedef SignalMap::iterator SigMapIt; - typedef SignalMap::value_type SigMapVal; - - typedef std::map< std::string, KTSlotWrapper* > SlotMap; - typedef SlotMap::iterator SlotMapIt; - typedef SlotMap::value_type SlotMapVal; - - public: - KTProcessor(const std::string& name="default-proc-name"); - virtual ~KTProcessor(); - - public: - - void ConnectASlot(const std::string& signalName, KTProcessor* processor, const std::string& slotName, int groupNum=-1); - void ConnectASignal(KTProcessor* processor, const std::string& signalName, const std::string& slotName, int groupNum=-1); - void ConnectSignalToSlot(KTSignalWrapper* signal, KTSlotWrapper* slot, int groupNum=-1); - - template< class XProcessor > - void RegisterSignal(std::string name, XProcessor* signalPtr); - - template< class XTarget, typename XReturn > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)()); - - template< class XTarget, typename XReturn, typename XArg1 > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1)); - - template< class XTarget, typename XReturn, typename XArg1, typename XArg2 > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1, XArg2)); - - KTSignalWrapper* GetSignal(const std::string& name); - - KTSlotWrapper* GetSlot(const std::string& name); - - protected: - - SignalMap fSignalMap; - - SlotMap fSlotMap; - - }; - - - template< typename XSignalSig > - void KTProcessor::RegisterSignal(std::string name, XSignalSig* signalPtr) - { - KTDEBUG(processorlog, "Registering signal <" << name << "> in processor <" << fConfigName << ">"); - KTSignalWrapper* sig = new KTSignalWrapper(signalPtr); - fSignalMap.insert(SigMapVal(name, sig)); - return; - } - - template< class XTarget, typename XReturn > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)()) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn () > signalConcept; - - boost::function< XReturn () > *func = new boost::function< XReturn () >(boost::bind(funcPtr, target)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - template< class XTarget, typename XReturn, typename XArg1 > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1)) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn (XArg1) > signalConcept; - - boost::function< XReturn (XArg1) > *func = new boost::function< XReturn (XArg1) >(boost::bind(funcPtr, target, _1)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - template< class XTarget, typename XReturn, typename XArg1, typename XArg2 > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1, XArg2)) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn (XArg1, XArg2) > signalConcept; - - boost::function< XReturn (XArg1, XArg2) > *func = new boost::function< XReturn (XArg1, XArg2) >(boost::bind(funcPtr, target, _1, _2)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - -#define KT_REGISTER_PROCESSOR(proc_class, proc_name) \ - static ::scarab::registrar< ::Nymph::KTProcessor, proc_class, const std::string& > sProc##proc_class##Registrar( proc_name ); - -} /* namespace Nymph */ -#endif /* KTPROCESSOR_HH_ */ diff --git a/Library/Processor/KTProcessorPy.hh b/Library/Processor/KTProcessorPy.hh deleted file mode 100644 index 92677dbe..00000000 --- a/Library/Processor/KTProcessorPy.hh +++ /dev/null @@ -1,109 +0,0 @@ -/** - @file KTProcessorPy.hh - @brief Contains KTProcessor python wrapping - @details KTProcessor is the processor base class - @author: B. H. LaRoque - @date: April 20, 2017 - */ - -#ifndef KTPROCESSORPY_HH_ -#define KTPROCESSORPY_HH_ - -#include "KTProcessor.hh" - -bool (Nymph::KTConfigurable::*Configure_JsonStr)(const std::string& config) = &Nymph::KTConfigurable::Configure; - -void export_Processor() -{ - using namespace Nymph; - using namespace boost::python; - - // KTProcessor base class - class_("KTProcessor", no_init) - .def("ConnectASlot", &KTProcessor::ConnectASlot) - .def("ConnectASignal", &KTProcessor::ConnectASignal) - .def("ConnectSignalToSlot", &KTProcessor::ConnectSignalToSlot) - .def("Configure", Configure_JsonStr, "Configure from json encoded configuration") - //.def("GetSignal", KTProcessor::GetSignal) - //.def("RegisterSignal", &KTProcessor::RegisterSignal) - ; -} - -/*{ - class KTProcessor : public KTConfigurable - { - public: - - template< class XProcessor > - void RegisterSignal(std::string name, XProcessor* signalPtr); - - template< class XTarget, typename XReturn > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)()); - - template< class XTarget, typename XReturn, typename XArg1 > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1)); - - template< class XTarget, typename XReturn, typename XArg1, typename XArg2 > - void RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1, XArg2)); - - KTSignalWrapper* GetSignal(const std::string& name); - - KTSlotWrapper* GetSlot(const std::string& name); - - }; - - - template< typename XSignalSig > - void KTProcessor::RegisterSignal(std::string name, XSignalSig* signalPtr) - { - KTDEBUG(processorlog, "Registering signal <" << name << "> in processor <" << fConfigName << ">"); - KTSignalWrapper* sig = new KTSignalWrapper(signalPtr); - fSignalMap.insert(SigMapVal(name, sig)); - return; - } - - template< class XTarget, typename XReturn > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)()) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn () > signalConcept; - - boost::function< XReturn () > *func = new boost::function< XReturn () >(boost::bind(funcPtr, target)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - template< class XTarget, typename XReturn, typename XArg1 > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1)) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn (XArg1) > signalConcept; - - boost::function< XReturn (XArg1) > *func = new boost::function< XReturn (XArg1) >(boost::bind(funcPtr, target, _1)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - template< class XTarget, typename XReturn, typename XArg1, typename XArg2 > - void KTProcessor::RegisterSlot(std::string name, XTarget* target, XReturn (XTarget::* funcPtr)(XArg1, XArg2)) - { - KTDEBUG(processorlog, "Registering slot <" << name << "> in processor <" << fConfigName << ">"); - - KTSignalConcept< XReturn (XArg1, XArg2) > signalConcept; - - boost::function< XReturn (XArg1, XArg2) > *func = new boost::function< XReturn (XArg1, XArg2) >(boost::bind(funcPtr, target, _1, _2)); - - KTSlotWrapper* slot = new KTSlotWrapper(func, &signalConcept); - fSlotMap.insert(SlotMapVal(name, slot)); - return; - } - - -}*/ -#endif /* KTPROCESSORPY_HH_ */ diff --git a/Library/Processor/KTSignal.cc b/Library/Processor/KTSignal.cc deleted file mode 100644 index 535ab2df..00000000 --- a/Library/Processor/KTSignal.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - * KTSignal.cc - * - * Created on: Feb 25, 2013 - * Author: nsoblath - */ - -#include "KTSignal.hh" - -namespace Nymph -{ - - KTSignalOneArg< void >::KTSignalOneArg(const std::string& name, KTProcessor* proc) : - fSignal() - { - proc->RegisterSignal(name, &fSignal); - } - - KTSignalOneArg< void >::KTSignalOneArg() : - fSignal() - {} - - KTSignalOneArg< void >::KTSignalOneArg(const KTSignalOneArg&) : - fSignal() - {} - - KTSignalOneArg< void >::~KTSignalOneArg() - { - } - - - - KTSignalData::KTSignalData(const std::string& name, KTProcessor* proc) : - KTSignalOneArg(name, proc), - fRefSignal() - { - proc->RegisterSignal("ref-"+name, &fRefSignal); - } - - KTSignalData::~KTSignalData() - { - } - - KTSignalData::KTSignalData() - { - } - - KTSignalData::KTSignalData(const KTSignalData&) - { - } - -} diff --git a/Library/Processor/KTSignal.hh b/Library/Processor/KTSignal.hh deleted file mode 100644 index 5a5b3cbc..00000000 --- a/Library/Processor/KTSignal.hh +++ /dev/null @@ -1,207 +0,0 @@ -/* - * KTSignal.hh - * - * Created on: Jan 15, 2013 - * Author: nsoblath - */ - -#ifndef KTSIGNAL_HH_ -#define KTSIGNAL_HH_ - -#include "KTProcessor.hh" - -#include "KTData.hh" - -#include - -#include - -namespace Nymph -{ - /*! - @class KTSignalOneArg - @author N. S. Oblath - - @brief Creates a signal that takes a single argument. - - @details - The signal is emitted by calling operator(). - If a KTDataSlot is being used, and the Slot has been given a pointer to this signal, the Slot will emit the Signal. - - Usage: - In your Processor's header add a member variable of type KTDataSignal< ArgumentType >. - - Initialize the signal with the processor's 'this' pointer and the name of the signal. - - To use the signal, call it as: fSignalObject(arg); - */ - template< class XSignalArgument > - class KTSignalOneArg - { - public: - typedef void (signature)(XSignalArgument); - typedef boost::signals2::signal< signature > boost_signal; - typedef typename boost::signals2::signal< signature >::slot_type slot_type; - - public: - KTSignalOneArg(); - KTSignalOneArg(const std::string& name, KTProcessor* proc); - virtual ~KTSignalOneArg(); - - protected: - KTSignalOneArg(const KTSignalOneArg&); - - public: - void operator()(XSignalArgument arg); - - boost_signal* Signal(); - - protected: - boost_signal fSignal; - - }; - - - template<> - class KTSignalOneArg< void > - { - public: - typedef void (signature)(void); - typedef boost::signals2::signal< signature > boost_signal; - // the following line is, technically, not valid C++03 code because the 'typename' keyword - // is appearing outside of a template (since the template here is fully specified, this counts). - // It should compiler with most new-ish compilers, even when compiling under C++03 mode. - // It has been tested successfully with GCC 4.6 and Clang 3.1 - typedef typename boost::signals2::signal< signature >::slot_type slot_type; - - public: - KTSignalOneArg(); - KTSignalOneArg(const std::string& name, KTProcessor* proc); - virtual ~KTSignalOneArg(); - - protected: - KTSignalOneArg(const KTSignalOneArg&); - - public: - void operator()(); - - boost_signal* Signal(); - - protected: - boost_signal fSignal; - - }; - - // convenience typedef for KTSignalDone - typedef KTSignalOneArg KTSignalDone; - - - - /*! - @class KTSignalData - @author N. S. Oblath - - @brief Creates a signal that takes a KTDataPtr object as its argument. - - @details - The purpose of the signal is for passing KTData pointers between Processors. - The signal is emitted by calling operator(). - If a KTDataSlot is being used, and the Slot has been given a pointer to this signal, the Slot will emit the Signal. - - Usage: - In your Processor's header add a member variable of type KTSignalData. - - Initialize the signal with the processor's 'this' pointer and the name of the signal. - - That's it! - */ - - class KTSignalData : public KTSignalOneArg< KTDataPtr > - { - public: - typedef void (signature)(KTDataPtr); - typedef boost::signals2::signal< signature > boost_signal; - typedef boost::signals2::signal< signature >::slot_type slot_type; - - typedef void (ref_signature)(KTDataPtr&); - typedef boost::signals2::signal< ref_signature > ref_boost_signal; - typedef boost::signals2::signal< ref_signature >::slot_type ref_slot_type; - - public: - KTSignalData(); - KTSignalData(const std::string& name, KTProcessor* proc); - virtual ~KTSignalData(); - - protected: - KTSignalData(const KTSignalData&); - - public: - void operator()(KTDataPtr arg); - - ref_boost_signal* RefSignal(); - - protected: - ref_boost_signal fRefSignal; - }; - - - - template< class XSignalArgument > - KTSignalOneArg< XSignalArgument >::KTSignalOneArg(const std::string& name, KTProcessor* proc) : - fSignal() - { - proc->RegisterSignal(name, &fSignal); - } - - template< class XSignalArgument > - KTSignalOneArg< XSignalArgument >::KTSignalOneArg() : - fSignal() - {} - - template< class XSignalArgument > - KTSignalOneArg< XSignalArgument >::KTSignalOneArg(const KTSignalOneArg&) : - fSignal() - {} - - template< class XSignalArgument > - KTSignalOneArg< XSignalArgument >::~KTSignalOneArg() - { - } - - template< class XSignalArgument > - inline void KTSignalOneArg< XSignalArgument >::operator()(XSignalArgument arg) - { - fSignal(arg); - } - - template< class XSignalArgument > - inline typename KTSignalOneArg< XSignalArgument >::boost_signal* KTSignalOneArg< XSignalArgument >::Signal() - { - return &fSignal; - } - - - inline void KTSignalOneArg< void >::operator()() - { - fSignal(); - } - - inline typename KTSignalOneArg< void >::boost_signal* KTSignalOneArg< void >::Signal() - { - return &fSignal; - } - - - inline void KTSignalData::operator()(KTDataPtr arg) - { - fSignal(arg); - fRefSignal(arg); - } - - inline typename KTSignalData::ref_boost_signal* KTSignalData::RefSignal() - { - return &fRefSignal; - } - -} /* namespace Nymph */ -#endif /* KTSIGNAL_HH_ */ diff --git a/Library/Processor/KTSlot.hh b/Library/Processor/KTSlot.hh deleted file mode 100644 index cf31968c..00000000 --- a/Library/Processor/KTSlot.hh +++ /dev/null @@ -1,552 +0,0 @@ -/* - * KTSlot.hh - * - * Created on: Jan 13, 2013 - * Author: nsoblath - */ - -#ifndef KTSLOT_HH_ -#define KTSLOT_HH_ - -#include "KTData.hh" -#include "KTLogger.hh" -#include "KTSignal.hh" - -#include -#include - -#include - -namespace Nymph -{ - KTLOGGER(slotlog, "KTSlot"); - - template< typename Signature> - class KTSlotNoArg - { - public: - typedef boost::function< Signature > function_signature; - typedef typename function_signature::result_type return_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotNoArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)()); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotNoArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)()); - virtual ~KTSlotNoArg(); - - return_type operator()(); - - protected: - boost::function< Signature > fFunc; - - }; - - /*! - @class KTSlotOneArg - @author N. S. Oblath - - @brief Creates a slot that calls a member function of the func_owner_type object, taking one argument. - - @details - Usage: - To use this slot type the function to be called by the slot must exist in an object of type FuncOwnerType. - The function should have the signature ReturnType (ArgumentType). - - In your Processor's header add a member variable of type KTSlotOneArg< ProcessorType, ArgumentType, ReturnType >. - The variable may be private. - - Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. - Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. - */ - template< typename Signature> - class KTSlotOneArg - { - public: - typedef boost::function< Signature > function_signature; - typedef typename function_signature::result_type return_type; - typedef typename function_signature::argument_type argument_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotOneArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(argument_type)); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotOneArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(argument_type)); - virtual ~KTSlotOneArg(); - - return_type operator()(argument_type arg); - - protected: - boost::function< Signature > fFunc; - - }; - - template< typename Signature> - class KTSlotTwoArg - { - public: - typedef boost::function< Signature > function_signature; - typedef typename function_signature::result_type return_type; - typedef typename function_signature::first_argument_type first_argument_type; - typedef typename function_signature::second_argument_type second_argument_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotTwoArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(first_argument_type, second_argument_type)); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotTwoArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(first_argument_type, second_argument_type)); - virtual ~KTSlotTwoArg(); - - return_type operator()(first_argument_type arg1, second_argument_type arg2); - - protected: - boost::function< Signature > fFunc; - - }; - - - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotNoArg< Signature >::KTSlotNoArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)()) : - fFunc(boost::bind(func, owner)) - { - owner->RegisterSlot(name, this, &KTSlotNoArg::operator()); - } - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotNoArg< Signature >::KTSlotNoArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)()) : - fFunc(boost::bind(func, owner)) - { - proc->RegisterSlot(name, this, &KTSlotNoArg::operator()); - } - - template< typename Signature> - KTSlotNoArg< Signature >::~KTSlotNoArg() - { - } - - template< typename Signature> - typename KTSlotNoArg< Signature >::return_type KTSlotNoArg< Signature >::operator()() - { - return fFunc(); - } - - - - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotOneArg< Signature >::KTSlotOneArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(argument_type)) : - fFunc(boost::bind(func, owner, _1)) - { - owner->RegisterSlot(name, this, &KTSlotOneArg::operator()); - } - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotOneArg< Signature >::KTSlotOneArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(argument_type)) : - fFunc(boost::bind(func, owner, _1)) - { - proc->RegisterSlot(name, this, &KTSlotOneArg::operator()); - } - - template< typename Signature> - KTSlotOneArg< Signature >::~KTSlotOneArg() - { - } - - template< typename Signature> - typename KTSlotOneArg< Signature >::return_type KTSlotOneArg< Signature >::operator()(argument_type arg) - { - return fFunc(arg); - } - - - - - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotTwoArg< Signature >::KTSlotTwoArg(const std::string& name, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(first_argument_type, second_argument_type)) : - fFunc(boost::bind(func, owner, _1, _2)) - { - owner->RegisterSlot(name, this, &KTSlotTwoArg::operator()); - } - - template< typename Signature> - template< class XFuncOwnerType > - KTSlotTwoArg< Signature >::KTSlotTwoArg(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, return_type (XFuncOwnerType::*func)(first_argument_type, second_argument_type)) : - fFunc(boost::bind(func, owner, _1, _2)) - { - proc->RegisterSlot(name, this, &KTSlotTwoArg::operator()); - } - - template< typename Signature> - KTSlotTwoArg< Signature >::~KTSlotTwoArg() - { - } - - template< typename Signature> - typename KTSlotTwoArg< Signature >::return_type KTSlotTwoArg< Signature >::operator()(first_argument_type arg1, second_argument_type arg2) - { - return fFunc(arg1, arg2); - } - - - - - - - /*! - @class KTDataSlotOneArg - @author N. S. Oblath - - @brief Creates a slot that takes a KTDataPtr object as the argument; the function that gets called should take DataType& as its argument. - - @details - Usage: - This slot type adds the slot function (signature void (KTDataPtr). - Your processor (or, optionally, a different object) must have a member function with the signature bool (DataType&). - The slot function checks that the provided KTData object contains data of type DataType, and then calls the member function. - - In your Processor's header add a member variable of type KTSlotOneArg< DataType >. - The variable may be private. - - Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. - Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. - - Also optionally, a signal to be emitted after the return of the member function can be specified as the last argument. - */ - template< class XDataType > - class KTSlotDataOneType - { - public: - typedef XDataType data_type; - typedef boost::function< void (KTDataPtr) > function_signature; - typedef typename function_signature::result_type return_type; - typedef typename function_signature::argument_type argument_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotDataOneType(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(data_type&), KTSignalData* signalPtr=NULL); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotDataOneType(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(data_type&), KTSignalData* signalPtr=NULL); - virtual ~KTSlotDataOneType(); - - void operator()(KTDataPtr data); - - protected: - boost::function< bool (data_type&) > fFunc; - - KTSignalData* fSignalPtr; - }; - - - template< class XDataType1, class XDataType2 > - class KTSlotDataTwoTypes - { - public: - typedef XDataType1 first_data_type; - typedef XDataType2 second_data_type; - typedef boost::function< void (KTDataPtr) > function_signature; - typedef typename function_signature::result_type return_type; - typedef typename function_signature::argument_type argument_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotDataTwoTypes(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&), KTSignalData* signalPtr=NULL); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotDataTwoTypes(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&), KTSignalData* signalPtr=NULL); - virtual ~KTSlotDataTwoTypes(); - - void operator()(KTDataPtr data); - - protected: - boost::function< bool (first_data_type&, second_data_type&) > fFunc; - - KTSignalData* fSignalPtr; - }; - - - template< class XDataType1, class XDataType2, class XDataType3 > - class KTSlotDataThreeTypes - { - public: - typedef XDataType1 first_data_type; - typedef XDataType2 second_data_type; - typedef XDataType3 third_data_type; - typedef boost::function< void (KTDataPtr) > function_signature; - typedef typename function_signature::result_type return_type; - typedef typename function_signature::argument_type argument_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotDataThreeTypes(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&, third_data_type&), KTSignalData* signalPtr=NULL); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotDataThreeTypes(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&, third_data_type&), KTSignalData* signalPtr=NULL); - virtual ~KTSlotDataThreeTypes(); - - void operator()(KTDataPtr data); - - protected: - boost::function< bool (first_data_type&, second_data_type&, third_data_type&) > fFunc; - - KTSignalData* fSignalPtr; - }; - - - - template< class XDataType > - template< class XFuncOwnerType > - KTSlotDataOneType< XDataType >::KTSlotDataOneType(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1)), - fSignalPtr(signalPtr) - { - owner->RegisterSlot(name, this, &KTSlotDataOneType::operator()); - } - - template< class XDataType > - template< class XFuncOwnerType > - KTSlotDataOneType< XDataType >::KTSlotDataOneType(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1)), - fSignalPtr(signalPtr) - { - proc->RegisterSlot(name, this, &KTSlotDataOneType::operator()); - } - - template< class XDataType > - KTSlotDataOneType< XDataType >::~KTSlotDataOneType() - { - } - - template< class XDataType > - void KTSlotDataOneType< XDataType >::operator()(KTDataPtr data) - { - // Standard data slot pattern: - // Check to ensure that the required data type is present - if (! data->Has< data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(data_type).name() << ">"); - return; - } - // Call the function - if (! fFunc(data->Of< data_type >())) - { - KTERROR(slotlog, "Something went wrong while analyzing data with type <" << typeid(data_type).name() << ">"); - return; - } - // If there's a signal pointer, emit the signal - if (fSignalPtr != NULL) - { - (*fSignalPtr)(data); - } - return; - } - - - - template< class XDataType1, class XDataType2 > - template< class XFuncOwnerType > - KTSlotDataTwoTypes< XDataType1, XDataType2 >::KTSlotDataTwoTypes(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1, _2)), - fSignalPtr(signalPtr) - { - owner->RegisterSlot(name, this, &KTSlotDataTwoTypes::operator()); - } - - template< class XDataType1, class XDataType2 > - template< class XFuncOwnerType > - KTSlotDataTwoTypes< XDataType1, XDataType2 >::KTSlotDataTwoTypes(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1, _2)), - fSignalPtr(signalPtr) - { - proc->RegisterSlot(name, this, &KTSlotDataTwoTypes::operator()); - } - - template< class XDataType1, class XDataType2 > - KTSlotDataTwoTypes< XDataType1, XDataType2 >::~KTSlotDataTwoTypes() - { - } - - template< class XDataType1, class XDataType2 > - void KTSlotDataTwoTypes< XDataType1, XDataType2 >::operator()(KTDataPtr data) - { - // Standard data slot pattern: - // Check to ensure that the required data type is present - if (! data->Has< first_data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(first_data_type).name() << ">"); - return; - } - if (! data->Has< second_data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(second_data_type).name() << ">"); - return; - } - // Call the function - if (! fFunc(data->Of< first_data_type >(), data->Of< second_data_type >())) - { - KTERROR(slotlog, "Something went wrong while analyzing data with types <" << typeid(first_data_type).name() << "> and <" << typeid(second_data_type).name() << ">"); - return; - } - // If there's a signal pointer, emit the signal - if (fSignalPtr != NULL) - { - (*fSignalPtr)(data); - } - return; - } - - - template< class XDataType1, class XDataType2, class XDataType3 > - template< class XFuncOwnerType > - KTSlotDataThreeTypes< XDataType1, XDataType2, XDataType3 >::KTSlotDataThreeTypes(const std::string& name, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&, third_data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1, _2, _3)), - fSignalPtr(signalPtr) - { - owner->RegisterSlot(name, this, &KTSlotDataThreeTypes::operator()); - } - - template< class XDataType1, class XDataType2, class XDataType3 > - template< class XFuncOwnerType > - KTSlotDataThreeTypes< XDataType1, XDataType2, XDataType3 >::KTSlotDataThreeTypes(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, bool (XFuncOwnerType::*func)(first_data_type&, second_data_type&, third_data_type&), KTSignalData* signalPtr) : - fFunc(boost::bind(func, owner, _1, _2, _3)), - fSignalPtr(signalPtr) - { - proc->RegisterSlot(name, this, &KTSlotDataThreeTypes::operator()); - } - - template< class XDataType1, class XDataType2, class XDataType3 > - KTSlotDataThreeTypes< XDataType1, XDataType2, XDataType3 >::~KTSlotDataThreeTypes() - { - } - - template< class XDataType1, class XDataType2, class XDataType3 > - void KTSlotDataThreeTypes< XDataType1, XDataType2, XDataType3 >::operator()(KTDataPtr data) - { - // Standard data slot pattern: - // Check to ensure that the required data type is present - if (! data->Has< first_data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(first_data_type).name() << ">"); - return; - } - if (! data->Has< second_data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(second_data_type).name() << ">"); - return; - } - if (! data->Has< third_data_type >()) - { - KTERROR(slotlog, "Data not found with type <" << typeid(third_data_type).name() << ">"); - return; - } - // Call the function - if (! fFunc(data->Of< first_data_type >(), data->Of< second_data_type >(), data->Of< third_data_type >())) - { - KTERROR(slotlog, "Something went wrong while analyzing data with types <" << typeid(first_data_type).name() << ">, <" << typeid(second_data_type).name() << ">, and <" << typeid(third_data_type).name() << ">"); - return; - } - // If there's a signal pointer, emit the signal - if (fSignalPtr != NULL) - { - (*fSignalPtr)(data); - } - return; - } - - - /*! - @class KTDoneSlot - @author N. S. Oblath - - @brief Creates a slot to receive indication that upstream processing is complete and will emit a similar signal. - - @details - Usage: - This slot type adds the slot function (signature void (). - Your processor (or, optionally, a different object) must have a member function with the signature bool (). - The slot calls the member function. - - In your Processor's header add a member variable of type KTDoneSlot. - The variable may be private. - - Initialize the slot with the name of the slot, the address of the owner of the slot function, and the function pointer. - Optionally, if the Processor is separate from the owner of the slot function, the Processor address is specified as the second argument to the constructor. - - Also optionally, a signal to be emitted after the return of the member function can be specified as the last argument. - */ - class KTSlotDone - { - public: - typedef boost::function< void () > function_signature; - typedef bool return_type; - - public: - /// Constructor for the case where the processor has the function that will be called by the slot - template< class XFuncOwnerType > - KTSlotDone(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr=NULL); - /// Constructor for the case where the processor and the object with the function that will be called are different - template< class XFuncOwnerType > - KTSlotDone(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr=NULL); - virtual ~KTSlotDone(); - - void operator()(); - - protected: - boost::function< void () > fFunc; - - KTSignalDone* fSignalPtr; - }; - - template< class XFuncOwnerType > - KTSlotDone::KTSlotDone(const std::string& name, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr) : - fFunc(boost::bind(func, owner)), - fSignalPtr(signalPtr) - { - owner->RegisterSlot(name, this, &KTSlotDone::operator()); - } - - template< class XFuncOwnerType > - KTSlotDone::KTSlotDone(const std::string& name, KTProcessor* proc, XFuncOwnerType* owner, void (XFuncOwnerType::*func)(), KTSignalDone* signalPtr) : - fFunc(boost::bind(func, owner)), - fSignalPtr(signalPtr) - { - proc->RegisterSlot(name, this, &KTSlotDone::operator()); - } - - inline KTSlotDone::~KTSlotDone() - { - } - - inline void KTSlotDone::operator()() - { - // Call the function - fFunc(); - - // If there's a signal pointer, emit the signal - if (fSignalPtr != NULL) - { - (*fSignalPtr)(); - } - return; - } - - - -} /* namespace Nymph */ -#endif /* KTSLOT_HH_ */ diff --git a/Library/Utility/KTException.hh b/Library/Utility/KTException.hh deleted file mode 100644 index a599fbf2..00000000 --- a/Library/Utility/KTException.hh +++ /dev/null @@ -1,61 +0,0 @@ -/* - * KTException.hh - * - * Created on: Feb 25, 2014 - * Author: nsoblath - */ - - -#ifndef KTEXCEPTION_HH_ -#define KTEXCEPTION_HH_ - -#include -#include -#include - -namespace Nymph -{ - - class KTException : - public std::exception - { - public: - KTException(); - KTException( const KTException& ); - ~KTException() throw (); - - template< class XStreamable > - KTException& operator<<( XStreamable a_fragment ); - KTException& operator<<( const std::string& a_fragment ); - KTException& operator<<( const char* a_fragment ); - - virtual const char* what() const throw(); - - private: - std::string fException; - }; - - template< class XStreamable > - KTException& KTException::operator<<( XStreamable a_fragment ) - { - std::stringstream stream; - stream << a_fragment; - stream >> fException; - return *this; - } - - inline KTException& KTException::operator<<( const std::string& a_fragment ) - { - fException += a_fragment; - return *this; - } - - inline KTException& KTException::operator<<( const char* a_fragment ) - { - fException += std::string( a_fragment ); - return *this; - } - -} - -#endif /* KTEXCEPTION_HH_ */ diff --git a/Library/Utility/KTLogger.cc b/Library/Utility/KTLogger.cc deleted file mode 100644 index 7cc87190..00000000 --- a/Library/Utility/KTLogger.cc +++ /dev/null @@ -1,195 +0,0 @@ -/* - * KTLogger.cc - * - * Created on: Jan 21, 2014 - * Author: nsoblath - */ - -/* - * KTLogger.cc - * based on KLogger.cxx from KATRIN's Kasper - * - * Created on: 18.11.2011 - * Author: Marco Haag - */ - -#include "KTLogger.hh" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -namespace Nymph -{ - const string& EndColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_NORMAL KTCOLOR_SUFFIX); return *color;} - const string& FatalColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_RED KTCOLOR_SUFFIX); return *color;} - const string& ErrorColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_RED KTCOLOR_SUFFIX); return *color;} - const string& WarnColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_YELLOW KTCOLOR_SUFFIX); return *color;} - const string& ProgColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_BLUE KTCOLOR_SUFFIX); return *color;} - const string& InfoColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_GREEN KTCOLOR_SUFFIX); return *color;} - const string& DebugColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_CYAN KTCOLOR_SUFFIX); return *color;} - const string& OtherColor() {static string* color = new string(KTCOLOR_PREFIX KTCOLOR_BRIGHT KTCOLOR_SEPARATOR KTCOLOR_FOREGROUND_WHITE KTCOLOR_SUFFIX); return *color;} - - struct KTLogger::Private - { - static char sDateTimeFormat[16]; - static time_t sRawTime; - static tm* sProcessedTime; - static char sTimeBuff[512]; - static size_t getTimeAbsoluteStr() - { - time(&KTLogger::Private::sRawTime); - sProcessedTime = localtime(&KTLogger::Private::sRawTime); - return strftime(KTLogger::Private::sTimeBuff, 512, - KTLogger::Private::sDateTimeFormat, - KTLogger::Private::sProcessedTime); - } - - const char* fLogger; - bool fColored; - ELevel fThreshold; - - static const char* level2Str(ELevel level) - { - switch(level) - { - case eTrace : return "TRACE"; break; - case eDebug : return "DEBUG"; break; - case eInfo : return "INFO"; break; - case eProg : return "PROG"; break; - case eWarn : return "WARN"; break; - case eError : return "ERROR"; break; - case eFatal : return "FATAL"; break; - default : return "XXX"; - } - } - - static string level2Color(ELevel level) - { - switch(level) - { - case eTrace : return DebugColor(); break; - case eDebug : return DebugColor(); break; - case eInfo : return InfoColor(); break; - case eProg : return ProgColor(); break; - case eWarn : return WarnColor(); break; - case eError : return ErrorColor(); break; - case eFatal : return FatalColor(); break; - default : return OtherColor(); - } - } - - - void logCout(ELevel level, const string& message, const Location& loc) - { - getTimeAbsoluteStr(); - if (fColored) - { - //cout << color << KTLogger::Private::sTimeBuff << " [" << setw(5) << level << "] " << setw(16) << left << loc.fFileName << "(" << loc.fLineNumber << "): " << message << skKTEndColor << endl; - cout << Private::level2Color(level) << KTLogger::Private::sTimeBuff << " [" << setw(5) << Private::level2Str(level) << "] "; - copy(loc.fFileName.end() - std::min< int >(loc.fFileName.size(), 16), loc.fFileName.end(), ostream_iterator(cout)); - cout << "(" << loc.fLineNumber << "): "; - cout << message << EndColor() << endl; - } - else - { - //cout << KTLogger::Private::sTimeBuff << " [" << setw(5) << level << "] " << setw(16) << left << loc.fFileName << "(" << loc.fLineNumber << "): " << message << endl; - cout << KTLogger::Private::sTimeBuff << " [" << setw(5) << level << "] "; - copy(loc.fFileName.end() - std::min< int >(loc.fFileName.size(), 16), loc.fFileName.end(), ostream_iterator(cout)); - cout << "(" << loc.fLineNumber << "): "; - cout << message << endl; - } - } - - void logCerr(ELevel level, const string& message, const Location& loc) - { - getTimeAbsoluteStr(); - if (fColored) - { - //cout << color << KTLogger::Private::sTimeBuff << " [" << setw(5) << level << "] " << setw(16) << left << loc.fFileName << "(" << loc.fLineNumber << "): " << message << skKTEndColor << endl; - cerr << Private::level2Color(level) << KTLogger::Private::sTimeBuff << " [" << setw(5) << Private::level2Str(level) << "] "; - copy(loc.fFileName.end() - std::min< int >(loc.fFileName.size(), 16), loc.fFileName.end(), ostream_iterator(cout)); - cerr << "(" << loc.fLineNumber << "): "; - cerr << message << EndColor() << endl; - } - else - { - //cout << KTLogger::Private::sTimeBuff << " [" << setw(5) << level << "] " << setw(16) << left << loc.fFileName << "(" << loc.fLineNumber << "): " << message << endl; - cerr << KTLogger::Private::sTimeBuff << " [" << setw(5) << Private::level2Str(level) << "] "; - copy(loc.fFileName.end() - std::min< int >(loc.fFileName.size(), 16), loc.fFileName.end(), ostream_iterator(cout)); - cerr << "(" << loc.fLineNumber << "): "; - cerr << message << endl; - } - } - }; - - char KTLogger::Private::sDateTimeFormat[16]; - time_t KTLogger::Private::sRawTime; - tm* KTLogger::Private::sProcessedTime; - char KTLogger::Private::sTimeBuff[512]; - - KTLogger::KTLogger(const char* name) : fPrivate(new Private()) - { - if (name == 0) - { - fPrivate->fLogger = "root"; - } - else - { - const char* logName = strrchr(name, '/') ? strrchr(name, '/') + 1 : name; - fPrivate->fLogger = logName; - } - fPrivate->fColored = true; - sprintf(KTLogger::Private::sDateTimeFormat, "%%T"); - SetLevel(eDebug); - } - - KTLogger::KTLogger(const std::string& name) : fPrivate(new Private()) - { - fPrivate->fLogger = name.c_str(); - fPrivate->fColored = true; - sprintf(KTLogger::Private::sDateTimeFormat, "%%T"); - SetLevel(eDebug); - } - - KTLogger::~KTLogger() - { - delete fPrivate; - } - - bool KTLogger::IsLevelEnabled(ELevel level) const - { - return level >= fPrivate->fThreshold; - } - - void KTLogger::SetLevel(ELevel level) const - { -#if defined(NDEBUG) && defined(STANDARD) - fPrivate->fThreshold = level >= eInfo ? level : eInfo; -#elif defined(NDEBUG) - fPrivate->fThreshold = level >= eProg ? level : eProg; -#else - fPrivate->fThreshold = level; -#endif - } - - void KTLogger::Log(ELevel level, const string& message, const Location& loc) - { - if (level >= eWarn) - { - fPrivate->logCerr(level, message, loc); - } - else - { - fPrivate->logCout(level, message, loc); - } - } -} - diff --git a/Library/Utility/KTLogger.hh b/Library/Utility/KTLogger.hh deleted file mode 100644 index f1fec2fd..00000000 --- a/Library/Utility/KTLogger.hh +++ /dev/null @@ -1,360 +0,0 @@ -/* - * KTLogger.hh - * Based on KLogger.h, from KATRIN's Kasper - * - * Created on: Jan 23, 2014 - * Author: nsoblath - */ - -#ifndef KTLOGGER_HH_ -#define KTLOGGER_HH_ - -/** - * @file - * @brief Contains the logger class and macros, based on Kasper's KLogger class. - * @date Created on: 18.11.2011 - * @author Marco Haag - * - */ - -// UTILITY MACROS - -#ifndef LOGGER_UTILITY_MACROS_ -#define LOGGER_UTILITY_MACROS_ - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) -#define __FILE_LINE__ __FILE__ "(" TOSTRING(__LINE__) ")" -#define __FILENAME_LINE__ (strrchr(__FILE__, '/') ? strrchr(__FILE_LINE__, '/') + 1 : __FILE_LINE__) - -#if defined(_MSC_VER) -#if _MSC_VER >= 1300 -#define __FUNC__ __FUNCSIG__ -#endif -#else -#if defined(__GNUC__) -#define __FUNC__ __PRETTY_FUNCTION__ -#endif -#endif -#if !defined(__FUNC__) -#define __FUNC__ "" -#endif - -#define va_num_args(...) va_num_args_impl(__VA_ARGS__, 5,4,3,2,1) -#define va_num_args_impl(_1,_2,_3,_4,_5,N,...) N - -#define macro_dispatcher(func, ...) macro_dispatcher_(func, va_num_args(__VA_ARGS__)) -#define macro_dispatcher_(func, nargs) macro_dispatcher__(func, nargs) -#define macro_dispatcher__(func, nargs) func ## nargs - -#endif /* LOGGER_UTILITY_MACROS_ */ - -// COLOR DEFINITIONS -#define KTCOLOR_NORMAL "0" -#define KTCOLOR_BRIGHT "1" -#define KTCOLOR_FOREGROUND_RED "31" -#define KTCOLOR_FOREGROUND_GREEN "32" -#define KTCOLOR_FOREGROUND_YELLOW "33" -#define KTCOLOR_FOREGROUND_BLUE "34" -#define KTCOLOR_FOREGROUND_CYAN "36" -#define KTCOLOR_FOREGROUND_WHITE "37" -#define KTCOLOR_PREFIX "\033[" -#define KTCOLOR_SUFFIX "m" -#define KTCOLOR_SEPARATOR ";" - -// INCLUDES - -#include -#include -#include - -// CLASS DEFINITIONS - -/** - * The standard Nymph namespace. - */ -namespace Nymph -{ - - /** - * The Nymph logger. - * - * The usage and syntax is inspired by log4j. logger itself uses the log4cxx library if it - * was available on the system during compiling, otherwise it falls back to std::stream. - * - * The logger output can be configured in a file specified with the environment variable - * @a LOGGER_CONFIGURATION (by default log4cxx.properties in the config directory). - * - * In most cases the following macro can be used - * to instantiate a Logger in your code: - *
        LOGGER(myLogger, "loggerName");
        - * - * This is equivalent to: - *
        static Nymph::logger myLogger("loggerName");
        - * - * For logging the following macros can be used. The source code location will then automatically - * included in the output: - * - *
        -     * KTLOG(myLogger, level, "message");
        -     * KTTRACE(myLogger, "message");
        -     * KTDEBUG(myLogger, "message");
        -     * KTINFO(myLogger, "message");
        -     * KTPROG(myLogger, "message");
        -     * KTWARN(myLogger, "message");
        -     * KTERROR(myLogger, "message");
        -     * KTFATAL(myLogger, "message");
        -     *
        -     * KTASSERT(myLogger, assertion, "message");
        -     *
        -     * KTLOG_ONCE(myLogger, level, "message");
        -     * KTTRACE_ONCE(myLogger, "message");
        -     * KTDEBUG_ONCE(myLogger, "message");
        -     * KTINFO_ONCE(myLogger, "message");
        -     * KTPROG_ONCE(myLogger, "message");
        -     * KTWARN_ONCE(myLogger, "message");
        -     * KTERROR_ONCE(myLogger, "message");
        -     * KTFATAL_ONCE(myLogger, "message");
        -     * 
        - * - */ - class KTLogger - { - public: - enum ELevel { - eTrace, eDebug, eInfo, eProg, eWarn, eError, eFatal - }; - - public: - /** - * A simple struct used by the Logger macros to pass information about the filename and line number. - * Not to be used directly by the user! - */ - struct Location { - Location(const char* const fileName = "", const char* const functionName = "", int lineNumber = -1) : - fLineNumber(lineNumber), fFileName(fileName), fFunctionName(functionName) - { } - int fLineNumber; - std::string fFileName; - std::string fFunctionName; - }; - - public: - static KTLogger& GetRootLogger() { - static KTLogger rootLogger; - return rootLogger; - } - - public: - /** - * Standard constructor assigning a name to the logger instance. - * @param name The logger name. - */ - KTLogger(const char* name = 0); - /// @overload - KTLogger(const std::string& name); - - virtual ~KTLogger(); - - /** - * Check whether a certain log-level is enabled. - * @param level The log level as string representation. - * @return - */ - bool IsLevelEnabled(ELevel level) const; - - /** - * Set a loggers minimum logging level - * @param level string identifying the log level - */ - void SetLevel(ELevel level) const; - - /** - * Log a message with the specified level. - * Use the macro LOG(logger, level, message). - * @param level The log level. - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void Log(ELevel level, const std::string& message, const Location& loc = Location()); - - /** - * Log a message at TRACE level. - * Use the macro TRACE(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogTrace(const std::string& message, const Location& loc = Location()) - { - Log(eTrace, message, loc); - } - /** - * Log a message at DEBUG level. - * Use the macro DEBUG(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogDebug(const std::string& message, const Location& loc = Location()) - { - Log(eDebug, message, loc); - } - /** - * Log a message at INFO level. - * Use the macro INFO(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogInfo(const std::string& message, const Location& loc = Location()) - { - Log(eInfo, message, loc); - } - /** - * Log a message at PROG level. - * Use the macro PROG(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogProg(const std::string& message, const Location& loc = Location()) - { - Log(eProg, message, loc); - } - /** - * Log a message at WARN level. - * Use the macro WARN(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogWarn(const std::string& message, const Location& loc = Location()) - { - Log(eWarn, message, loc); - } - /** - * Log a message at ERROR level. - * Use the macro ERROR(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogError(const std::string& message, const Location& loc = Location()) - { - Log(eError, message, loc); - } - /** - * Log a message at FATAL level. - * Use the macro FATAL(logger, message). - * @param message The message. - * @param loc Source code location (set automatically by the corresponding macro). - */ - void LogFatal(const std::string& message, const Location& loc = Location()) - { - Log(eFatal, message, loc); - } - - private: - struct Private; - Private* fPrivate; - }; - -} - -// PRIVATE MACROS - -#define __KTDEFAULT_LOGGER ::Nymph::KTLogger::GetRootLogger() - -#define __KTLOG_LOCATION ::Nymph::KTLogger::Location(__FILE__, __FUNC__, __LINE__) - -#define __KTLOG_LOG_4(I,L,M,O) \ - { \ - if (I.IsLevelEnabled(::Nymph::KTLogger::e##L)) { \ - static bool _sLoggerMarker = false; \ - if (!O || !_sLoggerMarker) { \ - _sLoggerMarker = true; \ - ::std::ostringstream stream; stream << M; \ - I.Log(::Nymph::KTLogger::e##L, stream.str(), __KTLOG_LOCATION); \ - } \ - } \ - } - -#define __KTLOG_LOG_3(I,L,M) __KTLOG_LOG_4(I,L,M,false) -#define __KTLOG_LOG_2(L,M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,L,M,false) -#define __KTLOG_LOG_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Debug,M,false) - -#define __KTLOG_TRACE_2(I,M) __KTLOG_LOG_4(I,Trace,M,false) -#define __KTLOG_TRACE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Trace,M,false) - -#ifndef NDEBUG -#define __KTLOG_DEBUG_2(I,M) __KTLOG_LOG_4(I,Debug,M,false) -#define __KTLOG_DEBUG_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Debug,M,false) -#else -#define __KTLOG_DEBUG_2(I,M) __KTLOG_LOG_4(I,Debug,"",false) -#define __KTLOG_DEBUG_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Debug,"",false) -#endif - -#define __KTLOG_INFO_2(I,M) __KTLOG_LOG_4(I,Info,M,false) -#define __KTLOG_INFO_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Info,M,false) - -#define __KTLOG_PROG_2(I,M) __KTLOG_LOG_4(I,Prog,M,false) -#define __KTLOG_PROG_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Prog,M,false) - -#define __KTLOG_WARN_2(I,M) __KTLOG_LOG_4(I,Warn,M,false) -#define __KTLOG_WARN_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Warn,M,false) - -#define __KTLOG_ERROR_2(I,M) __KTLOG_LOG_4(I,Error,M,false) -#define __KTLOG_ERROR_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Error,M,false) - -#define __KTLOG_FATAL_2(I,M) __KTLOG_LOG_4(I,Fatal,M,false) -#define __KTLOG_FATAL_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Fatal,M,false) - -#define __KTLOG_ASSERT_3(I,C,M) if (!(C)) { __MTLOG_ERROR_2(I,M) } -#define __KTLOG_ASSERT_2(C,M) __KTLOG_ASSERT_3(__KTDEFAULT_LOGGER,C,M) - - -#define __KTLOG_LOG_ONCE_3(I,L,M) __KTLOG_LOG_4(I,L,M,true) -#define __KTLOG_LOG_ONCE_2(L,M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,L,M,true) -#define __KTLOG_LOG_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Debug,M,true) - -#define __KTLOG_TRACE_ONCE_2(I,M) __KTLOG_LOG_4(I,Trace,M,true) -#define __KTLOG_TRACE_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Trace,M,true) - -#define __KTLOG_DEBUG_ONCE_2(I,M) __KTLOG_LOG_4(I,Debug,M,true) -#define __KTLOG_DEBUG_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Debug,M,true) - -#define __KTLOG_INFO_ONCE_2(I,M) __KTLOG_LOG_4(I,Info,M,true) -#define __KTLOG_INFO_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Info,M,true) - -#define __KTLOG_PROG_ONCE_2(I,M) __KTLOG_LOG_4(I,Prog,M,true) -#define __KTLOG_PROG_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Prog,M,true) - -#define __KTLOG_WARN_ONCE_2(I,M) __KTLOG_LOG_4(I,Warn,M,true) -#define __KTLOG_WARN_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Warn,M,true) - -#define __KTLOG_ERROR_ONCE_2(I,M) __KTLOG_LOG_4(I,Error,M,true) -#define __KTLOG_ERROR_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Error,M,true) - -#define __KTLOG_FATAL_ONCE_2(I,M) __KTLOG_LOG_4(I,Fatal,M,true) -#define __KTLOG_FATAL_ONCE_1(M) __KTLOG_LOG_4(__KTDEFAULT_LOGGER,Fatal,M,true) - - -// PUBLIC MACROS - -#define KTLOGGER(I,K) static ::Nymph::KTLogger I(K); - -#define KTLOG(...) macro_dispatcher(__KTLOG_LOG_, __VA_ARGS__)(__VA_ARGS__) -#define KTTRACE(...) macro_dispatcher(__KTLOG_TRACE_, __VA_ARGS__)(__VA_ARGS__) -#define KTDEBUG(...) macro_dispatcher(__KTLOG_DEBUG_, __VA_ARGS__)(__VA_ARGS__) -#define KTINFO(...) macro_dispatcher(__KTLOG_INFO_, __VA_ARGS__)(__VA_ARGS__) -#define KTPROG(...) macro_dispatcher(__KTLOG_PROG_, __VA_ARGS__)(__VA_ARGS__) -#define KTWARN(...) macro_dispatcher(__KTLOG_WARN_, __VA_ARGS__)(__VA_ARGS__) -#define KTERROR(...) macro_dispatcher(__KTLOG_ERROR_, __VA_ARGS__)(__VA_ARGS__) -#define KTFATAL(...) macro_dispatcher(__KTLOG_FATAL_, __VA_ARGS__)(__VA_ARGS__) -#define KTASSERT(...) macro_dispatcher(__KTLOG_ASSERT_, __VA_ARGS__)(__VA_ARGS__) - -#define KTLOG_ONCE(...) macro_dispatcher(__KTLOG_LOG_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTTRACE_ONCE(...) macro_dispatcher(__KTLOG_TRACE_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTDEBUG_ONCE(...) macro_dispatcher(__KTLOG_DEBUG_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTINFO_ONCE(...) macro_dispatcher(__KTLOG_INFO_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTPROG_ONCE(...) macro_dispatcher(__KTLOG_PROG_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTWARN_ONCE(...) macro_dispatcher(__KTLOG_WARN_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTERROR_ONCE(...) macro_dispatcher(__KTLOG_ERROR_ONCE_, __VA_ARGS__)(__VA_ARGS__) -#define KTFATAL_ONCE(...) macro_dispatcher(__KTLOG_FATAL_ONCE_, __VA_ARGS__)(__VA_ARGS__) - -#endif /* KTLOGGER_HH_ */ diff --git a/Library/Utility/KTMemberVariable.hh b/Library/Utility/KTMemberVariable.hh deleted file mode 100644 index d93438a6..00000000 --- a/Library/Utility/KTMemberVariable.hh +++ /dev/null @@ -1,101 +0,0 @@ -/* - * KTMemberVariable.hh - * - * Created on: Aug 5, 2014 - * Author: nsoblath - */ - -#ifndef KTMEMBERVARIABLE_HH_ -#define KTMEMBERVARIABLE_HH_ - - -/** - * Creates a member variable with type TYPE name f[NAME], plus getter and setter. - * MEMBERVARIABLE_NOSET will provide the variable and getter, but no setter, allowing you to provide a custom setter. - * - * Usage example, in a class header file: - * MEMBERVARIABLE(double, MyVar) - * - * You still need to initialize the variables in the class constructors. - * - * The generated code is: - * private: - * TYPE f[NAME]; - * public: - * inline TYPE Get[NAME]() const - * { - * return f[NAME]; - * } - * inline void Set[NAME](TYPE var) - * { - * f[NAME] = var; - * } - */ -#define MEMBERVARIABLE_NOSET(TYPE, NAME) \ - private: \ - TYPE f##NAME; \ - public: \ - inline TYPE Get##NAME() const {return f##NAME;} \ - -#define MEMBERVARIABLE(TYPE, NAME) \ - MEMBERVARIABLE_NOSET(TYPE, NAME) \ - inline void Set##NAME(TYPE var) {f##NAME = var; return;} - -#define MEMBERVARIABLE_PROTECTED_NOSET(TYPE, NAME) \ - protected: \ - TYPE f##NAME; \ - public: \ - inline TYPE Get##NAME() const {return f##NAME;} \ - -#define MEMBERVARIABLE_PROTECTED(TYPE, NAME) \ - MEMBERVARIABLE_PROTECTED_NOSET(TYPE, NAME) \ - inline void Set##NAME(TYPE var) {f##NAME = var; return;} - - - -/** - * Creates a member variable with type TYPE name f[NAME], plus getters and setters. - * MEMBERVARIABLEREF_NOSET will provide the variable and getter, but no setter, allowing you to provide a custom setter. - * - * Usage example, in a class header file: - * MEMBERVARIABLEREF(std::string, MyVar) - * - * You still need to initialize the variables in the class constructors. - * - * The generated code is: - * private: - * TYPE f[NAME]; - * public: - * inline const TYPE& Get[NAME]() const - * { - * return f[NAME]; - * } - * inline void Set[NAME](const TYPE& var) - * { - * f[NAME] = var; - * } - */ -#define MEMBERVARIABLEREF_NOSET(TYPE, NAME) \ - private: \ - TYPE f##NAME; \ - public: \ - inline const TYPE& Get##NAME() const {return f##NAME;} \ - inline TYPE& Get##NAME() {return f##NAME;} - -#define MEMBERVARIABLEREF(TYPE, NAME) \ - MEMBERVARIABLEREF_NOSET(TYPE, NAME) \ - inline void Set##NAME(const TYPE& var) {f##NAME = var; return;} - -#define MEMBERVARIABLEREF_PROTECTED_NOSET(TYPE, NAME) \ - protected: \ - TYPE f##NAME; \ - public: \ - inline const TYPE& Get##NAME() const {return f##NAME;} \ - inline TYPE& Get##NAME() {return f##NAME;} - -#define MEMBERVARIABLEREF_PROTECTED(TYPE, NAME) \ - MEMBERVARIABLEREF_PROTECTED_NOSET(TYPE, NAME) \ - inline void Set##NAME(const TYPE& var) {f##NAME = var; return;} - - -#endif /* KTMEMBERVARIABLE_HH_ */ diff --git a/Library/Utility/KTTIFactory.hh b/Library/Utility/KTTIFactory.hh deleted file mode 100644 index c2c0ccfe..00000000 --- a/Library/Utility/KTTIFactory.hh +++ /dev/null @@ -1,187 +0,0 @@ -/* - * KTTIFactory.hh - * - * Created on: Jan 2, 2013 - * Author: nsoblath - * - * Type-Indexed Factory and Registrars - */ - -#ifndef KTTIFACTORY_HH_ -#define KTTIFACTORY_HH_ - -#include "KTLogger.hh" - -#include "singleton.hh" - -#include -#include - -namespace Nymph -{ - KTLOGGER(utillog_ti_factory, "KTTIFactory"); - - template< class XBaseType > - class KTTIFactory; - - template< class XBaseType > - class KTTIRegistrarBase - { - public: - KTTIRegistrarBase() {} - virtual ~KTTIRegistrarBase() {} - - public: - friend class KTTIFactory< XBaseType >; - - protected: - virtual XBaseType* Create() const = 0; - - }; - - template< class XBaseType, class XDerivedType > - class KTTIRegistrar : public KTTIRegistrarBase< XBaseType > - { - public: - KTTIRegistrar(); - virtual ~KTTIRegistrar(); - - protected: - void Register() const; - - XBaseType* Create() const; - - }; - - - template< class XBaseType > - class KTTIFactory : public scarab::singleton< KTTIFactory< XBaseType > > - { - public: - struct CompareTypeInfo - { - bool operator() (const std::type_info* lhs, const std::type_info* rhs) - { - return lhs->before(*rhs); - } - }; - - public: - typedef std::map< const std::type_info*, const KTTIRegistrarBase< XBaseType >* > FactoryMap; - typedef typename FactoryMap::value_type FactoryEntry; - typedef typename FactoryMap::iterator FactoryIt; - typedef typename FactoryMap::const_iterator FactoryCIt; - - public: - template< class XDerivedType > - XBaseType* Create(); - - XBaseType* Create(const FactoryCIt& iter); - - template< class XDerivedType > - void Register(const KTTIRegistrarBase< XBaseType >* registrar); - - FactoryCIt GetFactoryMapBegin() const; - FactoryCIt GetFactoryMapEnd() const; - - protected: - FactoryMap* fMap; - - - protected: - friend class scarab::singleton< KTTIFactory >; - friend class scarab::destroyer< KTTIFactory >; - KTTIFactory(); - ~KTTIFactory(); - }; - - template< class XBaseType > - template< class XDerivedType > - XBaseType* KTTIFactory< XBaseType >::Create() - { - FactoryCIt it = fMap->find(&typeid(XDerivedType)); - if (it == fMap->end()) - { - KTERROR(utillog_ti_factory, "Did not find factory with type <" << typeid(XDerivedType).name() << ">."); - return NULL; - } - - return it->second->Create(); - } - - template< class XBaseType > - XBaseType* KTTIFactory< XBaseType >::Create(const FactoryCIt& iter) - { - return iter->second->Create(); - } - - template< class XBaseType > - template< class XDerivedType > - void KTTIFactory< XBaseType >::Register(const KTTIRegistrarBase< XBaseType >* registrar) - { - // A local (static) logger is created inside this function to avoid static initialization order problems - KTLOGGER(utillog_ti_factory_reg, "KTTIFactory-Register"); - - FactoryCIt it = fMap->find(&typeid(XDerivedType)); - if (it != fMap->end()) - { - KTERROR(utillog_ti_factory_reg, "Already have factory registered for type <" << typeid(XDerivedType).name() << ">."); - return; - } - fMap->insert(std::pair< const std::type_info*, const KTTIRegistrarBase< XBaseType >* >(&typeid(XDerivedType), registrar)); - KTDEBUG(utillog_ti_factory_reg, "Registered a factory for class type " << typeid(XDerivedType).name() << ", factory #" << fMap->size()-1); - } - - template< class XBaseType > - KTTIFactory< XBaseType >::KTTIFactory() : - fMap(new FactoryMap()) - {} - - template< class XBaseType > - KTTIFactory< XBaseType >::~KTTIFactory() - { - delete fMap; - } - - template< class XBaseType > - typename KTTIFactory< XBaseType >::FactoryCIt KTTIFactory< XBaseType >::GetFactoryMapBegin() const - { - return fMap->begin(); - } - - template< class XBaseType > - typename KTTIFactory< XBaseType >::FactoryCIt KTTIFactory< XBaseType >::GetFactoryMapEnd() const - { - return fMap->end(); - } - - - - - template< class XBaseType, class XDerivedType > - KTTIRegistrar< XBaseType, XDerivedType >::KTTIRegistrar() : - KTTIRegistrarBase< XBaseType >() - { - Register(); - } - - template< class XBaseType, class XDerivedType > - KTTIRegistrar< XBaseType, XDerivedType >::~KTTIRegistrar() - {} - - template< class XBaseType, class XDerivedType > - void KTTIRegistrar< XBaseType, XDerivedType >::Register() const - { - KTTIFactory< XBaseType >::get_instance()->template Register(this); - return; - } - - template< class XBaseType, class XDerivedType > - XBaseType* KTTIRegistrar< XBaseType, XDerivedType >::Create() const - { - return dynamic_cast< XBaseType* >(new XDerivedType()); - } - - -} /* namespace Nymph */ -#endif /* KTTIFACTORY_HH_ */ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..1cfdf450 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,12 @@ +graft Scarab +graft External +graft Cpp +graft Python +graft Testing +graft cmake +include CMakeLists.txt +include NymphConfig.cmake.in + + + + diff --git a/Python/Bindings/CMakeLists.txt b/Python/Bindings/CMakeLists.txt new file mode 100644 index 00000000..16128b86 --- /dev/null +++ b/Python/Bindings/CMakeLists.txt @@ -0,0 +1,48 @@ +# CMakeLists.txt file for Nymph/Python/Bindings +# Author: A. Ziegler +# Created on: Feb 4, 2022 + +message( "Building Python bindings") + +set( TEST_DIR Testing/Cpp ) + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/Data + ${CMAKE_CURRENT_SOURCE_DIR}/Processor +) + +# Python binding headers +set( PYBINDING_HEADERFILES + Data/DataPybind.hh + Processor/ProcessorPybind.hh + Processor/ProcessorToolboxPybind.hh + Processor/PyProcCreatorPybind.hh + Processor/PyProcRegistrar.hh + Processor/SignalPybind.hh + Processor/SlotPybind.hh +) + +# Python bindings +set( PYBINDING_SOURCEFILES + NymphPybind.cc + Processor/PyProcRegistrar.cc +) + +set( PYBINDING_PROJECT_LIBRARIES Nymph ) + +pbuilder_add_pybind11_module( + MODULE_NAME nymph_bindings + SOURCE_FILES ${PYBINDING_SOURCEFILES} + PROJECT_LIBRARIES ${PYBINDING_PROJECT_LIBRARIES} +) + +pbuilder_install_headers( ${PYBINDING_HEADERFILES} ) + +if( NOT DEFINED PBUILDER_PY_INSTALL_IN_SITELIB ) + message( STATUS "Installing add_lib_python_path.sh since python modules will not be installed in the site-package directory" ) + configure_file( add_lib_python_path.sh.in add_lib_python_path.sh @ONLY) + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/add_lib_python_path.sh DESTINATION ${BIN_INSTALL_DIR} ) +endif( NOT DEFINED PBUILDER_PY_INSTALL_IN_SITELIB ) + +message( "Done with Python bindings" ) diff --git a/Python/Bindings/Data/DataPybind.hh b/Python/Bindings/Data/DataPybind.hh new file mode 100644 index 00000000..5d1b6f3a --- /dev/null +++ b/Python/Bindings/Data/DataPybind.hh @@ -0,0 +1,38 @@ +/* + * DataPybind.hh + * + * Created on: Jun 13, 2022 + * Author: F. Thomas + */ + +#ifndef PYTHON_BINDINGS_DATA_DATAPYBIND_HH_ +#define PYTHON_BINDINGS_DATA_DATAPYBIND_HH_ + +#include +#include + +#include "Data.hh" + +namespace py = pybind11; + +namespace NymphPybind +{ + //trampoline class + class PyData : public Nymph::Data { + public: + /* Inherit the constructors */ + using Nymph::Data::Data; + + }; + + void ExportData( py::module_& nymphData) + { + py::class_< Nymph::Data, PyData, std::shared_ptr >(nymphData, "_Data") + .def(py::init< >()); + + } + +} + +#endif /* PYTHON_BINDINGS_DATA_DATAPYBIND_HH_ */ + diff --git a/Python/Bindings/NymphBindingHelpers.hh b/Python/Bindings/NymphBindingHelpers.hh new file mode 100644 index 00000000..5d47ffea --- /dev/null +++ b/Python/Bindings/NymphBindingHelpers.hh @@ -0,0 +1,15 @@ +#ifndef NYMPH_PYBIND_BINDING_HELPERS +#define NYMPH_PYBIND_BINDING_HELPERS + +#include "pybind11/iostream.h" + +#define NYMPH_BIND_CALL_GUARD_STREAMS \ + pybind11::call_guard< pybind11::scoped_ostream_redirect, pybind11::scoped_estream_redirect >() + +#define NYMPH_BIND_CALL_GUARD_GIL \ + pybind11::call_guard< pybind11::gil_scoped_release >() + +#define NYMPH_BIND_CALL_GUARD_STREAMS_AND_GIL \ + pybind11::call_guard< pybind11::scoped_ostream_redirect, pybind11::scoped_estream_redirect, pybind11::gil_scoped_release >() + +#endif /* NYMPH_PYBIND_BINDING_HELPERS */ diff --git a/Python/Bindings/NymphPybind.cc b/Python/Bindings/NymphPybind.cc new file mode 100644 index 00000000..bb077561 --- /dev/null +++ b/Python/Bindings/NymphPybind.cc @@ -0,0 +1,45 @@ +/* + * NymphPybind.cc + * + * Created on: Feb 14, 2022 + * Author: F. Thomas + */ + +#include + +#include "Data/DataPybind.hh" + +#include "Processor/ProcessorPybind.hh" +#include "Processor/ProcessorToolboxPybind.hh" +#include "Processor/PyProcCreatorPybind.hh" +#include "Processor/SignalPybind.hh" +#include "Processor/SlotPybind.hh" + +#include "DataFrame.hh" + +namespace py = pybind11; + + +PYBIND11_MODULE(nymph_bindings, nymphPackage) +{ + + nymphPackage.doc() = "Nymph package"; + + auto nymphControl = nymphPackage.def_submodule("control", "Control module"); + + auto nymphData = nymphPackage.def_submodule("data", "Data module"); + NymphPybind::ExportData(nymphData); + + auto nymphImplementation = nymphPackage.def_submodule("implementation", "Implementation module"); + + auto nymphProcessor = nymphPackage.def_submodule("processor", "Processor module"); + NymphPybind::ExportProcessor(nymphProcessor); + NymphPybind::ExportProcessorToolbox(nymphProcessor); + NymphPybind::ExportPyProcCreator(nymphProcessor); + NymphPybind::ExportSlotBase(nymphProcessor); + NymphPybind::ExportSlot(nymphProcessor, "Data"); + NymphPybind::ExportSignalBase(nymphProcessor); + NymphPybind::ExportSignal(nymphProcessor, "Data"); + + auto nymphUtility = nymphPackage.def_submodule("utility", "Utility module"); +} diff --git a/Python/Bindings/Processor/ProcessorPybind.hh b/Python/Bindings/Processor/ProcessorPybind.hh new file mode 100644 index 00000000..707f9944 --- /dev/null +++ b/Python/Bindings/Processor/ProcessorPybind.hh @@ -0,0 +1,62 @@ +/* + * ProcessorPybind.hh + * + * Created on: Feb 15, 2022 + * Author: F. Thomas + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_PROCESSORPYBIND_HH_ +#define PYTHON_BINDINGS_PROCESSOR_PROCESSORPYBIND_HH_ + +#include +#include + +#include "NymphBindingHelpers.hh" +#include "Processor.hh" + +#include "factory.hh" + +namespace py = pybind11; + +namespace NymphPybind +{ + //trampoline class + class PyProcessor : public Nymph::Processor { + public: + /* Inherit the constructors */ + using Nymph::Processor::Processor; + + /* Trampoline (need one for each virtual function) */ + void Configure( const scarab::param_node& node ) override { + PYBIND11_OVERRIDE_PURE_NAME( + void, /* Return type */ + Nymph::Processor, /* Parent class */ + "configure", /*Name of function in python*/ + Configure, /* Name of function in C++ */ + node/* Argument(s) */ + ); + } + }; + + void ExportProcessor( py::module_& nymphProcessor) + { + py::class_< Nymph::Processor, PyProcessor, std::shared_ptr >(nymphProcessor, "_Processor") + .def(py::init()) + .def("configure", &Nymph::Processor::Configure, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("connect_signal_to_slot", &Nymph::Processor::ConnectSignalToSlot, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("connect_a_slot", &Nymph::Processor::ConnectASlot, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("connect_a_signal", &Nymph::Processor::ConnectASignal, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("register_signal", &Nymph::Processor::RegisterSignal, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("register_slot", &Nymph::Processor::RegisterSlot, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("get_do_breakpoint", &Nymph::Processor::GetDoBreakpoint) + .def("set_do_breakpoint", &Nymph::Processor::SetDoBreakpoint) + .def_property("name", static_cast< const std::string& (Nymph::Processor::*)() const>(&Nymph::Processor::Name), + [](Nymph::Processor& processor, const std::string& name){processor.Name() = name;} ) + .def_property_readonly("signals", static_cast< const std::map< std::string, Nymph::SignalBase* >& (Nymph::Processor::*)() const>(&Nymph::Processor::Signals)) + .def_property_readonly("slots", static_cast< const std::map< std::string, Nymph::SlotBase* >& (Nymph::Processor::*)() const>(&Nymph::Processor::Slots)) + ; + } + +} + +#endif /* PYTHON_BINDINGS_PROCESSOR_PROCESSORPYBIND_HH_ */ diff --git a/Python/Bindings/Processor/ProcessorToolboxPybind.hh b/Python/Bindings/Processor/ProcessorToolboxPybind.hh new file mode 100644 index 00000000..d85974eb --- /dev/null +++ b/Python/Bindings/Processor/ProcessorToolboxPybind.hh @@ -0,0 +1,57 @@ +/* + * ProcessorToolboxPybind.hh + * + * Created on: Jun 14, 2022 + * Author: N.S. Oblath + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_PROCESSORTOOLBOXPYBIND_HH_ +#define PYTHON_BINDINGS_PROCESSOR_PROCESSORTOOLBOXPYBIND_HH_ + +#include "ProcessorToolbox.hh" + +#include "Processor.hh" + +#include +#include + +#include + +namespace py = pybind11; +using namespace py::literals; + +namespace NymphPybind +{ + void ExportProcessorToolbox( py::module_& nymphProcessor ) + { + py::class_< Nymph::ProcessorToolbox >( nymphProcessor, "ProcessorToolbox" ) + .def( py::init(), "name"_a="processor-toolbox" ) + .def( "configure", &Nymph::ProcessorToolbox::Configure ) + + .def( "configure_processors", &Nymph::ProcessorToolbox::ConfigureProcessors ) + .def( "get_processor", py::overload_cast< const std::string& >(&Nymph::ProcessorToolbox::GetProcessor) ) + .def( "get_processor_const", py::overload_cast< const std::string& >(&Nymph::ProcessorToolbox::GetProcessor, py::const_ ) ) + .def( "add_processor", py::overload_cast< const std::string&, std::shared_ptr >(&Nymph::ProcessorToolbox::AddProcessor) ) + .def( "add_processor", py::overload_cast< const std::string&, const std::string& >(&Nymph::ProcessorToolbox::AddProcessor) ) + .def( "has_processor", &Nymph::ProcessorToolbox::HasProcessor ) + .def( "could_build", &Nymph::ProcessorToolbox::CouldBuild ) + .def( "remove_processor", &Nymph::ProcessorToolbox::RemoveProcessor ) + .def( "release_processor", &Nymph::ProcessorToolbox::ReleaseProcessor ) + .def( "clear_processors", &Nymph::ProcessorToolbox::ClearProcessors ) + + .def( "configure_connections", &Nymph::ProcessorToolbox::ConfigureConnections ) + .def( "make_connection", py::overload_cast< const std::string&, const std::string&, int >(&Nymph::ProcessorToolbox::MakeConnection) ) + .def( "make_connection", py::overload_cast< const std::string&, const std::string& >(&Nymph::ProcessorToolbox::MakeConnection) ) + .def( "make_connection", py::overload_cast< const std::string&, const std::string&, const std::string&, const std::string&, int >(&Nymph::ProcessorToolbox::MakeConnection) ) + .def( "make_connection", py::overload_cast< const std::string&, const std::string&, const std::string&, const std::string& >(&Nymph::ProcessorToolbox::MakeConnection) ) + + .def( "set_breakpoint", py::overload_cast< const std::string& >(&Nymph::ProcessorToolbox::SetBreakpoint) ) // + .def( "set_breakpoint", py::overload_cast< const std::string&, const std::string& >(&Nymph::ProcessorToolbox::SetBreakpoint) ) // + .def( "remove_breakpoint", py::overload_cast< const std::string& >(&Nymph::ProcessorToolbox::RemoveBreakpoint) ) // + .def( "remove_breakpoint", py::overload_cast< const std::string&, const std::string& >(&Nymph::ProcessorToolbox::RemoveBreakpoint) ) // + ; + } + +} + +#endif /* PYTHON_BINDINGS_PROCESSOR_PROCESSORTOOLBOXPYBIND_HH_ */ diff --git a/Python/Bindings/Processor/PyProcCreatorPybind.hh b/Python/Bindings/Processor/PyProcCreatorPybind.hh new file mode 100644 index 00000000..5a2c9d44 --- /dev/null +++ b/Python/Bindings/Processor/PyProcCreatorPybind.hh @@ -0,0 +1,32 @@ +/* + * PyProcCreatorPybind.hh + * + * Created on: Jun 13, 2022 + * Author: N.S. Oblath + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_PYPROCCREATORPYBIND_HH_ +#define PYTHON_BINDINGS_PROCESSOR_PYPROCCREATORPYBIND_HH_ + +#include "NymphBindingHelpers.hh" +#include "PyProcRegistrar.hh" + +#include + +namespace py = pybind11; + +namespace NymphPybind +{ + void ExportPyProcCreator( py::module_& nymphProcessor ) + { + + py::class_< Nymph::PyProcRegistrar >(nymphProcessor, "_PyProcRegistrar") + .def(py::init< const std::string&, const std::string&, const std::string& >()) + .def("create_python_processor", &Nymph::PyProcRegistrar::CreatePyProc); + + nymphProcessor.def("_register", &Nymph::RegisterPyProcessor, NYMPH_BIND_CALL_GUARD_STREAMS); + nymphProcessor.def("_create", &Nymph::CreatePyProcessor, NYMPH_BIND_CALL_GUARD_STREAMS); + } +} + +#endif /* PYTHON_BINDINGS_PROCESSOR_PYPROCCREATORPYBIND_HH_ */ diff --git a/Python/Bindings/Processor/PyProcRegistrar.cc b/Python/Bindings/Processor/PyProcRegistrar.cc new file mode 100644 index 00000000..29a719b9 --- /dev/null +++ b/Python/Bindings/Processor/PyProcRegistrar.cc @@ -0,0 +1,82 @@ +/* + * PyProcRegistrar.cc + * + * Created on: Sep 24, 2025 + * Author: N.S. Oblath + */ + +#include "PyProcRegistrar.hh" +#include "ProcessorToolbox.hh" + +#include "Exception.hh" + +#include "logger.hh" + +#include + +#include + +//#include + +LOGGER( prlog, "PyProcRegistrar.hh" ); + +namespace py = pybind11; + +namespace Nymph +{ + PyProcCreator::PyProcCreator( const std::string& name ) : + Processor( name ) + {} + + PyProcCreator::~PyProcCreator() + {} + + void PyProcCreator::Configure( const scarab::param_node& ) + {} + + + PyProcRegistrar::PyProcRegistrar( const std::string& module, const std::string& type, const std::string& typeName ) : + ProcessorRegistrar< PyProcCreator, const std::string& >( typeName ), + fType( type ), + fModule( module ) + {} + + PyProcRegistrar::~PyProcRegistrar() {} + + std::shared_ptr PyProcRegistrar::CreatePyProc( const std::string& name ) const + { + std::string useModule( fModule ); + if( useModule.empty() ) useModule = "__main__"; + py::object scope = py::module_::import( useModule.c_str() ).attr( "__dict__" ); + std::shared_ptr proc = py::eval( fType + "(\'" + name + "\')", scope ).cast< std::shared_ptr >(); + return proc; + } + + std::unique_ptr RegisterPyProcessor( const std::string& module, const std::string& type, const std::string& typeName ) + { + return std::make_unique( module, type, typeName ); + } + + std::shared_ptr< Nymph::Processor > CreatePyProcessor( const std::string& typeName, const std::string& name ) + { + auto factory = ProcessorToolbox::GetProcFactory(); + //std::stringstream t_list_ss; + //for( auto it = factory->begin(); it != factory->end(); ++it ) + //{ + // t_list_ss << it->first << '\n'; + //} + //LWARN( prlog, "Available processors at " << factory << ": " << t_list_ss.str() ); + + if( ! factory->has_class( typeName ) ) + { + THROW_EXCEPT_HERE( Nymph::Exception() << "Did not find processor with type <" << typeName << ">" ); + } + const PyProcRegistrar* ppReg = dynamic_cast< const PyProcRegistrar* >( factory->get_registrar( typeName ) ); + if( ! ppReg ) + { + THROW_EXCEPT_HERE( Nymph::Exception() << "Registrar did not cast correctly for <" << typeName << ">" ); + } + return ppReg->CreatePyProc( name ); + } + +} diff --git a/Python/Bindings/Processor/PyProcRegistrar.hh b/Python/Bindings/Processor/PyProcRegistrar.hh new file mode 100644 index 00000000..da7fbcec --- /dev/null +++ b/Python/Bindings/Processor/PyProcRegistrar.hh @@ -0,0 +1,51 @@ +/* + * PyProcRegistrar.hh + * + * Created on: Jun 13, 2022 + * Author: N.S. Oblath + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_PYPROCREGISTRAR_HH_ +#define PYTHON_BINDINGS_PROCESSOR_PYPROCREGISTRAR_HH_ + +#include "ProcessorRegistrar.hh" +#include "Processor.hh" + +#include "Exception.hh" + +#include + + +namespace Nymph +{ + class PyProcCreator : public Processor + { + public: + PyProcCreator( const std::string& name = "test" ); + + virtual ~PyProcCreator(); + + void Configure( const scarab::param_node& ); + + }; + + class PyProcRegistrar : public ProcessorRegistrar< PyProcCreator, const std::string& > + { + public: + PyProcRegistrar( const std::string& module, const std::string& type, const std::string& typeName ); + virtual ~PyProcRegistrar(); + + std::shared_ptr CreatePyProc( const std::string& name ) const; + + protected: + std::string fType; + std::string fModule; + }; + + std::unique_ptr RegisterPyProcessor( const std::string& module, const std::string& type, const std::string& typeName ); + std::shared_ptr< Nymph::Processor > CreatePyProcessor( const std::string& typeName, const std::string& name ); + +} + + +#endif /* PYTHON_BINDINGS_PROCESSOR_PYPROCREGISTRAR_HH_ */ diff --git a/Python/Bindings/Processor/SignalPybind.hh b/Python/Bindings/Processor/SignalPybind.hh new file mode 100644 index 00000000..ec38aafc --- /dev/null +++ b/Python/Bindings/Processor/SignalPybind.hh @@ -0,0 +1,76 @@ +/* + * SignalPybind.hh + * + * Created on: Jun 13, 2022 + * Author: F. Thomas + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_SIGNALPYBIND_HH_ +#define PYTHON_BINDINGS_PROCESSOR_SIGNALPYBIND_HH_ + +#include +#include + +#include "NymphBindingHelpers.hh" +#include "SignalSlotBase.hh" +#include "Signal.hh" +#include "Slot.hh" +#include "Processor.hh" + +namespace py = pybind11; + +namespace NymphPybind +{ + + //trampoline class + class PySignalBase : public Nymph::SignalBase { + public: + /* Inherit the constructors */ + using Nymph::SignalBase::SignalBase; + + /* Trampoline (need one for each virtual function) */ + void Connect( Nymph::SlotBase* slot, int group = -1 ) override { + PYBIND11_OVERRIDE_PURE_NAME( + void, /* Return type */ + Nymph::SignalBase, /* Parent class */ + "connect", /* Name of function in python */ + Connect, /* Name of function in C++ */ + slot, + group/* Argument(s) */ + ); + } + }; + + void ExportSignalBase( py::module_& nymphProcessor) + { + + py::class_< Nymph::SignalBase, PySignalBase, std::shared_ptr >(nymphProcessor, "_SignalBase") + .def(py::init()) + .def(py::init()) + .def("connect", &Nymph::SignalBase::Connect, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("disconnect", &Nymph::SignalBase::Disconnect) + .def("disconnect_all", &Nymph::SignalBase::DisconnectAll) + .def_property_readonly("connections", static_cast< std::set< Nymph::SlotBase* >& (Nymph::SignalBase::*)() const>(&Nymph::SignalBase::Connections)) + .def_property("name", static_cast< const std::string& (Nymph::SignalBase::*)() const>(&Nymph::SignalBase::Name), + [](Nymph::SignalBase& signal, const std::string& name){signal.Name() = name;} ) + .def_property("do_breakpoint", &Nymph::SignalBase::GetDoBreakpoint, &Nymph::SignalBase::SetDoBreakpoint); + + } + + template< typename... XArgs > + void ExportSignal( py::module_& nymphProcessor, const std::string& typestr) + { + std::string pyClsName = std::string("_Signal") + typestr; + + py::class_< Nymph::Signal< XArgs... >, Nymph::SignalBase, std::shared_ptr > >(nymphProcessor, pyClsName.c_str()) + .def(py::init()) + .def(py::init()) + .def("emit", &Nymph::Signal< XArgs... >::Emit, NYMPH_BIND_CALL_GUARD_STREAMS) + .def("__call__", &Nymph::Signal< XArgs... >::operator(), NYMPH_BIND_CALL_GUARD_STREAMS); + + } + +} + + +#endif /* PYTHON_BINDINGS_PROCESSOR_SIGNALPYBIND_HH_ */ diff --git a/Python/Bindings/Processor/SlotPybind.hh b/Python/Bindings/Processor/SlotPybind.hh new file mode 100644 index 00000000..e0895a15 --- /dev/null +++ b/Python/Bindings/Processor/SlotPybind.hh @@ -0,0 +1,92 @@ +/* + * SlotPybind.hh + * + * Created on: Jun 15, 2022 + * Author: F. Thomas + */ + +#ifndef PYTHON_BINDINGS_PROCESSOR_SLOTPYBIND_HH_ +#define PYTHON_BINDINGS_PROCESSOR_SLOTPYBIND_HH_ + +#include +#include + +#include + +#include "SignalSlotBase.hh" +#include "Processor.hh" + +namespace py = pybind11; + +namespace NymphPybind +{ + + //trampoline class + class PySlotBase : public Nymph::SlotBase { + public: + /* Inherit the constructors */ + using Nymph::SlotBase::SlotBase; + + /* Trampoline (need one for each virtual function) */ + void ConnectTo( Nymph::SignalBase* signal, int group = -1 ) override { + PYBIND11_OVERRIDE_PURE_NAME( + void, /* Return type */ + Nymph::SlotBase, /* Parent class */ + "connect_to", /* Name of function in python */ + ConnectTo, /* Name of function in C++ */ + signal, + group/* Argument(s) */ + ); + } + + /* Trampoline (need one for each virtual function) */ + bool MatchesTo( Nymph::SignalBase* signal ) override { + PYBIND11_OVERRIDE_PURE_NAME( + bool, /* Return type */ + Nymph::SlotBase, /* Parent class */ + "matches_to", /* Name of function in python */ + MatchesTo, /* Name of function in C++ */ + signal/* Argument(s) */ + ); + } + }; + + void ExportSlotBase( py::module_& nymphProcessor) + { + + py::class_< Nymph::SlotBase, PySlotBase, std::shared_ptr >(nymphProcessor, "_SlotBase") + .def(py::init()) + .def(py::init()) + + .def("connect_to", &Nymph::SlotBase::ConnectTo) + .def("matches_to", &Nymph::SlotBase::MatchesTo) + .def("disconnect", &Nymph::SlotBase::Disconnect) + .def("disconnect_all", &Nymph::SlotBase::DisconnectAll) + .def_property("connections", static_cast< std::set< Nymph::SignalBase* >& (Nymph::SlotBase::*)() const>(&Nymph::SlotBase::Connections), + [](Nymph::SlotBase& slot, const std::set< Nymph::SignalBase* >& signals){slot.Connections() = signals;} ) + .def_property("name", static_cast< const std::string& (Nymph::SlotBase::*)() const>(&Nymph::SlotBase::Name), + [](Nymph::SlotBase& slot, const std::string& name){slot.Name() = name;} ); + + + } + + + template< typename... XArgs > + void ExportSlot( py::module_& nymphProcessor, const std::string& typestr) + { + std::string pyClsName = std::string("_Slot") + typestr; + + using signature = void( XArgs... ); + + py::class_< Nymph::Slot< XArgs... >, Nymph::SlotBase, std::shared_ptr > >(nymphProcessor, pyClsName.c_str()) + .def(py::init& >()) + .def(py::init& >()) + .def("__call__", &Nymph::Slot< XArgs... >::operator()) + .def_property("function", static_cast< const std::function< signature >& (Nymph::Slot< XArgs... >::*)() const>(&Nymph::Slot< XArgs... >::Function), + [](Nymph::Slot< XArgs... >& slot, const std::function< signature >& function){slot.Function() = function;} ); + } + +} + + +#endif /* PYTHON_BINDINGS_PROCESSOR_SLOTPYBIND_HH_ */ diff --git a/Python/Bindings/add_lib_python_path.sh.in b/Python/Bindings/add_lib_python_path.sh.in new file mode 100644 index 00000000..68aff4b8 --- /dev/null +++ b/Python/Bindings/add_lib_python_path.sh.in @@ -0,0 +1,3 @@ +# Adds the lib install directory to the python path + +export PYTHONPATH=@LIB_INSTALL_DIR@:$PYTHONPATH diff --git a/Python/README.md b/Python/README.md new file mode 100644 index 00000000..a89ff8f3 --- /dev/null +++ b/Python/README.md @@ -0,0 +1,57 @@ +Nymph +===== + +Nymph is a Python extension of the C++-based Nymph data analysis framework. + + +Dependencies +------------ + +- CMake (3.12 or better) +- Boost (date_time, filesystem, program_options, system, thread; 1.46 or better) +- RapidJSON (optional; required for JSON processing) +- yaml-cpp (optional; required for YAML processing) + + +Operating System Support +------------------------ + +* Mac OS X (usually tested on OS X 10.10) +* Linux (usually tested on Ubuntu) + + +Directory Structure +------------------- + +* nymph - nymph modules. + +Installing +---------- +To build the python package use + +python -m build + +To install and build the python package use + +pip install . + +in the directory where the setup.py script is located. + +Instructions for Use +-------------------- + + +Documentation +------------- + + + +Development +----------- + +The Git workflow used is git-flow: +* http://nvie.com/posts/a-successful-git-branching-model/ +We suggest that you use the aptly-named git extension, git-flow, which is available from commonly-used package managers: +* https://github.com/nvie/gitflow + +Issues should be posted via [GitHub](https://github.com/project8/nymph/issues). \ No newline at end of file diff --git a/Python/nymph/__init__.py b/Python/nymph/__init__.py new file mode 100644 index 00000000..dec3f0a6 --- /dev/null +++ b/Python/nymph/__init__.py @@ -0,0 +1,5 @@ + +from . import control +from . import implementation +from . import processor +from . import utility diff --git a/Python/nymph/control/__init__.py b/Python/nymph/control/__init__.py new file mode 100644 index 00000000..9220437c --- /dev/null +++ b/Python/nymph/control/__init__.py @@ -0,0 +1,2 @@ + +from nymph_bindings.control import * \ No newline at end of file diff --git a/Python/nymph/implementation/__init__.py b/Python/nymph/implementation/__init__.py new file mode 100644 index 00000000..cc0ca9e8 --- /dev/null +++ b/Python/nymph/implementation/__init__.py @@ -0,0 +1,2 @@ + +from nymph_bindings.implementation import * \ No newline at end of file diff --git a/Python/nymph/processor/__init__.py b/Python/nymph/processor/__init__.py new file mode 100644 index 00000000..fd92857a --- /dev/null +++ b/Python/nymph/processor/__init__.py @@ -0,0 +1,8 @@ + +from nymph_bindings.processor import * + +from .helloworldproc import * +from .processor import * +from .registrar import * + +register("nymph.processor", "HelloWorldPython", "hello-world-py") diff --git a/Python/nymph/processor/helloworldproc.py b/Python/nymph/processor/helloworldproc.py new file mode 100644 index 00000000..f1aaf651 --- /dev/null +++ b/Python/nymph/processor/helloworldproc.py @@ -0,0 +1,15 @@ +''' +Test Processor to demonstrate basic Python-based Nymph capabilities +''' + +from . import processor + +class HelloWorldPython( processor.Processor ): + def __init__( self, name ): + processor.Processor.__init__( self, name ) + + def configure( self, config ): + pass + + def say_hello(): + print( "Hello, world (python)" ) diff --git a/Python/nymph/processor/processor.py b/Python/nymph/processor/processor.py new file mode 100644 index 00000000..e58d992f --- /dev/null +++ b/Python/nymph/processor/processor.py @@ -0,0 +1,20 @@ +''' +The file contains the base class for Python processors +''' + +from nymph_bindings.processor import _Processor + + +class Processor(_Processor): + ''' + Base class for all Python processors + ''' + def __init__(self, name: str): + ''' + Initialize things + ''' + _Processor.__init__(self, name) + print(f"Initializing python processor {name}") + + def configure(self, config): + raise NotImplementedError( "Configure function has not been implemented for this processor" ) diff --git a/Python/nymph/processor/registrar.py b/Python/nymph/processor/registrar.py new file mode 100644 index 00000000..a5e71844 --- /dev/null +++ b/Python/nymph/processor/registrar.py @@ -0,0 +1,15 @@ + + +#from nymph import registrars +from nymph_bindings import processor as nb_proc + +registrars = {} + +def register(module_name: str, type: str, type_name: str): + print(f"Registring {module_name}.{type} under {type_name}") + registrars[type_name] = nb_proc._register(module_name, type, type_name) + + +def create(type_name: str, name: str): + print(f"Creating processor of type {type_name} called {name}") + return nb_proc._create(type_name, name); diff --git a/Python/nymph/utility/__init__.py b/Python/nymph/utility/__init__.py new file mode 100644 index 00000000..649c794d --- /dev/null +++ b/Python/nymph/utility/__init__.py @@ -0,0 +1,2 @@ + +from nymph_bindings.utility import * diff --git a/README.md b/README.md index 455147ee..43ccbae6 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ Installing ---------- +Testing +------- + +Automatic unit testing functionality is available through the use of github actions. See .github/workflows. C++ version uses [catch2](https://github.com/catchorg/Catch2) and python version uses [unittest](https://docs.python.org/3/library/unittest.html). In the python version, the files starting with names `test` are automatically `discovered`. + + Instructions for Use -------------------- diff --git a/Scarab b/Scarab index 2b140c45..30b06247 160000 --- a/Scarab +++ b/Scarab @@ -1 +1 @@ -Subproject commit 2b140c456c06cfd9052dbb90a44f133cbe437eca +Subproject commit 30b06247304fe6606761f8fd33e62e81bee16be8 diff --git a/Testing/CMakeLists.txt b/Testing/CMakeLists.txt new file mode 100644 index 00000000..a2c268dc --- /dev/null +++ b/Testing/CMakeLists.txt @@ -0,0 +1,138 @@ +# CMakeLists for Nymph/Testing +# Author: N.S. Oblath + +include( FetchContent ) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.7.1 +) + +FetchContent_MakeAvailable( Catch2 ) +list( APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras ) + +pbuilder_component_install_and_export( + COMPONENT Catch2 + LIBTARGETS Catch2 + NAMESPACE Catch2:: +) + +######################################################################## + +set( PY_DIR Python ) +set( CPP_DIR Cpp ) + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR}/${PY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${CPP_DIR} +) + +#set (pytesting_CONFIGFILES +# ${PY_DIR}/ConfigFiles/TestConfigurator.json +#) + +#pbuilder_install_config (${pytesting_CONFIGFILES}) + +set( testing_lib_HEADERS + ${CPP_DIR}/TestControllerClasses.hh + ${CPP_DIR}/TestDataClasses.hh + ${CPP_DIR}/TestDataProcessorClasses.hh + ${CPP_DIR}/TestProcessorClasses.hh + ${CPP_DIR}/TestServiceClasses.hh +) + +set( testing_lib_SOURCES + ${CPP_DIR}/TestProcessorClasses.cc + ${CPP_DIR}/TestServiceClasses.cc +) + +set( testing_lib_LIB_DEPENDENCIES + Nymph +) + +pbuilder_library( + TARGET NymphTesting + SOURCES ${testing_lib_SOURCES} + PUBLIC_EXTERNAL_LIBRARIES ${testing_lib_LIB_DEPENDENCIES} +) + +pbuilder_component_install_and_export( + COMPONENT Library + LIBTARGETS NymphTesting +) + +pbuilder_install_headers( ${testing_lib_HEADERS} ) + +set( testing_SOURCES + ${CPP_DIR}/TestConfigException.cc + ${CPP_DIR}/TestControlAccess.cc + ${CPP_DIR}/TestController.cc + ${CPP_DIR}/TestData.cc + ${CPP_DIR}/TestDataFrame.cc + ${CPP_DIR}/TestException.cc + ${CPP_DIR}/TestPrimaryProcessor.cc + ${CPP_DIR}/TestProcessor.cc + ${CPP_DIR}/TestProcessorToolbox.cc + ${CPP_DIR}/TestProcessorVisibility.cc + ${CPP_DIR}/TestReturnBuffer.cc + ${CPP_DIR}/TestRunNymph.cc + ${CPP_DIR}/TestService.cc + ${CPP_DIR}/TestServiceToolbox.cc + ${CPP_DIR}/TestSignalSlot.cc + ${CPP_DIR}/TestSingleRunController.cc + ${CPP_DIR}/TestSlotData.cc + ${CPP_DIR}/UseCatch.cc +) + +set( testing_LIB_DEPENDENCIES + Nymph + NymphTesting +) + +set( testing_PUBLIC_LIBS Catch2::Catch2 ) + +if( Nymph_ENABLE_PYTHON ) + set( testing_PUBLIC_LIBS + ${testing_PUBLIC_LIBS} + pybind11::embed + ) +endif( Nymph_ENABLE_PYTHON ) + +# Interface library was recommended for Catch: +# https://stackoverflow.com/questions/34896891/catch-lib-unit-testing-and-ctest-cmake-integration/34900012#34900012 +#add_library( Catch INTERFACE ) +#target_include_directories( Catch INTERFACE ${Scarab_LOCATION}/testing/catch ) + +pbuilder_executable( + EXECUTABLE RunTests + SOURCES ${testing_SOURCES} + PROJECT_LIBRARIES ${testing_LIB_DEPENDENCIES} + PUBLIC_EXTERNAL_LIBRARIES ${testing_PUBLIC_LIBS} +) +set( programs "RunTests" ) + +pbuilder_component_install_and_export( + COMPONENT Executables + EXETARGETS ${programs} +) + +# So far it seems sufficient to use Catch2's ability to run tests, and not CTest. +# Here are two ways it could potentially be integrated with CTest, if we change our minds. + +# Normal CMake Integration +#enable_testing() +#add_test(NAME test_test COMMAND run_tests ) + +# Automated CMake Integration +#include(CTest) +#include(Catch) +#catch_discover_tests(foo) + +############################ +# NymphTesting Python library +############################ + +if( Nymph_ENABLE_PYTHON ) + add_subdirectory( Python/Bindings ) +endif() diff --git a/Testing/Cpp/TestConfigException.cc b/Testing/Cpp/TestConfigException.cc new file mode 100644 index 00000000..8e602a0b --- /dev/null +++ b/Testing/Cpp/TestConfigException.cc @@ -0,0 +1,40 @@ +/* + * TestConfigException.cc + * + * Created on: Jun 13, 2022 + * Author: N.S. Oblath + */ + +#include "ConfigException.hh" + +#include "param_codec.hh" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_string.hpp" + + +TEST_CASE( "config_exception", "[exception],[utility]" ) +{ + using namespace Nymph; + + std::string tceConfigStr( + "- type: test-primary\n" + " name: testprimary-1\n" + "- type: test-primary\n" + " name: testprimary-2\n" + "- type: test-proc\n" + " name: testproc-1\n" + ); + + scarab::param_translator tceTranslator; + auto tceConfig = tceTranslator.read_string( tceConfigStr, "yaml" ); + + std::string tceConfigAsString = tceConfig->to_string(); + + ConfigException tceException = ConfigException( *tceConfig ); + std::string tceExWhat( tceException.what() ); + + using Catch::Matchers::EndsWith; + + REQUIRE_THAT( tceExWhat, EndsWith(tceConfigAsString) ); +} diff --git a/Testing/Cpp/TestControlAccess.cc b/Testing/Cpp/TestControlAccess.cc new file mode 100644 index 00000000..e961a3a5 --- /dev/null +++ b/Testing/Cpp/TestControlAccess.cc @@ -0,0 +1,114 @@ +/* + * TestControlAccesss.cc + * + * Created on: Sept 15, 2019 + * Author: N.S. Oblath + */ + + +#include "ControlAccess.hh" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/catch_approx.hpp" + +#include + +TEST_CASE( "control_access", "[control]" ) +{ + using namespace Nymph; + + ControlAccess* tcaControlAccess = ControlAccess::get_instance(); + + SECTION( "no_controller" ) + { + REQUIRE_FALSE( tcaControlAccess->GetControl() ); + REQUIRE_THROWS_AS( tcaControlAccess->WaitToContinue(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->WaitForBreakOrCanceled(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->WaitForEndOfRun(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->Continue(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->Cancel(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->IsCanceled(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->Break(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->IsAtBreak(), Exception ); + REQUIRE_THROWS_AS( tcaControlAccess->ChainIsQuitting( "blah" ), Exception ); + } + + Controller tcaControl; + + SECTION( "Basics" ) + { + REQUIRE_FALSE( tcaControlAccess->IsCanceled() ); + tcaControlAccess->Cancel( 5 ); + REQUIRE( tcaControlAccess->IsCanceled() ); + } + + SECTION( "WaitToContinue" ) + { + REQUIRE( tcaControlAccess->WaitToContinue() ); + } + + SECTION( "WaitForBreakOrCanceled" ) + { + tcaControlAccess->Break(); + REQUIRE( tcaControlAccess->WaitForBreakOrCanceled() == true ); + } + + SECTION( "BreakContinue" ) + { + REQUIRE_FALSE( tcaControlAccess->IsCanceled() ); + + // This thread will do the breaking and continuing + std::thread thread( [&](){ + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcaControlAccess->Break(); // step 1 + + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcaControlAccess->Continue(); // step 2 + + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcaControlAccess->Cancel( 0 ); // step 3 + } ); + + // Waiting for break (step 1) + REQUIRE( tcaControlAccess->WaitForBreakOrCanceled() ); + REQUIRE_FALSE( tcaControlAccess->IsCanceled() ); + + // Waiting for continue (step 2) + tcaControlAccess->WaitToContinue(); + REQUIRE_FALSE( tcaControlAccess->IsCanceled() ); + + // Waiting for cancelation (step 3) + REQUIRE_FALSE( tcaControlAccess->WaitForBreakOrCanceled() ); + REQUIRE( tcaControlAccess->IsCanceled() ); + + thread.join(); + + } + + SECTION( "ChainIsQuitting" ) + { + // As if an error is thrown + std::exception_ptr exceptPtr = std::make_exception_ptr( Exception() << "Test: ChainIsQuitting, throwing Exception" ); + REQUIRE_NOTHROW( tcaControlAccess->ChainIsQuitting( "TestController::ChainIsQuitting::Exception", exceptPtr ) ); + } + + SECTION( "ReturnBuffer" ) + { + REQUIRE_FALSE( tcaControlAccess->HasReturn() ); + REQUIRE_THROWS_AS( tcaControlAccess->GetReturn(), Exception ); // have to pick a return argument, so I just picked in this case; it could be anything for this test + double retval = 5; + auto retBuf = tcaControlAccess->BreakAndReturn(retval); + // check that we're now at a break point + REQUIRE( tcaControlAccess->IsAtBreak() ); + REQUIRE( tcaControlAccess->HasReturn() ); + // we can tcaControlAccess the return variable through the buffer + REQUIRE( std::get<0>( retBuf ) == Catch::Approx(5.) ); + // we can change the value of the return variable using the buffer + std::get<0>( retBuf ) = 10.; + REQUIRE( retval == Catch::Approx(10.) ); + } + +} \ No newline at end of file diff --git a/Testing/Cpp/TestController.cc b/Testing/Cpp/TestController.cc new file mode 100644 index 00000000..0f120163 --- /dev/null +++ b/Testing/Cpp/TestController.cc @@ -0,0 +1,185 @@ +/* + * TestController.cc + * + * Created on: Jan 21, 2022 + * Author: N.S. Oblath + */ + + +#include "Controller.hh" + +#include "Exception.hh" +#include "QuitChain.hh" + +#include "param_codec.hh" + +#include + +#include "catch2/catch_test_macros.hpp" +#include "catch2/catch_approx.hpp" + +namespace NymphTesting +{ + class ControllerRevealer : public Nymph::Controller + { + public: + using Nymph::Controller::Controller; + virtual ~ControllerRevealer() {} + + void SetBreakFlag( bool flag ) + { + fBreakFlag = flag; + return; + } + + void SetCanceled( bool flag ) + { + f_canceled.store( flag ); + return; + } + + void do_cancellation( int code ) + { + Controller::do_cancellation( code ); + return; + } + }; +} + +// unit tests +TEST_CASE( "controller", "[control]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + ControllerRevealer tcControl; + + SECTION( "Basics" ) + { + tcControl.SetCycleTimeMS( 100 ); + REQUIRE( tcControl.GetCycleTimeMS() == 100 ); + tcControl.SetCycleTimeMS( 500 ); + + REQUIRE_NOTHROW( tcControl.Mutex().lock() ); + REQUIRE_NOTHROW( tcControl.Mutex().unlock() ); + + REQUIRE_FALSE( tcControl.GetBreakFlag() ); + + REQUIRE_FALSE( tcControl.IsCanceled() ); + tcControl.Cancel( 5 ); + REQUIRE( tcControl.IsCanceled() ); + } + + SECTION( "Configure" ) + { + std::string config_str( + "cycle-time-ms: 10\n" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tcControl.Configure( config->as_node() ) ); + REQUIRE( tcControl.GetCycleTimeMS() == 10 ); + } + + SECTION( "WaitToContinue" ) + { + tcControl.SetCanceled( false ); + tcControl.SetBreakFlag( false ); + REQUIRE( tcControl.WaitToContinue() ); + + tcControl.SetBreakFlag( true ); + tcControl.SetCanceled( true ); + REQUIRE_FALSE( tcControl.WaitToContinue() ); + } + + SECTION( "WaitForBreakOrCanceled" ) + { + tcControl.SetCanceled( false ); + tcControl.SetBreakFlag( true ); + REQUIRE( tcControl.WaitForBreakOrCanceled() == tcControl.GetBreakFlag() ); + + tcControl.SetCanceled( true ); + tcControl.SetBreakFlag( false ); + REQUIRE_FALSE( tcControl.WaitForBreakOrCanceled() ); + + + } + + SECTION( "BreakContinue" ) + { + REQUIRE_FALSE( tcControl.GetBreakFlag() ); + REQUIRE_FALSE( tcControl.IsCanceled() ); + + // This thread will do the breaking and continuing + std::thread thread( [&](){ + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcControl.Break(); // step 1 + + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcControl.Continue(); // step 2 + + std::this_thread::sleep_for( std::chrono::milliseconds(1000) ); + + tcControl.Cancel( 0 ); // step 3 + } ); + + // Waiting for break (step 1) + REQUIRE( tcControl.WaitForBreakOrCanceled() ); + REQUIRE( tcControl.GetBreakFlag() ); + REQUIRE_FALSE( tcControl.IsCanceled() ); + + // Waiting for continue (step 2) + tcControl.WaitToContinue(); + REQUIRE_FALSE( tcControl.GetBreakFlag() ); + REQUIRE_FALSE( tcControl.IsCanceled() ); + + // Waiting for cancelation (step 3) + REQUIRE_FALSE( tcControl.WaitForBreakOrCanceled() ); + REQUIRE_FALSE( tcControl.GetBreakFlag() ); + REQUIRE( tcControl.IsCanceled() ); + + thread.join(); + + } + + SECTION( "ChainIsQuitting" ) + { + // As if an error is thrown + std::exception_ptr exceptPtr = std::make_exception_ptr( Exception() << "Test: ChainIsQuitting, throwing Exception" ); + REQUIRE_NOTHROW( tcControl.ChainIsQuitting( "TestController::ChainIsQuitting::Exception", exceptPtr ) ); + + tcControl.reset_cancel(); + + // As if QuitChain is thrown + std::exception_ptr qcPtr = std::make_exception_ptr( QuitChain() << "Test: ChainIsQuitting, throwing QuitChain" ); + REQUIRE_NOTHROW( tcControl.ChainIsQuitting( "TestController::ChainIsQuitting::QuitChain", qcPtr ) ); + + tcControl.reset_cancel(); + + // As if std::runtime_error is thrown; not handled by ChainIsQuitting() + std::exception_ptr runtimePtr = std::make_exception_ptr( std::runtime_error("Test: ChainIsQuitting, throwing std::runtime_error") ); + REQUIRE_THROWS_AS( tcControl.ChainIsQuitting( "TestController::ChainIsQuitting::QuitChain", runtimePtr ), std::runtime_error ); + + } + + SECTION( "ReturnBuffer" ) + { + REQUIRE_FALSE( tcControl.HasReturn() ); + REQUIRE_THROWS_AS( tcControl.GetReturn(), Exception ); // have to pick a return argument, so I just picked in this case; it could be anything for this test + double retval = 5; + auto retBuf = tcControl.BreakAndReturn(retval); + // check that we're now at a break point + REQUIRE( tcControl.IsAtBreak() ); + REQUIRE( tcControl.HasReturn() ); + // we can access the return variable through the buffer + REQUIRE( std::get<0>( retBuf ) == Catch::Approx(5.) ); + // we can change the value of the return variable using the buffer + std::get<0>( retBuf ) = 10.; + REQUIRE( retval == Catch::Approx(10.) ); + } + +} diff --git a/Testing/Cpp/TestControllerClasses.hh b/Testing/Cpp/TestControllerClasses.hh new file mode 100644 index 00000000..80b40e2b --- /dev/null +++ b/Testing/Cpp/TestControllerClasses.hh @@ -0,0 +1,36 @@ +/* + * TestControllerClasses.hh + * + * Created on: Jan 24, 2022 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_TESTING_TESTCONTROLLERCLASSES_HH +#define NYMPH_TESTING_TESTCONTROLLERCLASSES_HH + +#include "Controller.hh" + +namespace NymphTesting +{ + + class CIQThrowController : public Nymph::Controller + { + + public: + using Controller::Controller; + virtual ~CIQThrowController() {} + + virtual void ChainIsQuitting( const std::string& name, std::exception_ptr ePtr = std::exception_ptr() ) + { + if( ePtr ) + { + std::rethrow_exception( ePtr ); + } + return; + } + + }; + +} /* namespace Nymph */ + +#endif /* NYMPH_TESTING_TESTCONTROLLERCLASSES_HH */ diff --git a/Testing/Cpp/TestData.cc b/Testing/Cpp/TestData.cc new file mode 100644 index 00000000..768c07d5 --- /dev/null +++ b/Testing/Cpp/TestData.cc @@ -0,0 +1,30 @@ +/* + * TestData.cc + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#include "TestDataClasses.hh" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/catch_approx.hpp" + + +TEST_CASE( "data", "[data]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + TestData1 tdData1; + REQUIRE( tdData1.GetIValue1() == 0 ); + REQUIRE( tdData1.GetIValue2() == 5 ); + + tdData1.SetIValue1( 50 ); + REQUIRE( tdData1.GetIValue1() == 50 ); + + TestData2 tdData2; + REQUIRE( tdData2.GetDValue1() == Catch::Approx( 0.0 ) ); + REQUIRE( tdData2.GetDValue2() == Catch::Approx( 10.0 ) ); + +} diff --git a/Testing/Cpp/TestDataClasses.hh b/Testing/Cpp/TestDataClasses.hh new file mode 100644 index 00000000..a36a9221 --- /dev/null +++ b/Testing/Cpp/TestDataClasses.hh @@ -0,0 +1,51 @@ +/* + * TestDataClasses.hh + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_TESTING_TESTDATACLASSES +#define NYMPH_TESTING_TESTDATACLASSES + +#include "Data.hh" +#include "MemberVariable.hh" + +namespace NymphTesting +{ + class TestData1 : public Nymph::Data + { + public: + TestData1() : + Data(), + fIValue1( 0 ), + fIValue2( 5 ) + {} + + virtual ~TestData1() + {} + + MEMVAR( int, IValue1 ); + MEMVAR( int, IValue2 ); + }; + + class TestData2 : public Nymph::Data + { + public: + TestData2() : + Data(), + fDValue1( 0. ), + fDValue2( 10. ) + {} + + virtual ~TestData2() + {} + + MEMVAR( double, DValue1 ); + MEMVAR( double, DValue2 ); + }; + + +} /* namespace Nymph */ + +#endif /* NYMPH_TESTING_TESTDATACLASSES */ diff --git a/Testing/Cpp/TestDataFrame.cc b/Testing/Cpp/TestDataFrame.cc new file mode 100644 index 00000000..6dd3f703 --- /dev/null +++ b/Testing/Cpp/TestDataFrame.cc @@ -0,0 +1,80 @@ +/* + * TestDataFrame.cc + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#include "TestDataClasses.hh" + +#include "DataFrame.hh" + +#include "catch2/catch_test_macros.hpp" + + +TEST_CASE( "data_frame", "[data]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + DataFrame tdfFrame; + const DataFrame& tdfcFrame = tdfFrame; + + // empty Has<>() should return true + REQUIRE( tdfFrame.Has<>() ); + + REQUIRE( tdfFrame.DataObjects().empty() ); + REQUIRE( tdfFrame.Empty() ); + REQUIRE_FALSE( tdfFrame.Has< TestData1 >() ); + REQUIRE_FALSE( tdfFrame.Has< TestData2 >() ); + + // create a data object with Get() + TestData1& tdfData1 = tdfFrame.Get< TestData1 >(); + REQUIRE( tdfFrame.Has< TestData1 >() ); + // ensure TestData1 object was initialized correctly + REQUIRE( tdfData1.GetIValue1() == 0 ); + REQUIRE( tdfData1.GetIValue2() == 5 ); + + // make sure that we haven't made a copy of the data object + tdfData1.SetIValue1( 50 ); + REQUIRE( tdfData1.GetIValue1() == 50 ); + REQUIRE( tdfFrame.Get< TestData1 >().GetIValue1() == 50 ); + + // check const access + // currently has TestData1; does not have TestData2 + REQUIRE( tdfcFrame.Get< TestData1 >().GetIValue1() == 50 ); + REQUIRE_THROWS_AS( tdfcFrame.Get< TestData2 >(), DataFrameException ); + + // remove a data object + // currently has TestData1; does not have TestData2 + REQUIRE_NOTHROW( tdfFrame.Remove< TestData2 >() ); + REQUIRE_NOTHROW( tdfFrame.Remove< TestData1 >() ); + REQUIRE_FALSE( tdfFrame.Has< TestData1 >() ); + REQUIRE( tdfFrame.Empty() ); + + // set an object with a raw pointer + TestData2* tdfNewTestData2Ptr = new TestData2(); + REQUIRE_NOTHROW( tdfFrame.Set( tdfNewTestData2Ptr ) ); + REQUIRE( tdfFrame.Has< TestData2 >() ); + REQUIRE( (Data*)tdfNewTestData2Ptr == tdfFrame.DataObjects()[typeid(TestData2)].get() ); // we did not make a copy + + // set an object with a unique_ptr + std::unique_ptr< TestData1 > tdfNewTestData1Ptr( new TestData1 ); + REQUIRE_NOTHROW( tdfFrame.Set( std::move(tdfNewTestData1Ptr) ) ); + REQUIRE_FALSE( tdfNewTestData1Ptr ); + REQUIRE( tdfFrame.Has< TestData1 >() ); + REQUIRE( tdfFrame.Get< TestData1 >().GetIValue1() == 0 ); // check that fIValue1 has the default value; we'll overwrite the data object next, and verify we have the new value + + // verify multiple data types are present (TestData1 and TestData2 should be present at this point) + REQUIRE( tdfFrame.Has< TestData1, TestData2 >() ); + REQUIRE( tdfFrame.Has< TestData2, TestData1 >() ); + + // set an object with a copy of an object + TestData1 tdfAnotherTestData1; + tdfAnotherTestData1.SetIValue1( 4000 ); + REQUIRE_NOTHROW( tdfFrame.Set( tdfAnotherTestData1 ) ); + REQUIRE( tdfAnotherTestData1.GetIValue1() == 4000 ); + REQUIRE( tdfFrame.Get< TestData1 >().GetIValue1() == 4000 ); + REQUIRE_FALSE( (Data*)(&tdfAnotherTestData1) == tdfFrame.DataObjects()[typeid(TestData1)].get() ); // we made a copy + +} diff --git a/Testing/Cpp/TestDataProcessorClasses.hh b/Testing/Cpp/TestDataProcessorClasses.hh new file mode 100644 index 00000000..b6ff0aa6 --- /dev/null +++ b/Testing/Cpp/TestDataProcessorClasses.hh @@ -0,0 +1,117 @@ +/* + * TestProcessorClasses.hh + * + * Created on: Nov 25, 2021 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_TESTING_TESTDATAPROCESSORCLASSES_HH +#define NYMPH_TESTING_TESTDATAPROCESSORCLASSES_HH + +#include "TestDataClasses.hh" + +#include "Exception.hh" +#include "Processor.hh" +#include "SignalData.hh" +#include "SlotData.hh" + +#include "logger.hh" + +LOGGER( tdpclog_h, "TestDataProcessorClasses" ); + +namespace NymphTesting +{ + + // external slot function owner + struct MultiplyFuncOwner + { + double fValue = 2.5; + void Multiply( const TestData1& intData, TestData2& doubleData ) + { + doubleData.SetDValue1( (double)intData.GetIValue1() * fValue ); + doubleData.SetDValue2( (double)intData.GetIValue2() * fValue ); + return; + } + }; + + // This processor has a data slot that takes TestData1 as input and has no output (TestData1 is modified in place) + class Adder : public Nymph::Processor + { + public: + Adder( const std::string& name = "adder" ) : + Processor( name ), + fAddValue( 0 ), + fAddSignal( "add", this ), + fAddSlotWithSig( "add-sig", this, &Adder::Add, &fAddSignal ), + fAddSlotNoSig( "add-no-sig", this, &Adder::Add ), + fMultiplySlot( "multiply", this, &fMultFunc, &MultiplyFuncOwner::Multiply ), + fIToDSlot( "i-to-d", this, &Adder::IntToDouble ), + fPrintSlot( "print", this, &Adder::Print ), + fJustThrowsSlot( "just-throws", this, &Adder::JustThrows ) + {} + + virtual ~Adder() + {} + + void Configure( const scarab::param_node& node ) + { + fAddValue = node.get_value( "to-add", fAddValue ); + + fMultFunc.fValue = node.get_value( "to-mult", fMultFunc.fValue ); + + return; + } + + MEMVAR( int, AddValue ); + + public: + void Add( TestData1& data ) + { + data.SetIValue1( data.GetIValue1() + fAddValue ); + data.SetIValue2( data.GetIValue2() + fAddValue ); + return; + } + + void Print( const TestData1& data ) + { + LPROG( tdpclog_h, "Value 1: " << data.GetIValue1() ); + LPROG( tdpclog_h, "Value 2: " << data.GetIValue2() ); + return; + } + + void IntToDouble( const TestData1& intData, TestData2& doubleData ) + { + doubleData.SetDValue1( (double)intData.GetIValue1() ); + doubleData.SetDValue2( (double)intData.GetIValue2() ); + return; + } + + void JustThrows( TestData1& ) + { + throw CREATE_EXCEPT_HERE( Nymph::Exception ); + } + + protected: + MultiplyFuncOwner fMultFunc; + + public: + Nymph::SignalData fAddSignal; + + USING_NYMPH_IN_OUT; // avoids needing to prepend In and Out with Nymph:: + + Nymph::SlotData< In, Out<> > fAddSlotWithSig; + Nymph::SlotData< In, Out<> > fAddSlotNoSig; + + Nymph::SlotData< In, Out > fMultiplySlot; + + Nymph::SlotData< In, Out > fIToDSlot; + + Nymph::SlotData< In, Out<> > fPrintSlot; + + Nymph::SlotData< In, Out<> > fJustThrowsSlot; + + }; + +} + +#endif /* NYMPH_TESTING_TESTDATAPROCESSORCLASSES_HH */ diff --git a/Testing/Cpp/TestException.cc b/Testing/Cpp/TestException.cc new file mode 100644 index 00000000..6012ceb1 --- /dev/null +++ b/Testing/Cpp/TestException.cc @@ -0,0 +1,193 @@ +/* + * TestException.cc + * + * Created on: Jan 6, 2022 + * Author: N.S. Oblath + */ + +#include "Exception.hh" + +#include "catch2/catch_test_macros.hpp" + +#include "logger.hh" + +LOGGER( testlog, "TestException" ); + +TEST_CASE( "exception", "[utility]" ) +{ + using namespace Nymph; + + Exception teBlankEx; + REQUIRE( teBlankEx.what() == std::string() ); + REQUIRE( teBlankEx.filename().empty() ); + REQUIRE( teBlankEx.line() == 0 ); + + Exception teTestEx( "some_file", 1 ); + REQUIRE( teTestEx.filename() == "some_file" ); + REQUIRE( teTestEx.line() == 1 ); + + std::string teTestMessage("test message 2: "); + int testInt = 5; + teTestEx << teTestMessage << testInt; + REQUIRE( teTestEx.what() == teTestMessage + std::to_string(testInt) ); + + /*EXCEPT_HERE( Exception teTestEx );*/ + // uses of __LINE__ need to be on the same line + teTestEx( __FILE__, __LINE__ ); std::string tdeRecordFile( __FILE__ ); int tdeRecordLine( __LINE__ ); + REQUIRE( teTestEx.filename() == tdeRecordFile ); + REQUIRE( teTestEx.line() == tdeRecordLine ); + + EXCEPT_HERE( teTestEx ); tdeRecordFile = std::string(__FILE__); tdeRecordLine = __LINE__; + REQUIRE( teTestEx.filename() == tdeRecordFile ); + REQUIRE( teTestEx.line() == tdeRecordLine ); +} + +TEST_CASE( "throw", "[utility]" ) +{ + using namespace Nymph; + + try + { + throw Exception(); + } + catch(const Exception& e) + { + REQUIRE( e.what() == std::string() ); + REQUIRE( e.filename().empty() ); + REQUIRE( e.line() == 0 ); + } + + std::string tdeRecordFile; + int tdeRecordLine = 0; + + try + { + tdeRecordFile = std::string(__FILE__); tdeRecordLine = __LINE__; + throw Exception( __FILE__, __LINE__ ) << "test message"; + } + catch(const Exception& e) + { + REQUIRE( e.what() == std::string("test message") ); + REQUIRE( e.filename() == tdeRecordFile ); + REQUIRE( e.line() == tdeRecordLine + 1 ); // line number was recorded just before the throw + } + + // Give CREATE_EXCEPT_HERE the class name + try + { + tdeRecordFile = std::string(__FILE__); tdeRecordLine = __LINE__; + throw CREATE_EXCEPT_HERE( Exception ); + } + catch(const Exception& e) + { + REQUIRE( e.what() == std::string() ); + REQUIRE( e.filename() == tdeRecordFile ); + REQUIRE( e.line() == tdeRecordLine + 1 ); // line number was recorded just before the throw + } + + // Give EXCEPT_HERE an exception + try + { + tdeRecordFile = std::string(__FILE__); tdeRecordLine = __LINE__; + throw EXCEPT_HERE( Exception() << "test message" ); + } + catch(const Exception& e) + { + REQUIRE( e.what() == std::string("test message") ); + REQUIRE( e.filename() == tdeRecordFile ); + REQUIRE( e.line() == tdeRecordLine + 1 ); // line number was recorded just before the throw + } + + // THROW macro + try + { + tdeRecordFile = std::string(__FILE__); tdeRecordLine = __LINE__; + THROW_EXCEPT_HERE( Exception() << "test message" ); + } + catch(const Exception& e) + { + REQUIRE( e.what() == std::string("test message") ); + REQUIRE( e.filename() == tdeRecordFile ); + REQUIRE( e.line() == tdeRecordLine + 1 ); // line number was recorded just before the throw + } + +} + +const unsigned tdeLayers = 5; +const std::string tdeFirstThrowText( "First throw" ); +const std::string tdeNestedThrowText( "nested throw at layer " ); + +void CatchThrow() +{ + //const static unsigned tdeLayers = 5; + static unsigned tdeCount = 0; + + try + { + if( tdeCount == tdeLayers ) + { + throw CREATE_EXCEPT_HERE( Nymph::Exception ) << tdeFirstThrowText; + } + else + { + ++tdeCount; + CatchThrow(); + return; + } + } + catch(const Nymph::Exception& e) + { + if( tdeCount == tdeLayers ) + { + REQUIRE( e.what() == tdeFirstThrowText ); + } + else + { + std::string tdeWhat( e.what() ); + REQUIRE( tdeWhat.substr(0, tdeNestedThrowText.size()) == tdeNestedThrowText ); + } + THROW_NESTED_EXCEPT_HERE( Nymph::Exception() << tdeNestedThrowText << tdeCount-- ) ; + } +} + +void PrintExceptionWithTests( const Nymph::Exception& e, unsigned tdeCount = 0 ) +{ + std::string prefix( tdeCount, ' ' ); + LINFO( testlog, prefix << "Thrown at: " << e.where() ); + LINFO( testlog, prefix << e.what() ); + if( tdeCount == tdeLayers + 1 ) + { + REQUIRE( e.what() == tdeFirstThrowText ); + } + else + { + std::string tdeWhat( e.what() ); + REQUIRE( tdeWhat.substr(0, tdeNestedThrowText.size()) == tdeNestedThrowText ); + } + + try + { + std::rethrow_if_nested( e ); + } + catch(const Nymph::Exception& e) + { + PrintExceptionWithTests( e, ++tdeCount ); + } + return; +} + +TEST_CASE( "nested_throw", "[utility]" ) +{ + using namespace Nymph; + + try + { + CatchThrow(); + } + catch(const Exception& e) + { + + PrintExceptionWithTests(e); + } + +} diff --git a/Testing/Cpp/TestPrimaryProcessor.cc b/Testing/Cpp/TestPrimaryProcessor.cc new file mode 100644 index 00000000..8ef1fd7c --- /dev/null +++ b/Testing/Cpp/TestPrimaryProcessor.cc @@ -0,0 +1,45 @@ +/* + * TestPrimaryProcessor.cc + * + * Created on: Sept 15, 2019 + * Author: N.S. Oblath + */ + +#include "TestControllerClasses.hh" +#include "TestProcessorClasses.hh" + +#include "logger.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestPrimaryProcessor" ); + +TEST_CASE( "primary_processor", "[primary_processor]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + //SharedControl::get_instance()->Reset(); + + TestPrimaryProc tppTester; + CIQThrowController tppController; + + // SignalNewValue test + tppTester.SetTestSelection( TestPrimaryProc::TestType::SignalNewValue ); + tppTester.ConnectASlot( "value", tppTester, "value" ); + tppTester(); + REQUIRE( tppTester.GetValue() == tppTester.GetNewValue() ); + + // WaitTwoSec test + // -- no test implemented here right now -- + //tppTester.SetTestSelection( TestPrimaryProc::TestType::WaitTwoSec ); + + // ThrowExcept test + tppTester.SetTestSelection( TestPrimaryProc::TestType::ThrowExcept ); + REQUIRE_THROWS_AS( tppTester(), TestPPException ); + + + + + //REQUIRE( tppTester.ValueSig().GetControl() == &control ); +} diff --git a/Testing/Cpp/TestProcessor.cc b/Testing/Cpp/TestProcessor.cc new file mode 100644 index 00000000..41b2dae8 --- /dev/null +++ b/Testing/Cpp/TestProcessor.cc @@ -0,0 +1,155 @@ +/* + * TestProcessor.cc + * + * Created on: Sept 12, 2019 + * Author: N.S. Oblath + */ + +#include "TestControllerClasses.hh" +#include "TestProcessorClasses.hh" + +#include "logger.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestProcessor" ); + +TEST_CASE( "processor", "[signal],[slot],[processor]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + // need a controller to exist + CIQThrowController tpController; + + //SharedControl::get_instance()->Reset(); + + TestProc tpProcessor; + + SECTION( "Configuration" ) + { + REQUIRE( tpProcessor.GetValue() == 0 ); + + // setup the config parameter + scarab::param_node config; + int configValue = 5; + config.add( "value", configValue ); + + // perform configuration with no value for "string" + REQUIRE_NOTHROW( tpProcessor.Configure( config ) ); + // check that the value was set correctly + REQUIRE( tpProcessor.GetValue() == configValue ); + + // peform configuration with an illegal value for "string" + config.add( "string", "illegal value" ); + REQUIRE_THROWS_AS( tpProcessor.Configure( config ), ConfigException ); + try + { + tpProcessor.Configure( config ); + } + catch( const ConfigException& e ) + { + PrintException(e); + } + + // perform configuration with a legal value for "string" + config["string"]().set( "ok value 1" ); + REQUIRE_NOTHROW( tpProcessor.Configure( config ) ); + REQUIRE( tpProcessor.StringValue() == "ok 1" ); + + } + + SECTION( "Signals and Slots" ) + { + // check that the processor has the right number of signals and slots registered + REQUIRE( tpProcessor.Signals().size() == 2 ); + REQUIRE( tpProcessor.Slots().size() == 3 ); + + //************************** + // Testing signal->slot + //************************** + + // this is a slot using a lambda function to set a variable + int lambdaValue = 0; + Slot< int > lambdaSlot( "lambdaSlot", [&lambdaValue](int input){ lambdaValue = input; } ); + tpProcessor.RegisterSlot( "lambdaSlot", &lambdaSlot ); + + // this slot is a member function of an object other than the processor + TestSlotOwner slotOwner; + Slot< int > slotOwnerSlot( "slotOwnerSlot", &tpProcessor, &slotOwner, &TestSlotOwner::TestSlotFunc ); + + // check initial value + REQUIRE( slotOwner.fValue == 0 ); + REQUIRE( lambdaValue == 0 ); + + // we've added a couple more slots + REQUIRE( tpProcessor.Slots().size() == 5 ); + + // no connections yet + REQUIRE( tpProcessor.Signals().at("value")->Connections().size() == 0 ); + REQUIRE( tpProcessor.Slots().at("value")->Connections().size() == 0 ); + + // make a connection + tpProcessor.ConnectASlot( "value", tpProcessor, "value" ); + + REQUIRE( tpProcessor.Signals().at("value")->Connections().size() == 1 ); + REQUIRE( tpProcessor.Slots().at("value")->Connections().size() == 1 ); + + // repeat connection should be ignored + tpProcessor.ConnectASlot( "value", tpProcessor, "value" ); + + REQUIRE( tpProcessor.Signals().at("value")->Connections().size() == 1 ); + REQUIRE( tpProcessor.Slots().at("value")->Connections().size() == 1 ); + + // connect the lambda slot to the signal + tpProcessor.ConnectASignal( tpProcessor, "value", "lambdaSlot" ); + + REQUIRE( tpProcessor.Signals().at("value")->Connections().size() == 2 ); + REQUIRE( tpProcessor.Slots().at("value")->Connections().size() == 1 ); + REQUIRE( tpProcessor.Slots().at("lambdaSlot")->Connections().size() == 1 ); + + // connect the exteranlly-owned slot to the signal + Processor::ConnectSignalToSlot( tpProcessor.Signals().at("value"), tpProcessor.Slots().at("slotOwnerSlot") ); + + REQUIRE( tpProcessor.Signals().at("value")->Connections().size() == 3 ); + REQUIRE( tpProcessor.Slots().at("value")->Connections().size() == 1 ); + REQUIRE( tpProcessor.Slots().at("slotOwnerSlot")->Connections().size() == 1 ); + + // now have "signal" connected to three slots: + // value --> value + // value --> lambdaSlot + // value --> slotOwnerSlot + + // emit the signal + int newValue = 10; + LWARN( testlog, "do breakpoint in signal second-value? " << tpProcessor.Signals().at("second-value")->GetDoBreakpoint() ); + tpProcessor.ValueSig().Emit( newValue ); + + // check that all values have been changed + REQUIRE( tpProcessor.GetValue() == newValue ); + REQUIRE( slotOwner.fValue == newValue ); + REQUIRE( lambdaValue == newValue ); + + //********************************* + // testing slot->signal->slot + //********************************* + + // check initial value + REQUIRE( tpProcessor.GetSecondValue() == 0 ); + + //REQUIRE( tpProcessor.Slots().at("second-value")->SignalsUsed().size() == 1 ); + //REQUIRE( tpProcessor.Slots().at("second-value")->SignalsUsed()[0] == &tpProcessor.SecondValueSig() ); + + tpProcessor.ConnectASlot( "second-value", tpProcessor, "second-value-2" ); + + // now have second-value (slot) emits second-value --> second-value-2 + + REQUIRE( tpProcessor.Signals().at("second-value")->Connections().size() == 1 ); + REQUIRE( tpProcessor.Slots().at("second-value-2")->Connections().size() == 1 ); + + tpProcessor.SecondValueSlotFunction( newValue ); + + REQUIRE( tpProcessor.GetSecondValue() == newValue ); + } + +} diff --git a/Testing/Cpp/TestProcessorClasses.cc b/Testing/Cpp/TestProcessorClasses.cc new file mode 100644 index 00000000..d29d3cf8 --- /dev/null +++ b/Testing/Cpp/TestProcessorClasses.cc @@ -0,0 +1,125 @@ +/* + * TestProcessorClasses.cc + * + * Created on: Jun 14, 2022 + * Author: N.S. Oblath + */ + +#include "TestProcessorClasses.hh" + +REGISTER_PROCESSOR( NymphTesting, TestProc, "test-proc" ); +REGISTER_PROCESSOR( NymphTesting, TestPrimaryProc, "test-primary" ); + +namespace NymphTesting +{ + TestProc::TestProc( const std::string& name ) : + Nymph::Processor( name ), + fValue( 0 ), + fSecondValue( 0 ), + fValueSig( "value", this ), + fValueSlot( "value", this, &TestProc::SetValue ), + fSecondValueSig( "second-value", this ), + fSecondValueSlot( "second-value", this, &TestProc::SecondValueSlotFunction ), + fSecondValueSlot2( "second-value-2", this, &TestProc::SetSecondValue ) + {} + + TestProc::~TestProc() + {} + + void TestProc::Configure( const scarab::param_node& node ) + { + fValue = node.get_value( "value", fValue ); + + // we'll test configuration error throwing by having a string with restricted values + if( node.has("string") ) + { + if( node["string"]().as_string() == "ok value 1" ) fStringValue = "ok 1"; + else if( node["string"]().as_string() == "ok value 2" ) fStringValue = "ok 2"; + else THROW_EXCEPT_HERE( Nymph::ConfigException( node ) << "Invalid choice for \"string\"" ); + } + + return; + } + + void TestProc::SecondValueSlotFunction( int newValue ) + { + fSecondValueSig( newValue ); + } + + + TestPrimaryProc::TestPrimaryProc( const std::string& name ) : + PrimaryProcessor( name ), + fTestSelection( TestType::ThrowExcept ), + fNewValue( 10 ), + fValue( 0 ), + fValueSig( "value", this ), + fValueSlot( "value", this, &TestPrimaryProc::SetValue ) + {} + + TestPrimaryProc::~TestPrimaryProc() + {} + + void TestPrimaryProc::Configure( const scarab::param_node& node ) + { + if( node.has("test") ) + { + std::string testSelectionStr( node["test"]().as_string() ); + if( testSelectionStr == "signal-new-value" ) fTestSelection = TestType::SignalNewValue; + else if( testSelectionStr == "wait-two-sec" ) fTestSelection = TestType::WaitTwoSec; + else if( testSelectionStr == "throw-except" ) fTestSelection = TestType::ThrowExcept; + else + { + LERROR( testprocclasses_h_log, "Invalid test selection: " << testSelectionStr ); + THROW_EXCEPT_HERE( Nymph::ConfigException() << "Invalid test selection: " << testSelectionStr ); + } + } + return; + } + + void TestPrimaryProc::Run() + { + switch( fTestSelection ) + { + case TestType::SignalNewValue: + TestSignalNewValue(); + return; + case TestType::WaitTwoSec: + TestWaitTwoSec(); + return; + case TestType::ThrowExcept: + TestThrowExcept(); + return; + case TestType::QuitChain: + TestQuitChain(); + return; + default: + LERROR( testprocclasses_h_log, "Invalid test choice" ); + THROW_EXCEPT_HERE( Nymph::Exception() << "Invalid test choice" ); + } + return; + } + + void TestPrimaryProc::TestSignalNewValue() + { + fValueSig( fNewValue ); + return; + } + + void TestPrimaryProc::TestWaitTwoSec() + { + std::this_thread::sleep_for(std::chrono::seconds(2)); + return; + } + + void TestPrimaryProc::TestThrowExcept() + { + THROW_EXCEPT_HERE( TestPPException() << "PrimaryProcessor test function: throw Exception" ); + return; + } + + void TestPrimaryProc::TestQuitChain() + { + QUIT_CHAIN; + } + +} /* namespace NymphTesting */ diff --git a/Testing/Cpp/TestProcessorClasses.hh b/Testing/Cpp/TestProcessorClasses.hh new file mode 100644 index 00000000..d0132215 --- /dev/null +++ b/Testing/Cpp/TestProcessorClasses.hh @@ -0,0 +1,113 @@ +/* + * TestProcessorClasses.hh + * + * Created on: Nov 25, 2021 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_TESTING_PROCESSORCLASSES_HH_ +#define NYMPH_TESTING_PROCESSORCLASSES_HH_ + +#include "PrimaryProcessor.hh" +#include "Processor.hh" +#include "QuitChain.hh" +#include "Signal.hh" +#include "Slot.hh" + +#include "logger.hh" + +#include +#include + +LOGGER( testprocclasses_h_log, "TestProcessorClasses" ); + +namespace NymphTesting +{ + // concrete processor class that we can test + // implements Configure() and has its own signal and slot + class TestProc : public Nymph::Processor + { + public: + TestProc( const std::string& name = "test" ); + + virtual ~TestProc(); + + void Configure( const scarab::param_node& node ); + + MEMVAR( int, Value ); + MEMVAR( int, SecondValue ); + MEMVAR_REF( std::string, StringValue ); + + void SecondValueSlotFunction( int newValue ); + + MEMVAR_REF( Nymph::Signal< int >, ValueSig ); + MEMVAR_REF( Nymph::Slot< int >, ValueSlot ); + + MEMVAR_REF( Nymph::Signal< int >, SecondValueSig ); + MEMVAR_REF( Nymph::Slot< int >, SecondValueSlot ); + MEMVAR_REF( Nymph::Slot< int >, SecondValueSlot2 ); + + }; + + // external slot function owner + struct TestSlotOwner + { + int fValue = 0; + void TestSlotFunc( int input ) + { + fValue = input; + return; + } + }; + + + class TestPPException : public scarab::typed_exception< TestPPException > + { + public: + using scarab::typed_exception< TestPPException >::typed_exception; + ~TestPPException() = default; + }; + + + // concrete primary processor class that we can test + // implements Configure(), Run(), and has its own signal and slot + class TestPrimaryProc : public Nymph::PrimaryProcessor + { + public: + enum class TestType + { + SignalNewValue, + WaitTwoSec, + ThrowExcept, + QuitChain + }; + + TestPrimaryProc( const std::string& name = "test" ); + + virtual ~TestPrimaryProc(); + + void Configure( const scarab::param_node& node ); + + void Run(); + + void TestSignalNewValue(); + + void TestWaitTwoSec(); + + void TestThrowExcept(); + + void TestQuitChain(); + + MEMVAR( TestType, TestSelection ); + + MEMVAR( int, NewValue ); + MEMVAR( int, Value ); + + MEMVAR_REF( Nymph::Signal< int >, ValueSig ); + MEMVAR_REF( Nymph::Slot< int >, ValueSlot ); + + }; + +} /* namespace NymphTesting */ + +#endif diff --git a/Testing/Cpp/TestProcessorToolbox.cc b/Testing/Cpp/TestProcessorToolbox.cc new file mode 100644 index 00000000..7250a84d --- /dev/null +++ b/Testing/Cpp/TestProcessorToolbox.cc @@ -0,0 +1,199 @@ +/* + * TestProcessorToolbox.cc + * + * Created on: Nov 25, 2021 + * Author: N.S. Oblath + */ + +#include "TestProcessorClasses.hh" + +#include "ProcessorToolbox.hh" + +#include "logger.hh" +#include "param_codec.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestProcessorToolbox" ); + + +namespace NymphTesting +{ + class ProcTBRevealer : public Nymph::ProcessorToolbox + { + public: + using Nymph::ProcessorToolbox::ProcessorToolbox; + + bool ParseSignalSlotName( const std::string& toParse, std::string& nameOfProc, std::string& nameOfSigSlot ) const + { + return Nymph::ProcessorToolbox::ParseSignalSlotName( toParse, nameOfProc, nameOfSigSlot ); + } + }; +} + + +TEST_CASE( "processor_toolbox" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + //SharedControl::get_instance()->Reset(); + + ProcTBRevealer tptToolbox; + + SECTION( "AddRemoveProcessors" ) + { + LINFO( testlog, "Add/Remove Processor tests" ); + + std::string procName1( "testproc-1" ); + std::string procName2( "testproc-2" ); + + REQUIRE( tptToolbox.CouldBuild( "hello-world-cpp" ) ); + + REQUIRE( tptToolbox.CouldBuild( "test-proc" ) ); + REQUIRE( tptToolbox.CouldBuild( "test-primary" ) ); + REQUIRE_FALSE( tptToolbox.CouldBuild( procName1 ) ); + REQUIRE_FALSE( tptToolbox.CouldBuild( procName2 ) ); + + REQUIRE_FALSE( tptToolbox.HasProcessor( procName1 ) ); + REQUIRE_FALSE( tptToolbox.HasProcessor( procName2 ) ); + + REQUIRE_FALSE( tptToolbox.GetProcessor( procName1 ) ); + REQUIRE_FALSE( tptToolbox.GetProcessor( procName2 ) ); + + std::string config_str( + "- type: test-proc\n" + " name: testproc-1\n" + " value: 5\n" + " string: ok value 1" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tptToolbox.ConfigureProcessors( config->as_array() ) ); + + auto proc = tptToolbox.GetProcessor( procName1 ); + REQUIRE( proc ); + REQUIRE( proc->Name() == procName1 ); + + auto testProc = std::dynamic_pointer_cast< TestProc >( proc ); + REQUIRE( testProc ); + REQUIRE( testProc->GetValue() == 5 ); + REQUIRE( testProc->StringValue() == "ok 1" ); + + std::shared_ptr< Processor > tp1 = tptToolbox.ReleaseProcessor( procName1 ); + REQUIRE_FALSE( tptToolbox.GetProcessor( procName1 ) ); + + REQUIRE( tptToolbox.AddProcessor( procName1, tp1 ) ); + REQUIRE( tptToolbox.GetProcessor( procName1 ) ); + + REQUIRE( tptToolbox.AddProcessor( "test-proc", procName2 ) ); + REQUIRE( tptToolbox.GetProcessor( procName2 ) ); + + REQUIRE( tptToolbox.HasProcessor( procName1 ) ); + REQUIRE( tptToolbox.HasProcessor( procName2 ) ); + + REQUIRE( tptToolbox.RemoveProcessor( procName1 ) ); + REQUIRE_FALSE( tptToolbox.GetProcessor( procName1 ) ); + REQUIRE( tptToolbox.GetProcessor( procName2 ) ); + + tptToolbox.ClearProcessors(); + REQUIRE_FALSE( tptToolbox.GetProcessor( procName2 ) ); + } + + SECTION( "Connections" ) + { + LINFO( testlog, "Connections Tests"); + + std::string toParse( "proc:sigslot" ); + std::string parsedProc, parsedSigSlot; + + tptToolbox.ParseSignalSlotName( toParse, parsedProc, parsedSigSlot ); + REQUIRE( parsedProc == "proc" ); + REQUIRE( parsedSigSlot == "sigslot" ); + + std::string procName1( "testproc-1" ); + std::string procName2( "testproc-2" ); + + std::string p_config_str( + "- type: test-proc\n" + " name: testproc-1" + ); + + std::string c_config_str( + "- signal: \"testproc-1:value\"\n" + " slot: \"testproc-1:second-value\"" + ); + + scarab::param_translator translator; + auto p_config = translator.read_string( p_config_str, "yaml" ); + auto c_config = translator.read_string( c_config_str, "yaml" ); + + REQUIRE_NOTHROW( tptToolbox.ConfigureProcessors( p_config->as_array() ) ); + REQUIRE_NOTHROW( tptToolbox.ConfigureConnections( c_config->as_array() ) ); + + std::shared_ptr< Processor > tp1 = tptToolbox.GetProcessor( procName1 ); + + REQUIRE( tp1->Signals().at("value")->Connections().size() == 1 ); + REQUIRE( tp1->Slots().at("value")->Connections().size() == 0 ); + REQUIRE( tp1->Signals().at("second-value")->Connections().size() == 0 ); + REQUIRE( tp1->Slots().at("second-value")->Connections().size() == 1 ); + + tp1->Signals().at("value")->DisconnectAll(); + REQUIRE( tp1->Signals().at("value")->Connections().empty() ); + + tptToolbox.MakeConnection( "testproc-1:value", "testproc-1:second-value" ); + REQUIRE( tp1->Signals().at("value")->Connections().size() == 1 ); + REQUIRE( tp1->Slots().at("value")->Connections().size() == 0 ); + REQUIRE( tp1->Signals().at("second-value")->Connections().size() == 0 ); + REQUIRE( tp1->Slots().at("second-value")->Connections().size() == 1 ); + + tp1->Signals().at("value")->DisconnectAll(); + REQUIRE( tp1->Signals().at("value")->Connections().empty() ); + + tptToolbox.MakeConnection( "testproc-1", "value", "testproc-1", "second-value" ); + REQUIRE( tp1->Signals().at("value")->Connections().size() == 1 ); + REQUIRE( tp1->Slots().at("value")->Connections().size() == 0 ); + REQUIRE( tp1->Signals().at("second-value")->Connections().size() == 0 ); + REQUIRE( tp1->Slots().at("second-value")->Connections().size() == 1 ); + } + + SECTION( "FullConfig" ) + { + std::string testprimaryName( "testprimary" ); + std::string testprocName( "testproc" ); + + std::string config_str( + "processors:\n" + "- type: test-primary\n" + " name: testprimary\n" + "- type: test-proc\n" + " name: testproc\n" + "connections:\n" + "- signal: \"testprimary:value\"\n" + " slot: \"testproc:value\"\n" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tptToolbox.Configure( config->as_node() ) ); + + // processors + REQUIRE( tptToolbox.GetProcessor( testprimaryName ) ); + REQUIRE( tptToolbox.GetProcessor( testprimaryName )->Name() == testprimaryName ); + + REQUIRE( tptToolbox.GetProcessor( testprocName ) ); + REQUIRE( tptToolbox.GetProcessor( testprocName )->Name() == testprocName ); + + // connections + std::shared_ptr< Processor > testprimary = tptToolbox.GetProcessor( testprimaryName ); + REQUIRE( testprimary->Signals().at("value")->Connections().size() == 1 ); + + std::shared_ptr< Processor > testproc = tptToolbox.GetProcessor( testprocName ); + REQUIRE( testproc->Signals().at("value")->Connections().size() == 0 ); + REQUIRE( testproc->Slots().at("value")->Connections().size() == 1 ); + } + +} diff --git a/Testing/Cpp/TestProcessorVisibility.cc b/Testing/Cpp/TestProcessorVisibility.cc new file mode 100644 index 00000000..4a3ac5c0 --- /dev/null +++ b/Testing/Cpp/TestProcessorVisibility.cc @@ -0,0 +1,70 @@ +/* + * TestProcessorToolbox.cc + * + * Created on: Nov 25, 2021 + * Author: N.S. Oblath + */ + +#include "TestProcessorClasses.hh" + +#include "ProcessorToolbox.hh" + +#include "logger.hh" + +#include "catch2/catch_test_macros.hpp" + +#ifdef NYMPH_USING_PYTHON +#include +namespace py = pybind11; +#endif + +LOGGER( testlog, "TestProcessorVisibility" ); + +TEST_CASE( "processor_visibility" ) +{ + using namespace Nymph; + + ProcessorToolbox tptToolbox; + + SECTION( "Cpp" ) + { + LINFO( testlog, "C++ Processor Visibility" ); + + REQUIRE( tptToolbox.CouldBuild( "hello-world-cpp" ) ); + REQUIRE( tptToolbox.CouldBuild( "test-proc" ) ); + REQUIRE( tptToolbox.CouldBuild( "test-primary" ) ); + REQUIRE_FALSE( tptToolbox.CouldBuild( "does not exist" ) ); + + } + + SECTION( "Python - no import" ) + { + LINFO( testlog, "Python Processor Visibility (no python import)"); + + REQUIRE_FALSE( tptToolbox.CouldBuild( "hello-world-python" ) ); + } + +#ifdef NYMPH_USING_PYTHON + SECTION( "Python - with import" ) + { + py::scoped_interpreter guard{}; +// py::module_ sys = py::module_::import("sys"); +// py::print(sys.attr("path")); + + LINFO( testlog, "Python Processor Visibility (with python import)"); + + try + { + py::module_ nymph = py::module_::import("nymph"); + REQUIRE( tptToolbox.CouldBuild( "hello-world-py" ) ); + } + catch(const std::exception& e) + { + LWARN( testlog, "Unable to import `nymph` ") + } + + + } +#endif + +} diff --git a/Testing/Cpp/TestReturnBuffer.cc b/Testing/Cpp/TestReturnBuffer.cc new file mode 100644 index 00000000..d80e76b6 --- /dev/null +++ b/Testing/Cpp/TestReturnBuffer.cc @@ -0,0 +1,47 @@ +/* + * TestReturnBuffer.cc + * + * Created on: Feb 1, 2022 + * Author: N.S. Oblath + */ + +#include "ReturnBuffer.hh" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/catch_approx.hpp" + +#include "logger.hh" + +LOGGER( testlog, "TestReturnBuffer" ); + +TEST_CASE( "return_buffer_base", "[processor]" ) +{ + using namespace Nymph; + + ReturnBufferBase trbRBB; + + REQUIRE_THROWS_AS( trbRBB.GetReturn(), Exception ); +} + +TEST_CASE( "return_buffer", "[processor]" ) +{ + using namespace Nymph; + + int trbIntData = 5; + double trbDoubleData = 100.2; + std::string trbStringData( "I'm a string!" ); + ReturnBuffer< int, double, std::string > trbBuffer( trbIntData, trbDoubleData, trbStringData ); + + REQUIRE_NOTHROW( trbBuffer.GetReturn() ); + auto trbTheReturn = trbBuffer.GetReturn(); + REQUIRE( std::get<0>( trbTheReturn ) == 5 ); + REQUIRE( std::get<1>( trbTheReturn ) == Catch::Approx(100.2) ); + REQUIRE( std::get<2>( trbTheReturn ) == "I'm a string!" ); + + ReturnBufferBase& trbRBB = trbBuffer; + auto trbTheRBBReturn = trbRBB.GetReturn< int, double, std::string >(); + REQUIRE( std::get<0>( trbTheRBBReturn ) == 5 ); + REQUIRE( std::get<1>( trbTheRBBReturn ) == Catch::Approx(100.2) ); + REQUIRE( std::get<2>( trbTheRBBReturn ) == "I'm a string!" ); + +} diff --git a/Testing/Cpp/TestRunNymph.cc b/Testing/Cpp/TestRunNymph.cc new file mode 100644 index 00000000..abaf7743 --- /dev/null +++ b/Testing/Cpp/TestRunNymph.cc @@ -0,0 +1,42 @@ +/* + * TestRunNymph.cc + * + * Created on: Jan 28, 2021 + * Author: N.S. Oblath + */ + +#include "RunNymph.hh" + +#include "logger.hh" +#include "param_codec.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestSingleRunController" ); + + +TEST_CASE( "run_nymph" ) +{ + using namespace Nymph; + + SECTION( "DoRun" ) + { + std::string config_str( + "processors:\n" + "- type: test-primary\n" + " name: pp\n" + "connections:\n" + "- signal: \"pp:value\"\n" + " slot: \"pp:value\"\n" + "run-queue:\n" + "- pp\n" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( RunNymph( config->as_node() ) ); + + } + +} diff --git a/Testing/Cpp/TestService.cc b/Testing/Cpp/TestService.cc new file mode 100644 index 00000000..c8414c61 --- /dev/null +++ b/Testing/Cpp/TestService.cc @@ -0,0 +1,58 @@ +/* + * TestService.cc + * + * Created on: Sept 12, 2019 + * Author: N.S. Oblath + */ + +#include "TestControllerClasses.hh" +#include "TestServiceClasses.hh" + +#include "ConfigException.hh" + +#include "logger.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestService" ); + +TEST_CASE( "service", "[service]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + TestService tpService; + + SECTION( "Configuration" ) + { + REQUIRE( tpService.GetValue() == 0 ); + + // setup the config parameter + scarab::param_node config; + int configValue = 5; + config.add( "value", configValue ); + + // perform configuration with no value for "string" + REQUIRE_NOTHROW( tpService.Configure( config ) ); + // check that the value was set correctly + REQUIRE( tpService.GetValue() == configValue ); + + // peform configuration with an illegal value for "string" + config.add( "string", "illegal value" ); + REQUIRE_THROWS_AS( tpService.Configure( config ), ConfigException ); + try + { + tpService.Configure( config ); + } + catch( const ConfigException& e ) + { + PrintException(e); + } + + // perform configuration with a legal value for "string" + config["string"]().set( "ok value 1" ); + REQUIRE_NOTHROW( tpService.Configure( config ) ); + REQUIRE( tpService.StringValue() == "ok 1" ); + + } +} diff --git a/Testing/Cpp/TestServiceClasses.cc b/Testing/Cpp/TestServiceClasses.cc new file mode 100644 index 00000000..d2615284 --- /dev/null +++ b/Testing/Cpp/TestServiceClasses.cc @@ -0,0 +1,40 @@ +/* + * TestServiceClasses.cc + * + * Created on: Dec 28, 2023 + * Author: N.S. Oblath + */ + +#include "TestServiceClasses.hh" + +#include "ConfigException.hh" + +REGISTER_SERVICE( NymphTesting, TestService, "test-svc" ); + +namespace NymphTesting +{ + TestService::TestService( const std::string& name ) : + Nymph::Service( name ), + fValue( 0 ), + fSecondValue( 0 ) + {} + + TestService::~TestService() + {} + + void TestService::Configure( const scarab::param_node& node ) + { + fValue = node.get_value( "value", fValue ); + + // we'll test configuration error throwing by having a string with restricted values + if( node.has("string") ) + { + if( node["string"]().as_string() == "ok value 1" ) fStringValue = "ok 1"; + else if( node["string"]().as_string() == "ok value 2" ) fStringValue = "ok 2"; + else THROW_EXCEPT_HERE( Nymph::ConfigException( node ) << "Invalid choice for \"string\"" ); + } + + return; + } + +} /* namespace NymphTesting */ diff --git a/Testing/Cpp/TestServiceClasses.hh b/Testing/Cpp/TestServiceClasses.hh new file mode 100644 index 00000000..4f346026 --- /dev/null +++ b/Testing/Cpp/TestServiceClasses.hh @@ -0,0 +1,33 @@ +/* + * TestServiceClasses.hh + * + * Created on: Dec 28, 2023 + * Author: N.S. Oblath + */ + +#ifndef NYMPH_TESTING_SERVICECLASSES_HH_ +#define NYMPH_TESTING_SERVICECLASSES_HH_ + +#include "Service.hh" + +namespace NymphTesting +{ + // concrete processor class that we can test + // implements Configure() and has its own signal and slot + class TestService : public Nymph::Service + { + public: + TestService( const std::string& name = "test" ); + + virtual ~TestService(); + + void Configure( const scarab::param_node& node ); + + MEMVAR( int, Value ); + MEMVAR( int, SecondValue ); + MEMVAR_REF( std::string, StringValue ); + }; + +} /* namespace NymphTesting */ + +#endif diff --git a/Testing/Cpp/TestServiceToolbox.cc b/Testing/Cpp/TestServiceToolbox.cc new file mode 100644 index 00000000..113dd9f3 --- /dev/null +++ b/Testing/Cpp/TestServiceToolbox.cc @@ -0,0 +1,87 @@ +/* + * TestServiceToolbox.cc + * + * Created on: Dec 28, 2023 + * Author: N.S. Oblath + */ + +#include "TestServiceClasses.hh" + +#include "ServiceToolbox.hh" + +#include "logger.hh" +#include "param_codec.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestServiceToolbox" ); + + +TEST_CASE( "service_toolbox" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + //SharedControl::get_instance()->Reset(); + + ServiceToolbox tstToolbox; + + SECTION( "AddRemoveServices" ) + { + LINFO( testlog, "Add/Remove Service tests" ); + + std::string svcName1( "testsvc-1" ); + std::string svcName2( "testsvc-2" ); + + REQUIRE( tstToolbox.CouldBuild( "test-svc" ) ); + REQUIRE_FALSE( tstToolbox.CouldBuild( svcName1 ) ); + REQUIRE_FALSE( tstToolbox.CouldBuild( svcName2 ) ); + + REQUIRE_FALSE( tstToolbox.HasService( svcName1 ) ); + REQUIRE_FALSE( tstToolbox.HasService( svcName2 ) ); + + REQUIRE_FALSE( tstToolbox.GetService( svcName1 ) ); + REQUIRE_FALSE( tstToolbox.GetService( svcName2 ) ); + + std::string config_str( + "- type: test-svc\n" + " name: testsvc-1\n" + " value: 5\n" + " string: ok value 1" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tstToolbox.ConfigureServices( config->as_array() ) ); + + auto svc = tstToolbox.GetService( svcName1 ); + REQUIRE( svc ); + REQUIRE( svc->Name() == svcName1 ); + + auto testService = std::dynamic_pointer_cast< TestService >( svc ); + REQUIRE( testService ); + REQUIRE( testService->GetValue() == 5 ); + REQUIRE( testService->StringValue() == "ok 1" ); + + std::shared_ptr< Service > tp1 = tstToolbox.ReleaseService( svcName1 ); + REQUIRE_FALSE( tstToolbox.GetService( svcName1 ) ); + + REQUIRE( tstToolbox.AddService( svcName1, tp1 ) ); + REQUIRE( tstToolbox.GetService( svcName1 ) ); + + REQUIRE( tstToolbox.AddService( "test-svc", svcName2 ) ); + REQUIRE( tstToolbox.GetService( svcName2 ) ); + + REQUIRE( tstToolbox.HasService( svcName1 ) ); + REQUIRE( tstToolbox.HasService( svcName2 ) ); + + REQUIRE( tstToolbox.RemoveService( svcName1 ) ); + REQUIRE_FALSE( tstToolbox.GetService( svcName1 ) ); + REQUIRE( tstToolbox.GetService( svcName2 ) ); + + tstToolbox.ClearServices(); + REQUIRE_FALSE( tstToolbox.GetService( svcName2 ) ); + } + +} diff --git a/Testing/Cpp/TestSignalSlot.cc b/Testing/Cpp/TestSignalSlot.cc new file mode 100644 index 00000000..bf6b2f31 --- /dev/null +++ b/Testing/Cpp/TestSignalSlot.cc @@ -0,0 +1,170 @@ +/* + * TestSigSlot.cc + * + * Created on: Sept 7, 2019 + * Author: N.S. Oblath + */ + +#include "Signal.hh" +#include "Slot.hh" + +#include "TestControllerClasses.hh" + +#include "catch2/catch_test_macros.hpp" + +#include +#include + +TEST_CASE( "signal_slot", "[signal],[slot],[processor]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + // need a controller to exist + CIQThrowController tssController; + + int tssTestValue = 0; + + Signal< int > tssSignal( "tssSignal" ); + Signal< float > tssFloatSignal( "float-signal" ); + Slot< int > tssSlot( "tssSlot", [&](int aValue ){ tssTestValue = aValue; } ); + + REQUIRE_FALSE( tssSignal.GetDoBreakpoint() ); + + SECTION( "signal_emit" ) + { + // this section tests all of the paths in Signal::operator()() + // nothing is listening to the signal, and instead we verify whether exceptions are thrown + + SECTION( "basic emit and after break" ) + { + // test successful throw, no unusual circumstances + REQUIRE_NOTHROW( tssSignal.Emit( 1 ) ); + + // test successful throw after breakpoint not set by the signal + auto asyncReturn = std::async( std::launch::async, [&tssController](){ + tssController.WaitForBreakOrCanceled(); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + tssController.Continue(); + } ); + tssController.Break(); + REQUIRE_NOTHROW( tssSignal.Emit( 2 ) ); + } + + SECTION( "throw after break by signal" ) + { + // test successful throw after breakpoint set by the signal + tssSignal.SetDoBreakpoint( true ); + auto asyncReturn = std::async( std::launch::async, [&tssController](){ + tssController.WaitForBreakOrCanceled(); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + tssController.Continue(); + } ); + REQUIRE_NOTHROW( tssSignal.Emit( 3 ) ); + } + + SECTION( "canceled controller" ) + { + // test behavior for canceled controller + tssController.Cancel(); + REQUIRE_THROWS_AS( tssSignal.Emit( 1 ), QuitChain ); + } + + SECTION( "cancel during break" ) + { + // test behavior for controller canceled during break + auto asyncReturn = std::async( std::launch::async, [&tssController](){ + tssController.WaitForBreakOrCanceled(); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + tssController.Cancel(); + } ); + tssController.Break(); + REQUIRE_THROWS_AS( tssSignal.Emit( 2 ), QuitChain ); + } + + SECTION( "cancel during break" ) + { + // test behavior for signal starting the break, and controller is canceled during the break + tssSignal.SetDoBreakpoint( true ); + auto asyncReturn = std::async( std::launch::async, [&tssController](){ + tssController.WaitForBreakOrCanceled(); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + tssController.Cancel(); + } ); + REQUIRE_THROWS_AS( tssSignal.Emit( 3 ), QuitChain ); + } + + SECTION( "return from break" ) + { + // test return from break + tssSignal.SetDoBreakpoint( true ); + auto asyncReturn = std::async( std::launch::async, [&tssSignal, &tssController](){ + tssSignal.Emit( 4 ); // cannot test this with REQUIRE() because tests can currently only be done in one thread + tssController.WaitForEndOfRun(); + } ); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + REQUIRE( tssController.WaitForBreakOrCanceled() ); + REQUIRE( tssController.HasReturn() ); + auto theReturn = tssController.GetReturn< int >(); + REQUIRE( std::get<0>( theReturn ) == 4 ); + std::this_thread::sleep_for( std::chrono::milliseconds(200) ); + tssController.Cancel(); + } + } + + SECTION( "connections" ) + { + + REQUIRE_THROWS_AS( tssFloatSignal.Connect( &tssSlot), ConnectionException ); + + REQUIRE_NOTHROW( tssSignal.Connect( &tssSlot ) ); + + REQUIRE( tssSignal.Connections().size() == 1 ); + REQUIRE( tssSlot.Connections().size() == 1 ); + + tssSignal.Emit( 5 ); + REQUIRE( tssTestValue == 5 ); + + tssSignal( 10 ); + REQUIRE( tssTestValue == 10 ); + + tssSignal.Disconnect( &tssSlot ); + + REQUIRE( tssSignal.Connections().size() == 0 ); + REQUIRE( tssSlot.Connections().size() == 0 ); + + // connect and disconnect via slot + tssSlot.ConnectTo( &tssSignal ); + + REQUIRE( tssSignal.Connections().size() == 1 ); + REQUIRE( tssSlot.Connections().size() == 1 ); + + tssSlot.Disconnect( &tssSignal ); + + REQUIRE( tssSignal.Connections().size() == 0 ); + REQUIRE( tssSlot.Connections().size() == 0 ); + + // reconnect for testing SignalBase::DisconnectAll() + tssSignal.Connect( &tssSlot ); + + REQUIRE( tssSignal.Connections().size() == 1 ); + REQUIRE( tssSlot.Connections().size() == 1 ); + + tssSignal.DisconnectAll(); + + REQUIRE( tssSignal.Connections().size() == 0 ); + REQUIRE( tssSlot.Connections().size() == 0 ); + + // reconnect for testing SlotBaseDisconnectAll() + tssSlot.ConnectTo( &tssSignal ); + + REQUIRE( tssSignal.Connections().size() == 1 ); + REQUIRE( tssSlot.Connections().size() == 1 ); + + tssSlot.DisconnectAll(); + + REQUIRE( tssSignal.Connections().size() == 0 ); + REQUIRE( tssSlot.Connections().size() == 0 ); + } + +} diff --git a/Testing/Cpp/TestSingleRunController.cc b/Testing/Cpp/TestSingleRunController.cc new file mode 100644 index 00000000..535ee44e --- /dev/null +++ b/Testing/Cpp/TestSingleRunController.cc @@ -0,0 +1,120 @@ +/* + * TestSingleRunController.cc + * + * Created on: Jan 17, 2021 + * Author: N.S. Oblath + */ + +#include "TestProcessorClasses.hh" + +#include "ProcessorToolbox.hh" +#include "SingleRunController.hh" + +#include "logger.hh" +#include "param_codec.hh" + +#include "catch2/catch_test_macros.hpp" + +LOGGER( testlog, "TestSingleRunController" ); + +namespace NymphTesting +{ + class SRCRevealer : public Nymph::SingleRunController + { + public: + using Nymph::SingleRunController::SingleRunController; + + using Nymph::SingleRunController::ThreadSource; + using Nymph::SingleRunController::ThreadSourceGroupT; + bool AddProcessorToThreadGroup( const std::string& name, ThreadSourceGroupT& group ) + { + return SingleRunController::AddProcessorToThreadGroup( name, group ); + } + }; +} + +TEST_CASE( "single_run_controller" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + ProcessorToolbox tsrcToolbox; + SRCRevealer tsrcController( tsrcToolbox ); + + SECTION( "DoRun" ) + { + std::string config_str( + "processors:\n" + "- type: test-primary\n" + " name: pp\n" + "connections:\n" + "- signal: \"pp:value\"\n" + " slot: \"pp:value\"\n" + "run-queue:\n" + "- pp" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tsrcToolbox.Configure( config->as_node() ) ); + REQUIRE_NOTHROW( tsrcController.Configure( config->as_node() ) ); + + auto ppProc = std::dynamic_pointer_cast< TestPrimaryProc >( tsrcToolbox.GetProcessor( "pp" ) ); + REQUIRE( ppProc ); + ppProc->SetTestSelection( TestPrimaryProc::TestType::SignalNewValue ); + + // do the run + REQUIRE_NOTHROW( tsrcController.Run() ); + + // check the results + REQUIRE( ppProc->GetValue() == ppProc->GetNewValue() ); + + } + + SECTION( "RunQueue" ) + { + LINFO( testlog, "RunQueue Tests" ); + + std::string config_str( + "- type: test-primary\n" + " name: testprimary-1\n" + "- type: test-primary\n" + " name: testprimary-2\n" + "- type: test-proc\n" + " name: testproc-1\n" + ); + + scarab::param_translator translator; + auto config = translator.read_string( config_str, "yaml" ); + + REQUIRE_NOTHROW( tsrcToolbox.ConfigureProcessors( config->as_array() ) ); + + SRCRevealer::ThreadSourceGroupT group; + REQUIRE_FALSE( tsrcController.AddProcessorToThreadGroup( "blah", group ) ); + REQUIRE_FALSE( tsrcController.AddProcessorToThreadGroup( "testproc-1", group ) ); + REQUIRE( tsrcController.AddProcessorToThreadGroup( "testprimary-1", group ) ); + REQUIRE( group.size() == 1 ); + + REQUIRE_FALSE( tsrcController.PushBackToRunQueue( "testproc-1" ) ); + REQUIRE( tsrcController.PushBackToRunQueue( "testprimary-1" ) ); + REQUIRE( tsrcController.RunQueue().size() == 1 ); + REQUIRE( tsrcController.RunQueue().begin()[0].size() == 1 ); + REQUIRE( tsrcController.PushBackToRunQueue( "testprimary-2" ) ); + REQUIRE( tsrcController.RunQueue().size() == 2 ); + REQUIRE( tsrcController.RunQueue().begin()[0].size() == 1 ); + + tsrcController.PopBackOfRunQueue(); + REQUIRE( tsrcController.RunQueue().size() == 1 ); + + tsrcController.ClearRunQueue(); + REQUIRE( tsrcController.RunQueue().empty() ); + + REQUIRE( tsrcController.PushBackToRunQueue( {"testprimary-1", "testprimary-2"} ) ); + REQUIRE( tsrcController.RunQueue().size() == 1 ); + REQUIRE( tsrcController.RunQueue()[0].size() == 2 ); + + } + + +} diff --git a/Testing/Cpp/TestSlotData.cc b/Testing/Cpp/TestSlotData.cc new file mode 100644 index 00000000..e746af41 --- /dev/null +++ b/Testing/Cpp/TestSlotData.cc @@ -0,0 +1,129 @@ +/* + * TestSlotData.cc + * + * Created on: Jan 25, 2022 + * Author: N.S. Oblath + */ + +#include "TestControllerClasses.hh" +#include "TestDataProcessorClasses.hh" + +#include "DataFrame.hh" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/catch_approx.hpp" + +#include "logger.hh" +LOGGER(tsdlog_hh, "testslotdata.hh"); + +TEST_CASE( "slot_data", "[slot],[data]" ) +{ + using namespace Nymph; + using namespace NymphTesting; + + // need a controller to exist + CIQThrowController tsdController; + + +// TODO: test creation of slot with: +// x proc == owner; no return; multiple inputs +// x proc == owner; multiple returns; multiple inputs +// * proc= != owner; multiple returns; multiple inputs +// x const arguments (supply const with type) + +// TODO: test calling of function +// x multiple inputs; data not present; throws Exception +// x multiple inputs; multiple output; call is successful +// x call throws results in thrown Exception + +// TODO: test signal emmission +// x slot with output calls a second slot + + // create the processor and verify the presence of the slots + Adder tsdAdder; + REQUIRE( tsdAdder.Slots().count("add-sig") == 1 ); + REQUIRE( tsdAdder.Slots().count("add-no-sig") == 1 ); + + // get signal and slot pointers and cast + SlotData< In, Out<> >* tsdSlotDAddSig = dynamic_cast< SlotData< In, Out<> >* >( tsdAdder.Slots().at("add-sig") ); + SlotData< In, Out<> >* tsdSlotDAddNoSig = dynamic_cast< SlotData< In, Out<> >* >( tsdAdder.Slots().at("add-no-sig") ); + REQUIRE( tsdSlotDAddSig ); + REQUIRE( tsdSlotDAddNoSig ); + + SlotData< In, Out >* tsdSlotDMult = dynamic_cast< SlotData< In, Out >* >( tsdAdder.Slots().at("multiply") ); + REQUIRE( tsdSlotDMult ); + + SlotData< In, Out >* tsdSlotDIToD = dynamic_cast< SlotData< In, Out >* >( tsdAdder.Slots().at("i-to-d") ); + REQUIRE( tsdSlotDIToD ); + + SlotData< In, Out<> >* tsdSlotDPrint = dynamic_cast< SlotData< In, Out<> >* >( tsdAdder.Slots().at("print") ); + REQUIRE( tsdSlotDPrint ); + + SlotData< In, Out<> >* tsdSlotDJustThrows = dynamic_cast< SlotData< In, Out<> >* >( tsdAdder.Slots().at("just-throws") ); + REQUIRE( tsdSlotDJustThrows ); + + SignalData* tsdSigDAdd = dynamic_cast< SignalData* >( tsdAdder.Signals().at("add") ); + REQUIRE( tsdSigDAdd ); + + // configure the processor + tsdAdder.SetAddValue( 5 ); + + // create a data frame + DataHandle tsdHandle = std::make_shared< DataFrame >(); + + // verify that when we call the slot on an empty frame we get an exception + REQUIRE_THROWS_AS( (*tsdSlotDAddSig)( tsdHandle ), Exception ); + + TestData1& tsdTestData1 = tsdHandle->Get< TestData1 >(); + tsdTestData1.SetIValue1( 1 ); + tsdTestData1.SetIValue2( 2 ); + + // verify that slots can be called, in this case with an input and no output + // verify that slots work whether or not signals are automatically called + // call the add slot functions and verify operation + REQUIRE_NOTHROW( (*tsdSlotDAddSig)( tsdHandle ) ); // this adds the tsdAdder.fAddValue (i.e. 5) to 1 and 2, respectively + REQUIRE( tsdTestData1.GetIValue1() == 6 ); + REQUIRE( tsdTestData1.GetIValue2() == 7 ); + + REQUIRE_NOTHROW( (*tsdSlotDAddNoSig)( tsdHandle ) ); // this adds the tsdAdder.fAddValue to 6 and 7, respectively + REQUIRE( tsdTestData1.GetIValue1() == 11 ); // and IValue2 would be 12 + + // call the IToD slot function and verify operation + REQUIRE_NOTHROW( (*tsdSlotDIToD)( tsdHandle ) ); // this adds TestData2 to the frame and sets the integer values in its double variables + REQUIRE( tsdHandle->Has< TestData2 >() ); + TestData2& tsdTestData2 = tsdHandle->Get< TestData2 >(); + REQUIRE( tsdTestData2.GetDValue1() == Catch::Approx(11.) ); + REQUIRE( tsdTestData2.GetDValue2() == Catch::Approx(12.) ); + + // verify that a slot with a const data object works + // call the print slot function to verify use with const data + REQUIRE_NOTHROW( (*tsdSlotDPrint)( tsdHandle ) ); + + // verify that a thrown exception appears + // calls the slot function that just throws Exception; verify it appears here + REQUIRE_THROWS_AS( (*tsdSlotDJustThrows)( tsdHandle ), Exception ); + + // verify that an automatic signal call is made + // connect signal "add" to slot "i-to-d" + REQUIRE_NOTHROW( tsdAdder.ConnectSignalToSlot( tsdSigDAdd, tsdSlotDIToD ) ); + // remove TestData2 + tsdHandle->Remove< TestData2 >(); // we'll start fresh without a TestData2 + REQUIRE_FALSE( tsdHandle->Has< TestData2 >() ); + // call the add-sig slot and verify that the i-to-d slot gets called via the signal + REQUIRE_NOTHROW( (*tsdSlotDAddSig)( tsdHandle ) ); //add slot executes, adding 5 to 11, then triggers the i-to-d slot via the add signal, making TestData2 with fDValue2 = 16 + REQUIRE( tsdHandle->Has< TestData2 >() ); + TestData2& tsdAnotherTestData2 = tsdHandle->Get< TestData2 >(); + REQUIRE( tsdTestData1.GetIValue1() == 16 ); + REQUIRE( tsdAnotherTestData2.GetDValue1() == Catch::Approx(16.) ); + + // verify that slots owned by something else work + // remove TestData2 + tsdHandle->Remove< TestData2 >(); + REQUIRE_FALSE( tsdHandle->Has< TestData2 >() ); + // call the mult slot and verify that it worked + REQUIRE_NOTHROW( (*tsdSlotDMult)( tsdHandle ) ); // for TestData1::fIValue1, multiplies 16 by 2.5 and stores in TestData2::fDValue1 + TestData2& tsdYetAnotherTestData2 = tsdHandle->Get< TestData2 >(); + REQUIRE( tsdYetAnotherTestData2.GetDValue1() == Catch::Approx(40.) ); +} + + diff --git a/Testing/Cpp/UseCatch.cc b/Testing/Cpp/UseCatch.cc new file mode 100644 index 00000000..a8347e78 --- /dev/null +++ b/Testing/Cpp/UseCatch.cc @@ -0,0 +1,15 @@ +/* + * use_catch.cc + * + * Created on: Aug 14, 2018 + * Author: N.S. Oblath + */ + +#include "catch2/catch_session.hpp" + +int main( int argc, char* argv[] ) +{ + int result = Catch::Session().run( argc, argv ); + + return result; +} diff --git a/Testing/Python/Bindings/CMakeLists.txt b/Testing/Python/Bindings/CMakeLists.txt new file mode 100644 index 00000000..44387138 --- /dev/null +++ b/Testing/Python/Bindings/CMakeLists.txt @@ -0,0 +1,41 @@ +# CMakeLists.txt file for Nymph/Testing/Python/Bindings +# Author: N.S. Oblath +# Created on: Jun 14, 2022 + +message( "Building Python bindings for NymphTesting") + +include_directories( BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# Python binding headers +set( TESTING_PYBINDING_HEADERFILES +) + +# Python bindings +set( PYBINDING_SOURCEFILES + NymphTestingPybind.cc +) + +set( PYBINDING_PROJECT_LIBRARIES + Nymph + NymphTesting +) + +pbuilder_add_pybind11_module( + MODULE_NAME nymph_bindings_testing + SOURCE_FILES ${PYBINDING_SOURCEFILES} + PROJECT_LIBRARIES ${PYBINDING_PROJECT_LIBRARIES} +) + +# Currently there are no headers +#pbuilder_install_headers( ${TESTING_PYBINDING_HEADERFILES} ) + +# This is done in the main Nymph pybinding +#if( NOT DEFINED PBUILDER_PY_INSTALL_IN_SITELIB ) +# message( STATUS "Installing add_lib_python_path.sh since python modules will not be installed in the site-package directory" ) +# configure_file( add_lib_python_path.sh.in add_lib_python_path.sh @ONLY) +# install( FILES ${CMAKE_CURRENT_BINARY_DIR}/add_lib_python_path.sh DESTINATION ${BIN_INSTALL_DIR} ) +#endif( NOT DEFINED PBUILDER_PY_INSTALL_IN_SITELIB ) + +message( "Done with Testing Python bindings" ) diff --git a/Testing/Python/Bindings/NymphTestingPybind.cc b/Testing/Python/Bindings/NymphTestingPybind.cc new file mode 100644 index 00000000..3a7400e5 --- /dev/null +++ b/Testing/Python/Bindings/NymphTestingPybind.cc @@ -0,0 +1,19 @@ +/* + * NymphTestingPybind.cc + * + * Created on: Jun 14, 2022 + * Author: N.S. Oblath + */ + +#include + + +namespace py = pybind11; + + +PYBIND11_MODULE(_nymph_testing, nymphTestingPackage) +{ + + nymphTestingPackage.doc() = "Nymph Testing package"; + +} diff --git a/Testing/Python/Bindings/testdata.py b/Testing/Python/Bindings/testdata.py new file mode 100644 index 00000000..e6f077c4 --- /dev/null +++ b/Testing/Python/Bindings/testdata.py @@ -0,0 +1,47 @@ + +""" + testdata.py + + Created on: Jun 13, 2022 + Author: P. T. Surukuchi +""" +import unittest + +import nymph_bindings + +'''Data class with integer variables''' +class TestData1(nymph_bindings.data._Data): + + def __init__(self, test_var1=0, test_var2=5): + super().__init__() + self.test_var1 = test_var1 + self.test_var2 = test_var2 + +'''Data class with floating point variables''' +class TestData2(nymph_bindings.data._Data): + + def __init__(self, test_var1=0.0, test_var2=10.0): + super().__init__() + self.test_var1 = test_var1 + self.test_var2 = test_var2 + +# The way to access member variables need to be modified if getters/setters are used in the python version +class TestDataMethods(unittest.TestCase): + '''Testing of integer type data''' + data1=TestData1() + def test_idata_assignment(self): + self.assertEqual(self.data1.test_var1,0) + self.assertEqual(self.data1.test_var2,5) + + '''Testing of floating point type data''' + data2=TestData2() + def test_fdata_assignment(self): + self.assertAlmostEqual(self.data2.test_var1,0.0) + self.assertAlmostEqual(self.data2.test_var2,10.0) + + #Change value and test + self.data2.test_var2=20.0 + self.assertAlmostEqual(self.data2.test_var2,20.0) + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/Bindings/testprocessor.py b/Testing/Python/Bindings/testprocessor.py new file mode 100644 index 00000000..2829c55d --- /dev/null +++ b/Testing/Python/Bindings/testprocessor.py @@ -0,0 +1,71 @@ + +""" + testprocessor.py + + Created on: Apr 05, 2022 + Author: F. Thomas +""" + +__all__ = [] + +import unittest + +import nymph_bindings +import scarab + +class TestProcessorBroken(nymph_bindings.processor._Processor): + """Should not work because it does not override configure""" + + def foo(): + print('I am a failed processor implementation') + + +class TestProcessor(nymph_bindings.processor._Processor): + + def configure(self, param_node): + + param_dict = param_node.to_python() + + self.value = param_dict['value'] + + if 'foo' in param_dict: + self.foo = param_dict['foo'] + + #todo add test of a configexception thing + + +class TestPureVirtual(unittest.TestCase): + + def test_pure_virtual(self): + test_proc = TestProcessorBroken('test-proc') + + with self.assertRaises(RuntimeError) as context: + test_proc.configure(scarab.to_param({})) + + self.assertTrue(str(context.exception) == 'Tried to call pure virtual function "Nymph::Processor::configure"') + + +class TestConfigure(unittest.TestCase): + + def test_configure(self): + test_proc = TestProcessor('test-proc') + test_proc.configure(scarab.to_param({'value': 42, 'foo': 'bar'})) + + self.assertEqual(test_proc.value, 42) + self.assertEqual(test_proc.foo, 'bar') + self.assertEqual(test_proc.name, 'test-proc') + + +class TestPyProcCreator(unittest.TestCase): + + def test_creating(self): + + registrar = nymph_bindings.processor._register('testprocessor', 'TestProcessor', 'processor-name') + test_proc = nymph_bindings.processor._create('processor-name', 'test-proc') + + self.assertEqual(test_proc.name, 'test-proc') + + +if __name__ == '__main__': + unittest.main() + diff --git a/Testing/Python/Bindings/testprocessortoolbox.py b/Testing/Python/Bindings/testprocessortoolbox.py new file mode 100644 index 00000000..c30baf98 --- /dev/null +++ b/Testing/Python/Bindings/testprocessortoolbox.py @@ -0,0 +1,31 @@ + +""" + testprocessortoolbox.py + + Created on: Jun 14, 2022 + Author: N.S. Oblath +""" +import unittest + +import nymph_bindings #, _nymph_testing + + +class TestDataMethods(unittest.TestCase): + tb = nymph_bindings.processor.ProcessorToolbox() + + procName1 = 'testproc-1' # not a registered processor + procName2 = 'testproc-2' # not a registered processor + procName3 = 'test-proc' # a registered processor + + '''Testing Processor handling''' + def test_add_remove_processors(self): + self.assertFalse( self.tb.has_processor( self.procName1 ) ) + self.assertFalse( self.tb.has_processor( self.procName2 ) ) + self.assertFalse( self.tb.has_processor( self.procName3 ) ) + self.assertFalse( self.tb.could_build( self.procName1 ) ) + self.assertFalse( self.tb.could_build( self.procName2 ) ) + #self.assertTrue( self.tb.could_build( self.procName3 ) ) + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/Bindings/testprocessorvisibility.py b/Testing/Python/Bindings/testprocessorvisibility.py new file mode 100644 index 00000000..5acc5741 --- /dev/null +++ b/Testing/Python/Bindings/testprocessorvisibility.py @@ -0,0 +1,25 @@ + +""" + testprocessorvisibility.py + + Created on: Sep 22, 2025 + Author: N.S. Oblath +""" +import unittest + +import nymph_bindings #, _nymph_testing + + +class TestProcessorVisibility(unittest.TestCase): + tb = nymph_bindings.processor.ProcessorToolbox() + + + '''Testing whether the processor toolbox knows about certain processors''' + def test_could_build(self): + self.assertFalse( self.tb.could_build( 'does not exist' ) ) # Something that definitely shouldn't exist + self.assertTrue( self.tb.could_build( 'hello-world-cpp' ) ) # hw-cpp exists in the C++ library, and therefore should be visible + self.assertFalse( self.tb.could_build( 'hello-world-python' ) ) # Binding doesn't know about hw-python + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/Bindings/testprocfactory.py b/Testing/Python/Bindings/testprocfactory.py new file mode 100644 index 00000000..b809d996 --- /dev/null +++ b/Testing/Python/Bindings/testprocfactory.py @@ -0,0 +1,33 @@ +""" + testprocfactory.py + + Testing of the Processor factory functions + + Created on: Sep 24, 2025 + Author: N.S. Oblath +""" +import unittest + +import nymph_bindings as nb +import scarab + + +class TestNoProc(unittest.TestCase): + '''Testing the situation where the processor type doesn't exist''' + def test_noproc(self): + with self.assertRaises(RuntimeError) as cm: + nb.processor._create( "blah", "blah-blah" ) + the_exception = cm.exception + self.assertEqual(str(the_exception), "Did not find processor with type ") + +class TestCppProc(unittest.TestCase): + '''Testing the creation of a C++ processor, which won't work''' + def test_cppproc(self): + with self.assertRaises(RuntimeError) as cm: + nb.processor._create( "hello-world-cpp", "hw" ) + the_exception = cm.exception + self.assertEqual(str(the_exception), "Registrar did not cast correctly for ") + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/Bindings/testsignal.py b/Testing/Python/Bindings/testsignal.py new file mode 100644 index 00000000..abc1abf7 --- /dev/null +++ b/Testing/Python/Bindings/testsignal.py @@ -0,0 +1,39 @@ + +""" + testsignal.py + + Created on: Jun 14, 2022 + Author: F. Thomas +""" + +__all__ = [] + +import nymph_bindings +import scarab + +class TestProcessor(nymph_bindings.processor._Processor): + + def configure(self, param_node): + + param_dict = param_node.to_python() + self.x = param_dict['x'] + +def main(args): + + testprocessor = TestProcessor('test') + + param = scarab.ParamValue( 1.0 ) + param_node = scarab.ParamNode() + param_node.add('x', param) + + testprocessor.configure(param_node) + + signal = nymph_bindings.processor._SignalData('test-signal', testprocessor) + + print(signal.name) + print(testprocessor.x) + return 0 + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv)) diff --git a/Testing/Python/nymph/testdata.py b/Testing/Python/nymph/testdata.py new file mode 100644 index 00000000..8c01cd76 --- /dev/null +++ b/Testing/Python/nymph/testdata.py @@ -0,0 +1,47 @@ + +""" + nymph/testdata.py + + Created on: Jun 13, 2022 + Author: P. T. Surukuchi +""" +import unittest + +import nymph + +'''Data class with integer variables''' +class Data1(nymph.data._Data): + + def __init__(self, test_var1=0, test_var2=5): + super().__init__() + self.test_var1 = test_var1 + self.test_var2 = test_var2 + +'''Data class with floating point variables''' +class Data2(nymph.data._Data): + + def __init__(self, test_var1=0.0, test_var2=10.0): + super().__init__() + self.test_var1 = test_var1 + self.test_var2 = test_var2 + +# The way to access member variables need to be modified if getters/setters are used in the python version +class TestDataMethods(unittest.TestCase): + '''Testing of integer type data''' + data1=Data1() + def test_idata_assignment(self): + self.assertEqual(self.data1.test_var1,0) + self.assertEqual(self.data1.test_var2,5) + + '''Testing of floating point type data''' + data2=Data2() + def test_fdata_assignment(self): + self.assertAlmostEqual(self.data2.test_var1,0.0) + self.assertAlmostEqual(self.data2.test_var2,10.0) + + #Change value and test + self.data2.test_var2=20.0 + self.assertAlmostEqual(self.data2.test_var2,20.0) + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/nymph/testprocessor.py b/Testing/Python/nymph/testprocessor.py new file mode 100644 index 00000000..fe02906b --- /dev/null +++ b/Testing/Python/nymph/testprocessor.py @@ -0,0 +1,28 @@ + +""" + testprocessor.py + + Created on: Sep 22, 2025 + Author: N.S. Oblath +""" +import unittest + +import nymph #, _nymph_testing +import scarab + + +class TestProcessor(unittest.TestCase): + + '''Testing whether the processor toolbox knows about certain processors''' + def test_basics(self): + proc = nymph.processor.Processor( "test" ) + with self.assertRaises(NotImplementedError): + proc.configure(scarab.to_param({})) + + def test_helloworld(self): + proc = nymph.processor.HelloWorldPython( "hw" ) + self.assertIsInstance( proc, nymph.processor.Processor ) + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/nymph/testprocessorvisibility.py b/Testing/Python/nymph/testprocessorvisibility.py new file mode 100644 index 00000000..2612d6ae --- /dev/null +++ b/Testing/Python/nymph/testprocessorvisibility.py @@ -0,0 +1,24 @@ + +""" + testprocessorvisibility.py + + Created on: Sep 22, 2025 + Author: N.S. Oblath +""" +import unittest + +import nymph #, _nymph_testing + + +class TestProcessorVisibility(unittest.TestCase): + tb = nymph.processor.ProcessorToolbox() + + '''Testing whether the processor toolbox knows about certain processors''' + def test_could_build(self): + self.assertFalse( self.tb.could_build( 'does not exist' ) ) + self.assertTrue( self.tb.could_build( 'hello-world-cpp' ) ) + self.assesrtTrue( self.tb.could_build( 'hello-world-python' ) ) + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/Python/nymph/testprocfactory.py b/Testing/Python/nymph/testprocfactory.py new file mode 100644 index 00000000..ba828daa --- /dev/null +++ b/Testing/Python/nymph/testprocfactory.py @@ -0,0 +1,38 @@ +""" + testprocfactory.py + + Testing of the Processor factory functions + + Created on: Sep 29, 2025 + Author: N.S. Oblath +""" +import unittest + +import nymph +import scarab + + +class TestNoProc(unittest.TestCase): + '''Testing the situation where the processor type doesn't exist''' + def test_noproc(self): + with self.assertRaises(RuntimeError) as cm: + nymph.processor.create( "blah", "blah-blah" ) + the_exception = cm.exception + self.assertEqual(str(the_exception), "Did not find processor with type ") + +class TestCppProc(unittest.TestCase): + '''Testing the creation of a C++ processor, which won't work''' + def test_cppproc(self): + with self.assertRaises(RuntimeError) as cm: + nymph.processor.create( "hello-world-cpp", "hw" ) + the_exception = cm.exception + self.assertEqual(str(the_exception), "Registrar did not cast correctly for ") + +class TestPyProc(unittest.TestCase): + '''Testing the creation of a Python processor, which should work''' + def test_pyproc(self): + proc = nymph.processor.create( "hello-world-py", "hw" ) + + +if __name__ == '__main__': + unittest.main() diff --git a/Testing/README.md b/Testing/README.md new file mode 100644 index 00000000..3b0601b3 --- /dev/null +++ b/Testing/README.md @@ -0,0 +1,63 @@ +# README for Nymph/Testing + +## C++ + +### Testing infrastructure: Catch2 + +Nymph uses the Catch2 testing framework for unit tests. See these links for more information: + +* [Main GitHub page](https://github.com/catchorg/Catch2) +* [Tutorial](https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md) + +### Building tests + +In CMake, enable the option `Nymph_ENABLE_TESTING`, and build. + +The testing executable, `RunTests`, will be installed in `install_prefix/bin`. + +### Running tests + +To see the available options for running tests, you can do: + +``` +> bin/RunTests -h +``` + +To simply run all of the tests, you can do: + +``` +> bin/RunTests +``` + +Or if you want to run a specific test, you can do: + +``` +> bin/RunTests [name] +``` + +For further documentation on using Catch2, see the [tutorial](https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md). + +## Python + +Python testing is divided into two sections: binding and nymph. The former tests the C++-to-python binding and doesn't +require the Python `nymph` package. Those tests will import the `nymph_bindings` package. It requires having built the +C++ side of Nymph with the `Nymph_ENABLE_PYTHON` flag set to `TRUE`. + +The nymph tests test the Python `nymph` package and requires having installed the nymph package (e.g. with `pip install`). +If the package is not installed in a system location, you'll need to source `add_lib_python_path.sh` before running the tests. + +### Testing infrastructure: unittest + + + +### Binding tests + +#### Running tests + +Individual tests can be run by specifying the module name or the python file. +From the CL, in the `Testing/Python` directory, you can run: + +``` +> python -m unittest Bindings/test[name].py +``` + diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..227cea21 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.0.0 diff --git a/cmake/Nymph.cmake b/cmake/Nymph.cmake index 003dab92..67521d5c 100644 --- a/cmake/Nymph.cmake +++ b/cmake/Nymph.cmake @@ -49,13 +49,12 @@ macro (nymph_process_options) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAG_WARNINGS}") if( UNIX AND NOT APPLE ) - pbuilder_add_ext_libraries (rt) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed") endif( UNIX AND NOT APPLE ) endmacro () macro (nymph_build_executables) - add_subdirectory (${NYMPH_DIR}/Executables/Main) + add_subdirectory (${NYMPH_DIR}/Cpp/Executables_v1) endmacro () diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..356c608c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = [ + "setuptools>=42", +] +build-backend = "setuptools.build_meta" + +[project] +name = "nymph" +#dependencies = [ +# "nymph_bindings @ file:///src/build-ny22py-debug-docker/lib", +#] +dynamic = ["version"] + +[tool.setuptools.packages.find] +where = ["Python"] + +[tool.setuptools.dynamic] +version = {file = ["VERSION"]}