diff --git a/src/reactmicp/solver/reactive_transport_solver.cpp b/src/reactmicp/solver/reactive_transport_solver.cpp index 6361a59..d3ca7e8 100644 --- a/src/reactmicp/solver/reactive_transport_solver.cpp +++ b/src/reactmicp/solver/reactive_transport_solver.cpp @@ -1,314 +1,315 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #include "reactive_transport_solver.hpp" #include "staggers_base/staggers_base.hpp" #include "specmicp_common/log.hpp" #include "specmicp_common/timer.hpp" namespace specmicp { namespace reactmicp { namespace solver { namespace internal { +//! \brief Residuals info to check convergence in coupling algorithm // This contains internal information for the reactive transport solver // It is used to check the convergence struct ReactiveTransportResiduals { - index_t nb_iterations; - scalar_t transport_residual_0; - scalar_t transport_residuals; - scalar_t update; + index_t nb_iterations; //!< The number of iteratons + scalar_t transport_residual_0; //!< The residuals at the beginning of the timestep + scalar_t transport_residuals; //!< The current residuals + scalar_t update; //!< Variables update ReactiveTransportResiduals(): nb_iterations(0), transport_residual_0(-1), transport_residuals(-1), update(-1) {} }; } // end namespace internal // ReactiveTransportSolver:: // Solve a timestep // // Attention : This function uses goto to handle return code and performance ReactiveTransportReturnCode ReactiveTransportSolver::solve_timestep( scalar_t timestep, VariablesBasePtr variables ) { return solve_timestep(timestep, variables.get()); } ReactiveTransportReturnCode ReactiveTransportSolver::solve_timestep( scalar_t timestep, VariablesBase* variables ) { // start the timer Timer tot_timer; tot_timer.start(); // initialization internal::ReactiveTransportResiduals residuals; reset_perfs(); get_perfs().timestep = timestep; - // copy of variables // ? m_transport_stagger->initialize_timestep(timestep, variables); m_chemistry_stagger->initialize_timestep(timestep, variables); m_upscaling_stagger->initialize_timestep(timestep, variables); - // + // get convergence criterion from transport stagger residuals.transport_residual_0 = m_transport_stagger->get_residual_0(variables); + // run the first iteration ReactiveTransportReturnCode retcode = one_iteration(variables, residuals); // check for failure if (retcode < ReactiveTransportReturnCode::StaggerFailure) { ERROR << "Failed to solve the iteration, return code : " << (int) retcode; goto set_return; } retcode = check_convergence(variables, residuals, retcode); ++residuals.nb_iterations; // if sequential non-iterative algorithm if (get_options().is_snia()) { goto end; } - // else + // else run the other iterations while (retcode == ReactiveTransportReturnCode::NotConvergedYet) { retcode = one_iteration(variables, residuals); // check for failure if (retcode < ReactiveTransportReturnCode::StaggerFailure) { ERROR << "Failed to solve the iteration, return code : " << (int) retcode; goto set_return; } retcode = check_convergence(variables, residuals, retcode); ++residuals.nb_iterations; } // wrapup end: // upscaling, if needed if (not get_options().implicit_upscaling) { Timer timer; timer.start(); StaggerReturnCode upscaling_ret_code = m_upscaling_stagger->restart_timestep(variables); if (upscaling_ret_code <= StaggerReturnCode::NotConvergedYet) { retcode = ReactiveTransportReturnCode::UpscalingFailure; goto set_return; } else if (upscaling_ret_code == StaggerReturnCode::UserTermination) retcode = ReactiveTransportReturnCode::UserTermination; timer.stop(); const scalar_t utime = timer.elapsed_time(); m_timer.upscaling_time += utime; get_perfs().upscaling_time += utime; } // record performance and return code set_return: if (retcode <= ReactiveTransportReturnCode::NotConvergedYet) { ERROR << "Current Residual : " << residuals.transport_residuals/residuals.transport_residual_0; m_transport_stagger->print_debug_information(variables); } else if (retcode == ReactiveTransportReturnCode::GoodEnough) { WARNING << "Good enough convergence was triggered"; m_transport_stagger->print_debug_information(variables); } // perfs informations get_perfs().nb_iterations = residuals.nb_iterations; get_perfs().return_code = retcode; get_perfs().residuals = residuals.transport_residuals/residuals.transport_residual_0; get_perfs().update = residuals.update; // at the end, stop timer, and record time tot_timer.stop(); get_perfs().total_time = tot_timer.elapsed_time(); // timer for staggers are already registered return retcode; } ReactiveTransportReturnCode ReactiveTransportSolver::one_iteration( VariablesBasePtr variables, internal::ReactiveTransportResiduals& residuals ) { return one_iteration(variables.get(), residuals); } -//! \brief Set the clock +// Set the clock void ReactiveTransportSolver::set_clock(scalar_t clock_time) { m_upscaling_stagger->set_clock(clock_time); } ReactiveTransportReturnCode ReactiveTransportSolver::one_iteration( VariablesBase* variables, internal::ReactiveTransportResiduals& residuals ) { Timer timer; bool bypass = false; bool user_termination = false; // Transport // --------- timer.start(); StaggerReturnCode transport_ret_code = m_transport_stagger->restart_timestep(variables); if (transport_ret_code <= StaggerReturnCode::NotConvergedYet) { return ReactiveTransportReturnCode::TransportFailure; } else if (transport_ret_code == StaggerReturnCode::ErrorMinimized) { bypass = true; } timer.stop(); const scalar_t ttime = timer.elapsed_time(); m_timer.transport_time += ttime; get_perfs().transport_time += ttime; // Chemistry // --------- timer.start(); StaggerReturnCode chemistry_ret_code = m_chemistry_stagger->restart_timestep(variables); if (chemistry_ret_code <= StaggerReturnCode::NotConvergedYet) { return ReactiveTransportReturnCode::ChemistryFailure; } timer.stop(); const scalar_t ctime = timer.elapsed_time(); m_timer.chemistry_time += ctime; get_perfs().chemistry_time += ctime; // Upscaling // --------- if (get_options().implicit_upscaling) { timer.start(); StaggerReturnCode upscaling_ret_code = m_upscaling_stagger->restart_timestep(variables); if (upscaling_ret_code <= StaggerReturnCode::NotConvergedYet) { return ReactiveTransportReturnCode::UpscalingFailure; } else if (upscaling_ret_code == StaggerReturnCode::UserTermination) { user_termination = true; } timer.stop(); const scalar_t utime = timer.elapsed_time(); get_perfs().upscaling_time += utime; m_timer.upscaling_time += utime; } // Final residuals // --------------- residuals.transport_residuals = m_transport_stagger->get_residual(variables); residuals.update = m_transport_stagger->get_update(variables); if (user_termination) return ReactiveTransportReturnCode::UserTermination; else if (bypass) return ReactiveTransportReturnCode::TransportBypass; else return ReactiveTransportReturnCode::NotConvergedYet; } // Check the convergence ReactiveTransportReturnCode ReactiveTransportSolver::check_convergence( VariablesBase* _, const internal::ReactiveTransportResiduals& residuals, ReactiveTransportReturnCode iteration_return_code ) { const scalar_t relative_residual = residuals.transport_residuals/residuals.transport_residual_0; // Residual if (relative_residual < get_options().residuals_tolerance or residuals.transport_residuals < get_options().absolute_residuals_tolerance) { return ReactiveTransportReturnCode::ResidualMinimized; } // Step else if (residuals.update < get_options().step_tolerance) { if (relative_residual < get_options().good_enough_tolerance) { return ReactiveTransportReturnCode::ErrorMinimized; } else return ReactiveTransportReturnCode::StationaryPoint; } else if (iteration_return_code == ReactiveTransportReturnCode::TransportBypass) { return ReactiveTransportReturnCode::TransportBypass; } else if (iteration_return_code == ReactiveTransportReturnCode::UserTermination) { return ReactiveTransportReturnCode::UserTermination; } // Number of iterations else if (residuals.nb_iterations >= get_options().maximum_iterations) { if (relative_residual < get_options().good_enough_tolerance) { return ReactiveTransportReturnCode::GoodEnough; } return ReactiveTransportReturnCode::MaximumIterationsReached; } return ReactiveTransportReturnCode::NotConvergedYet; } } // end namespace solver } // end namespace reactmicp } // end namespace specmicp diff --git a/src/reactmicp/solver/reactive_transport_solver.hpp b/src/reactmicp/solver/reactive_transport_solver.hpp index d58897f..6cbcf0b 100644 --- a/src/reactmicp/solver/reactive_transport_solver.hpp +++ b/src/reactmicp/solver/reactive_transport_solver.hpp @@ -1,195 +1,209 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #ifndef SPECMICP_REACTMICP_SOLVER_REACTIVETRANSPORTSOLVER_HPP #define SPECMICP_REACTMICP_SOLVER_REACTIVETRANSPORTSOLVER_HPP //! \file reactive_transport_solver.hpp The reactive transport solver //! \namespace specmicp::reactmicp::solver Namespace containing the algorithms for the reactive transport solver #include #include "reactive_transport_solver_structs.hpp" #include "specmicp_common/types.hpp" #include "specmicp_common/options_handler.hpp" #include "specmicp_common/perfs_handler.hpp" // forward declarations // ==================== namespace specmicp { namespace reactmicp { namespace solver { class VariablesBase; //! \brief Shared pointer to the variables using VariablesBasePtr = std::shared_ptr; class TransportStaggerBase; class ChemistryStaggerBase; class UpscalingStaggerBase; //! \brief A shared pointer to the transport stagger //! //! \sa TransportStaggerBase using TransportStaggerPtr = std::shared_ptr; //! \brief A shared pointer to the chemistry stagger //! //! \sa ChemistryStaggerBase using ChemistryStaggerPtr = std::shared_ptr; //! \brief A shared pointer to the upscaling stagger //! //! \sa UpscalingStaggerBase using UpscalingStaggerPtr = std::shared_ptr; namespace internal { struct ReactiveTransportResiduals; } // end namespace internal } // end namespace solver } // end namespace reactmicp } // end namespace specmicp // Reactive Transport Solver // ========================= namespace specmicp { //! \namespace specmicp::reactmicp //! \brief The ReactMiCP solver and systems namespace reactmicp { //! \namespace specmicp::reactmicp::solver //! \brief The ReactMiCP solver namespace solver { //! \brief The reactive transport solver //! //! This class solves a reactive transport problem. //! The details of the problem are implemented in the staggers. //! -//! There is three staggers : +//! There are three staggers : //! - The transport stagger //! - The chemistry stagger //! - The upscaling stagger //! //! The transport stagger also implements the residuals used to checked the convergence. //! -//! This algorithm do not update, modify the variables. +//! This algorithm do not directly update, nor modify the variables. //! The details must be implemented in the staggers. //! The variables shared by the algorithm is a shared_ptr to the abstract base class specmicp::reactmicp::solver::VariablesBase //! To be useful, this variable must be casted to the true class in the staggers. +//! +//! This class only calls the staggers in the right order to run a timestep. +//! How to use : +//! - Setup the class by providing the staggers +//! +//! This class is usually used with the runner specmicp::reactmicp::solver::ReactiveTransportRunner +//! +//! \sa specmicp::reactmicp::solver::ReactiveTransportRunner class SPECMICP_DLL_PUBLIC ReactiveTransportSolver: public OptionsHandler, public PerformanceHandler { public: - //! \brief Build a reactive transport + //! \brief Build a reactive transport problem + //! + //! //! //! \param transport_stagger shared_ptr to a transport stagger //! \param chemistry_stagger shared_ptr to a chemistry stagger //! \param upscaling_stagger shared_ptr to an upscaling stagger ReactiveTransportSolver( TransportStaggerPtr transport_stagger, ChemistryStaggerPtr chemistry_stagger, UpscalingStaggerPtr upscaling_stagger ): m_transport_stagger(transport_stagger), m_chemistry_stagger(chemistry_stagger), m_upscaling_stagger(upscaling_stagger) {} //! \brief Solve a timestep //! //! \param timestep The duration of the timestep //! \param variables shared_ptr to the variables ReactiveTransportReturnCode solve_timestep( scalar_t timestep, VariablesBasePtr variables ); //! \brief Solve a timestep //! //! \param timestep The duration of the timestep - //! \param variables shared_ptr to the variables + //! \param variables pointer to the variables ReactiveTransportReturnCode solve_timestep( scalar_t timestep, VariablesBase* variables ); - //! \brief Return the timer + //! \brief Return the time spent in each staggers ReactiveTransportTimer& get_timer() {return m_timer;} - //! \brief Set the clock + //! \brief Set the simulation clock + //! + //! This information is passed to the upscaling stagger. + //! It can be used to change boundary conditions during the simulations + //! or adapt the model as function of time. void set_clock(scalar_t clock_time); private: // members //! \brief One iteration inside the timestep //! //! \param variables shared_ptr to the variables //! \param residuals struct containing the residuals information ReactiveTransportReturnCode SPECMICP_DLL_LOCAL one_iteration( VariablesBasePtr variables, internal::ReactiveTransportResiduals& residuals ); //! \brief One iteration inside the timestep //! //! \param variables raw ptr to the variables //! \param residuals struct containing the residuals information ReactiveTransportReturnCode SPECMICP_DLL_LOCAL one_iteration( VariablesBase* variables, internal::ReactiveTransportResiduals& residuals ); //! \brief Check the convergence //! //! \param variables shared_ptr to the variables //! \param residuals struct containing the residuals information ReactiveTransportReturnCode SPECMICP_DLL_LOCAL check_convergence( VariablesBase* variables, const internal::ReactiveTransportResiduals& residuals, ReactiveTransportReturnCode iteration_return_code ); private: // attributes TransportStaggerPtr m_transport_stagger; //!< The transport stagger ChemistryStaggerPtr m_chemistry_stagger; //!< The chemistry stagger UpscalingStaggerPtr m_upscaling_stagger; //!< The upscaling stagger - ReactiveTransportTimer m_timer; + ReactiveTransportTimer m_timer; //!< Time spent in each stagger }; } // end namespace solver } // end namespace reactmicp } // end namespace specmicp #endif // SPECMICP_REACTMICP_SOLVER_REACTIVETRANSPORTSOLVER_HPP diff --git a/src/reactmicp/solver/runner.hpp b/src/reactmicp/solver/runner.hpp index 8f3f109..5fc38d6 100644 --- a/src/reactmicp/solver/runner.hpp +++ b/src/reactmicp/solver/runner.hpp @@ -1,166 +1,171 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #ifndef SPECMICP_REACTMICP_SOLVER_RUNNER_HPP #define SPECMICP_REACTMICP_SOLVER_RUNNER_HPP /*! \file reactmicp/solver/runner.hpp \brief Run the reactive transport solver for several timestep */ #include "specmicp_common/types.hpp" #include "specmicp_common/pimpl_ptr.hpp" #include #include #include namespace specmicp { namespace reactmicp { namespace solver { // forward declarations class ReactiveTransportSolver; struct ReactiveTransportOptions; struct ReactiveTransportPerformance; struct ReactiveTransportTimer; struct TimestepperOptions; class VariablesBase; using VariablesBasePtr = std::shared_ptr; //! \struct SimulationInformation -//! \brief Information about the simulation +//! \brief Contains some basic information about the simulation (name, output dir,...) +//! struct SPECMICP_DLL_PUBLIC SimulationInformation { std::string name; //!< Name of the simulation std::string output_prefix; //!< prefix for the output files std::string working_dir {""}; //!< Working directory (to complete filepath) bool print_iter_info{true}; //!< If true, print the iteration informations - scalar_t output_step; //!< output step + scalar_t output_step; //!< Output step, runner will output every 'output_step' seconds //! \brief Constructor //! //! \param name_simul name of the simulation //! \param outputstep time (in s) between too save in outputs files SimulationInformation(std::string name_simul, scalar_t outputstep): name(name_simul), output_prefix(name_simul+"_"), output_step(outputstep) {} //! \brief Complete the name of an output file //! //! \param name base name of the file //! \param suffix the suffix for the name //! \return the complete file name std::string complete_filepath( const std::string& name, const std::string& suffix ) const { return output_prefix+name+"."+suffix; } //! \brief Complete the name of an output file //! //! \param name base name of the file //! \param suffix the suffix for the name //! \return the complete file name std::string complete_filepath( std::string&& name, std::string&& suffix ) const { return output_prefix+name+"."+suffix; } }; //! \brief Signature of an output function using output_f = std::function; -//! \brief No output = default +//! \brief default = no output inline void dummy_output(scalar_t _, VariablesBasePtr __) {} -//! \brief Run the reactive transport solver until a target +//! \brief Run the reactive transport solver until a time target is reached +//! +//! \sa specmicp::reactmicp::solver::ReactiveTransportSolver class SPECMICP_DLL_PUBLIC ReactiveTransportRunner { public: //! //! \param solver the reactive transport solver //! \param lower_dt_bound the timestep lower bound //! \param upper_dt_bound the timestep upper bound //! \param info information about the simulation ReactiveTransportRunner(ReactiveTransportSolver& solver, scalar_t lower_dt_bound, scalar_t upper_dt_bound, const SimulationInformation& info); ~ReactiveTransportRunner(); //! \brief Run the solver until a certain target //! //! \param target the target to reach //! \param variables the variables to use scalar_t run_until(scalar_t target, VariablesBasePtr variables); //! \brief Set the output function + //! + //! The output is every 'SimulationInformation.output_step' second void set_output_policy(output_f output_policy); //! \brief Returns the options of the reactive transport solver ReactiveTransportOptions& get_options(); //! \brief Returns the options of the timestepper TimestepperOptions& get_timestepper_options(); //! \brief Returns the performance struct of the reactive transport solver ReactiveTransportPerformance& get_perfs(); //! \brief Returns the timer of the reactive transport solver ReactiveTransportTimer& get_timer(); //! \brief Return the path to the iter file //! //! Empty string if no such file std::string get_iter_file_path(); private: struct SPECMICP_DLL_LOCAL ReactiveTransportRunnerImpl; utils::pimpl_ptr m_impl; //!< implementation }; } // end namespace solver } // end namespace reactmicp } // end namespace specmicp #endif // SPECMICP_REACTMICP_SOLVER_RUNNER_HPP diff --git a/src/specmicp_common/io/safe_config.cpp b/src/specmicp_common/io/safe_config.cpp index f0e95f7..d1ac945 100644 --- a/src/specmicp_common/io/safe_config.cpp +++ b/src/specmicp_common/io/safe_config.cpp @@ -1,825 +1,825 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #include "safe_config.hpp" #include "specmicp_common/string_algorithms.hpp" #include #include #include #include #include "specmicp_common/log.hpp" #include "specmicp_common/compat.hpp" #define SECTION_USER_VARIABLES "vars" #define SECTION_INT_USER_VARIABLES "int" #define SECTION_FLOAT_USER_VARIABLES "float" #define NAME_PATH_ROOT "root" namespace specmicp { namespace io { // ================ // ConfigFileHandle // ================ struct YAMLConfigFileHandle { std::string m_filename; std::unordered_map m_float_vars; std::unordered_map m_int_vars; YAMLConfigFileHandle(const std::string& filename): m_filename(filename) {} }; // ============ // ConfigHandle // ============ struct YAMLConfigHandle::YAMLConfigHandleImpl { YAML::Node m_node; std::shared_ptr m_file; std::string m_path; std::string m_section_name; std::vector m_asked_attrs; YAMLConfigHandleImpl( const YAML::Node& node, std::shared_ptr const file_handle, const std::string& section, const std::string& path): m_node(node), m_file(file_handle), m_path(path), m_section_name(section) { if (node.IsMap()) { m_asked_attrs.reserve(node.size()); } } ~YAMLConfigHandleImpl(); scalar_t parse_scalar_expr(const std::string& expr) { return utils::parse_expression( expr, m_file->m_float_vars ); } index_t parse_int_expr(const std::string& expr) { return utils::parse_expression( expr, m_file->m_int_vars ); } bool parse_bool_expr(const std::string& expr) { return utils::string_to_bool(expr); } void record_attr(const std::string& attr) { m_asked_attrs.push_back(attr); } void assert_open() { if (m_file == nullptr) { throw std::runtime_error( "Information from file '" + m_file->m_filename + "' are lost..."); } } template std::vector list_to_vector( const std::string& list, YAMLConfigHandle& parent ); }; YAMLConfigHandle::YAMLConfigHandle(const YAML::Node& node, const std::shared_ptr file_handle, const std::string& section, const std::string& path): m_impl(utils::make_pimpl( node, file_handle, section, path)) { } YAMLConfigHandle& YAMLConfigHandle::operator= (const YAMLConfigHandle& other) { m_impl = other.m_impl; m_impl->assert_open(); return *this; } YAMLConfigHandle::YAMLConfigHandle(const YAMLConfigHandle& other): m_impl(other.m_impl) { m_impl->assert_open(); } YAMLConfigHandle::YAMLConfigHandle(YAMLConfigHandle&& other): m_impl(std::move(other.m_impl)) { m_impl->assert_open(); } YAMLConfigHandle::~YAMLConfigHandle() = default; uindex_t YAMLConfigHandle::size() { return static_cast(m_impl->m_node.size()); } void YAMLConfigHandle::set_file_handle( std::shared_ptr file_handle ) { m_impl->m_file = file_handle; m_impl->assert_open(); } bool YAMLConfigHandle::is_map() { return m_impl->m_node.IsMap(); } bool YAMLConfigHandle::is_map(const std::string& node) { bool val = false; if (m_impl->m_node[node]) { val = m_impl->m_node[node].IsMap(); } return val; } bool YAMLConfigHandle::is_sequence() { return m_impl->m_node.IsSequence(); } bool YAMLConfigHandle::is_sequence(const std::string& node) { bool val = false; if (m_impl->m_node[node]) { val = m_impl->m_node[node].IsSequence(); } return val; } bool YAMLConfigHandle::has_node(const std::string& node) { return (m_impl->m_node[node]); } bool YAMLConfigHandle::has_node(uindex_t index) { return (m_impl->m_node[index]); } bool YAMLConfigHandle::has_section(const std::string& section) { bool val = false; if (m_impl->m_node[section]) { YAML::Node attr = m_impl->m_node[section]; val = (attr.IsMap() or attr.IsSequence()); } return val; } bool YAMLConfigHandle::has_section(uindex_t index) { bool val = false; if (m_impl->m_node[index]) { YAML::Node attr = m_impl->m_node[index]; val = (attr.IsMap() or attr.IsSequence()); } return val; } YAMLConfigHandle YAMLConfigHandle::get_section(const std::string& section) { if (not has_section(section)) { report_error(YAMLConfigError::MissingRequiredSection, section); } m_impl->record_attr(section); return YAMLConfigHandle(m_impl->m_node[section], m_impl->m_file, section, m_impl->m_path+"->"+section); } YAMLConfigHandle YAMLConfigHandle::get_section(uindex_t index) { auto section_name = "["+std::to_string(index)+"]"; if (not has_section(index)) { report_error(YAMLConfigError::MissingRequiredSection, section_name ); } return YAMLConfigHandle(m_impl->m_node[index], m_impl->m_file, section_name, m_impl->m_path+section_name); } bool YAMLConfigHandle::has_attribute(const std::string& attribute) { bool val = false; if (m_impl->m_node[attribute]) { YAML::Node attr = m_impl->m_node[attribute]; val = not (attr.IsMap() or attr.IsSequence()); } return val; } #ifndef SPC_DOXYGEN_SHOULD_SKIP_THIS #define report_conversion_error(attribute, type) \ report_error( \ YAMLConfigError::ConversionError, \ "Conversion of attribute '" \ + attribute + "' to " type + " : " + e.what() \ ); template <> scalar_t YAMLConfigHandle::get_attribute(const std::string& attribute) { m_impl->record_attr(attribute); YAML::Node node = m_impl->m_node[attribute]; scalar_t val; m_impl->assert_open(); try { val = m_impl->parse_scalar_expr(node.as()); } catch (const std::invalid_argument& e) { report_conversion_error(attribute, "scalar"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of attribute '" + attribute + "' to scalar : " + e.what() ); } return val; } template <> index_t YAMLConfigHandle::get_attribute(const std::string& attribute) { m_impl->record_attr(attribute); YAML::Node node = m_impl->m_node[attribute]; index_t val; m_impl->assert_open(); try { val = m_impl->parse_int_expr(node.as()); } catch (const std::invalid_argument& e) { report_conversion_error(attribute, "integer"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of attribute '" + attribute + "' to integer : " + e.what() ); } return val; } template <> uindex_t YAMLConfigHandle::get_attribute(const std::string& attribute) { m_impl->record_attr(attribute); YAML::Node node = m_impl->m_node[attribute]; index_t val; m_impl->assert_open(); try { val = m_impl->parse_int_expr(node.as()); } catch (const std::invalid_argument& e) { report_conversion_error(attribute, "integer"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of attribute '" + attribute + "' to integer : " + e.what() ); } if (val < 0) { report_error(YAMLConfigError::InvalidArgument, "Expected positive value for attribute " + attribute + ", got '" + std::to_string(val) + "'."); } return val; } template <> bool YAMLConfigHandle::get_attribute(const std::string& attribute) { m_impl->record_attr(attribute); YAML::Node node = m_impl->m_node[attribute]; bool val; try { val = m_impl->parse_bool_expr(node.as()); } catch (const std::invalid_argument& e) { report_conversion_error(attribute, "boolean"); } return val; } template <> std::string YAMLConfigHandle::get_attribute(const std::string& attribute) { m_impl->record_attr(attribute); return m_impl->m_node[attribute].as(); } #undef report_conversion_error // list #define report_list_conversion_error(index, type) \ report_error( \ YAMLConfigError::ConversionError, \ "Conversion of value at index '" \ + std::to_string(index) + "' to " \ type + " : " + e.what() \ ); template <> scalar_t YAMLConfigHandle::get_value(uindex_t ind) { YAML::Node node = m_impl->m_node[ind]; scalar_t val; m_impl->assert_open(); try { val = m_impl->parse_scalar_expr(node.as()); } catch (const std::invalid_argument& e) { report_list_conversion_error(ind, "scalar"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of value at index '" + std::to_string(ind) + "' to scalar : " + e.what() ); } return val; } template <> index_t YAMLConfigHandle::get_value(uindex_t ind) { YAML::Node node = m_impl->m_node[ind]; index_t val; m_impl->assert_open(); try { val = m_impl->parse_int_expr(node.as()); } catch (const std::invalid_argument& e) { report_list_conversion_error(ind, "integer"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of value at index '" + std::to_string(ind) + "' to integer : " + e.what() ); } return val; } template <> uindex_t YAMLConfigHandle::get_value(uindex_t ind) { YAML::Node node = m_impl->m_node[ind]; index_t val; m_impl->assert_open(); try { val = m_impl->parse_int_expr(node.as()); } catch (const std::invalid_argument& e) { report_list_conversion_error(ind, "integer"); } catch (const std::out_of_range& e) { report_error(YAMLConfigError::UnknownVariable, "Conversion of value at index '" + std::to_string(ind) + "' to integer : " + e.what() ); } if (val < 0) { report_error(YAMLConfigError::InvalidArgument, "Expected positive value at index " + std::to_string(ind) + ", got '" + std::to_string(val) + "'."); } return val; } template <> bool YAMLConfigHandle::get_value(uindex_t ind) { YAML::Node node = m_impl->m_node[ind]; bool val; try { val = m_impl->parse_bool_expr(node.as()); } catch (const std::invalid_argument& e) { report_list_conversion_error(ind, "boolean"); } return val; } template <> std::string YAMLConfigHandle::get_value(uindex_t ind) { return m_impl->m_node[ind].as(); } #endif // SPC_DOXYGEN_SHOULD_SKIP_THIS void YAMLConfigHandle::report_error( YAMLConfigError error_type, const std::string& error_msg ) { std::string header; m_impl->assert_open(); if (m_impl->m_file->m_filename != "") { header += "\n\t - in file " + m_impl->m_file->m_filename; } if (m_impl->m_path != "") { header += "\n\t - in section " + m_impl->m_path + "\n"; } switch (error_type) { case YAMLConfigError::ConversionError: throw std::invalid_argument("Conversion error : " + header + error_msg); break; case YAMLConfigError::UnknownVariable: throw std::out_of_range("Unknown variable :" + header + error_msg); break; case YAMLConfigError::MissingRequiredAttribute: throw std::out_of_range("Missing required attribute : " + error_msg + header); break; case YAMLConfigError::MissingRequiredSection: throw std::out_of_range("Missing required section : " + error_msg + header); break; default: throw std::runtime_error(error_msg + header); break; } } YAMLConfigHandle::YAMLConfigHandleImpl::~YAMLConfigHandleImpl() { if (m_node.IsMap()) { assert_open(); // report untouched attribute std::vector unread; for (auto it=m_node.begin(); it!=m_node.end(); ++it) { auto is_read = std::find( m_asked_attrs.cbegin(), m_asked_attrs.cend(), it->first.as() ); if (is_read == m_asked_attrs.cend()) { WARNING << "Unread key : " + it->first.as() + " in file '" + m_file->m_filename + "'" + " in section '" + m_path + "'."; } } } } // =========== // MapIterator // =========== YAMLConfigHandle::MapIterator::MapIterator(MapIterator&& other): m_handle(other.m_handle), m_true_it(other.m_true_it.release()) { } YAMLConfigHandle::MapIterator::~MapIterator() = default; std::pair YAMLConfigHandle::MapIterator::operator* () { m_handle->record_attr((*m_true_it)->first.as()); return { (*m_true_it)->first.as(), (*m_true_it)->second.as() }; } YAMLConfigHandle::MapIterator& YAMLConfigHandle::MapIterator::operator++ () { m_true_it->operator++ (); return *this; } bool YAMLConfigHandle::MapIterator::operator==(const MapIterator& other) { return (*m_true_it == *other.m_true_it); } bool YAMLConfigHandle::MapIterator::operator!=(const MapIterator& other) { return (*m_true_it != *other.m_true_it); } YAMLConfigHandle::MapIterator::MapIterator( YAMLConfigHandleImpl* handle, std::unique_ptr it ): m_handle(handle), m_true_it(it.release()) {} YAMLConfigHandle::MapIterator YAMLConfigHandle::map_begin() { return MapIterator( m_impl.get(), make_unique(m_impl->m_node.begin()) ); } YAMLConfigHandle::MapIterator YAMLConfigHandle::map_end() { return MapIterator( m_impl.get(), make_unique(m_impl->m_node.end()) ); } // List to vector // ------------- // implementation of list_to_vector template std::vector YAMLConfigHandle::YAMLConfigHandleImpl::list_to_vector( const std::string& list, YAMLConfigHandle& parent ) { // check that it is a list as expected if (not m_node[list]) { parent.report_error(YAMLConfigError::MissingRequiredAttribute, list); } auto node = m_node[list]; if (not node.IsSequence()) { parent.report_error(YAMLConfigError::ListExpected, "A list was expected for attribute : " + list); } record_attr(list); // parse the list std::vector vec; vec.reserve(parent.size()); auto list_section = parent.get_section(list); uindex_t size = list_section.size(); for (uindex_t ind=0; ind(ind)); } return vec; } #ifndef SPC_DOXYGEN_SHOULD_SKIP_THIS // two specialization for indices => also read range of indices template <> std::vector YAMLConfigHandle::YAMLConfigHandleImpl::list_to_vector( const std::string& list, YAMLConfigHandle& parent ) { if (not m_node[list]) { parent.report_error(YAMLConfigError::MissingRequiredAttribute, list); } record_attr(list); // parse the list auto node = m_node[list]; std::vector vec; if (not node.IsSequence()) { try { // it ;ay be our special way to generate list "1:10" vec = utils::range_indices(node.as()); } catch (const std::invalid_argument& e) { parent.report_error(YAMLConfigError::InvalidArgument, "Expected valid list for attribute " + list + ". Error : " + e.what()); } } else { vec.reserve(parent.size()); auto list_section = parent.get_section(list); uindex_t size = list_section.size(); for (uindex_t ind=0; ind(ind)); } } return vec; } template <> std::vector YAMLConfigHandle::YAMLConfigHandleImpl::list_to_vector( const std::string& list, YAMLConfigHandle& parent ) { if (not m_node[list]) { parent.report_error(YAMLConfigError::MissingRequiredAttribute, list); } record_attr(list); // parse the list auto node = m_node[list]; std::vector vec; if (not node.IsSequence()) { try { // it may be the slice way to generate lists ":" vec = utils::range_indices(node.as()); } catch (const std::invalid_argument& e) { parent.report_error(YAMLConfigError::InvalidArgument, "Expected valid list for attribute " + list + ". Error : " + e.what()); } } else { vec.reserve(parent.size()); auto list_section = parent.get_section(list); uindex_t size = list_section.size(); for (uindex_t ind=0; ind(ind)); } } return vec; } template <> std::vector YAMLConfigHandle::list_to_vector(const std::string& list) { return m_impl->list_to_vector(list, *this); } template <> std::vector YAMLConfigHandle::list_to_vector(const std::string& list) { return m_impl->list_to_vector(list, *this); } template <> std::vector YAMLConfigHandle::list_to_vector(const std::string& list) { return m_impl->list_to_vector(list, *this); } template <> std::vector YAMLConfigHandle::list_to_vector(const std::string& list) { return m_impl->list_to_vector(list, *this); } template <> std::vector YAMLConfigHandle::list_to_vector(const std::string& list) { return m_impl->list_to_vector(list, *this); } #endif // SPC_SHOULD_SKIP_THIS // ========== // ConfigFile // ========== YAMLConfigFile YAMLConfigFile::load( const std::string& file ) { return YAMLConfigFile(YAML::LoadFile(file), file); } YAMLConfigFile YAMLConfigFile::load_from_string( const std::string& str, std::string name ) { return YAMLConfigFile(YAML::Load(str), name); } std::unique_ptr YAMLConfigFile::make( const std::string& file ) { // can't use make unique, YAMLConfigFile() is private.... return std::unique_ptr( new YAMLConfigFile(YAML::LoadFile(file), file) ); } std::unique_ptr YAMLConfigFile::make_from_string( const std::string& str, std::string name ) { return std::unique_ptr( new YAMLConfigFile(YAML::Load(str), name) ); } YAMLConfigFile::YAMLConfigFile( const YAML::Node& node, const std::string name ): YAMLConfigHandle(node, nullptr, NAME_PATH_ROOT, NAME_PATH_ROOT), m_handle(std::make_shared(name)) { set_file_handle(m_handle); // read variables if (has_section(SECTION_USER_VARIABLES)) { auto& int_vars = m_handle->m_int_vars; auto& float_vars = m_handle->m_float_vars; auto var_section = get_section(SECTION_USER_VARIABLES); if (var_section.has_section(SECTION_INT_USER_VARIABLES)) { auto int_section = var_section.get_section(SECTION_INT_USER_VARIABLES); int_vars.reserve( int_vars.size() + int_section.size()); float_vars.reserve(float_vars.size() + int_section.size()); for ( auto it=int_section.map_begin(); it!=int_section.map_end(); ++it ) { auto tp = *it; auto key = utils::strip(tp.first); index_t val = utils::parse_expression(tp.second, int_vars); int_vars.insert({key, val}); float_vars.insert({key, static_cast(val)}); } } if (var_section.has_section(SECTION_FLOAT_USER_VARIABLES)) { auto float_section = var_section.get_section(SECTION_FLOAT_USER_VARIABLES); float_vars.reserve(float_vars.size()+float_section.size()); for ( auto it=float_section.map_begin(); it!=float_section.map_end(); ++it ) { auto tp = *it; auto key = utils::strip(tp.first); - scalar_t val = utils::parse_expression(tp.second); + scalar_t val = utils::parse_expression(tp.second); // FIXME ? float_vars.insert({key, val}); } } } } YAMLConfigFile::~YAMLConfigFile() = default; } //end namespace io } //end namespace specmicp diff --git a/src/specmicp_common/macros.hpp b/src/specmicp_common/macros.hpp index e004ede..b298e81 100644 --- a/src/specmicp_common/macros.hpp +++ b/src/specmicp_common/macros.hpp @@ -1,173 +1,173 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #ifndef SPECMCICP_MACROS_HPP #define SPECMCICP_MACROS_HPP #include //! \file macros.hpp //! \brief Macros and config //! \var ndebug //! \brief true of NDEBUG is defined (i.e. false in DEBUG mode) // a lot of the eigen stuff may raise an error if in Debug mode #ifdef NDEBUG constexpr bool ndebug { false }; #else constexpr bool ndebug { true }; #endif //! \def NOEXCEPT //! \brief Tag a function to be noexcept when in non-debug mod but can raise exception in debug mod #define NOEXCEPT noexcept(ndebug) //! \def specmicp_assert //! \brief Assertion macro used in SpecMiCP //! //! This is the assertion macro used in SpecMiCP. //! It can be selectively disabled by declaring SPECMICP_NO_DEBUG. #ifdef SPECMICP_NO_DEBUG #define specmicp_assert #else #define specmicp_assert(x) assert(x) #endif // SPECMICP_NO_DEBUG // deprecation // ----------- //! \def SPECMICP_DEPRECATED //! \brief Declare a symbol to be deprecated //! //! This symbol will be removed shortly. Do not use it. #if defined(__GNUC__) && (__GNUC__ > 4 || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5))) #define SPECMICP_DEPRECATED(message) __attribute__((deprecated(message))) #elif defined(__clang__) && defined(__has_feature) #if __has_feature(attribute_deprecated_with_message) #define SPECMICP_DEPRECATED(message) __attribute__ ((deprecated(message))) #endif #else #define SPECMICP_DEPRECATED #endif // visibility // ---------- //! \def SPECMICP_DLL_PUBLIC //! \brief Declare a symbol to be exported in a shared library //! //! Only symbols defined with this macro will be exported in the shared library. //! The others symbols are hidden. //! //! \sa SPECMICP_DLL_LOCAL //! \def SPECMICP_DLL_LOCAL //! \brief Declare a symbol to be hidden in a shared library //! //! This symbols are not available to the user. They are not part of the API. //! //! \sa SPECMICP_DLL_PUBLIC #if defined(_WIN32) || defined(__CYGWIN__) #ifdef _EXPORTING #ifdef __GNUC__ #define SPECMICP_DLL_PUBLIC __attribute__((dllexport)) #else #define SPECMICP_DLL_PUBLIC __declspec(dllexport) #endif #else #ifdef __GNUC__ #define SPECMICP_DLL_PUBLIC __attribute__((dllimport)) #else #define SPECMICP_DLL_PUBLIC __declspec(dllimport) #endif #endif #else #if __GNUC__ >= 4 #define SPECMICP_DLL_PUBLIC __attribute__ ((visibility("default"))) #define SPECMICP_DLL_LOCAL __attribute__ ((visibility("hidden"))) #else #define SPECMICP_DLL_PUBLIC #define SPECMICP_DLL_LOCAL #endif #endif // likely / unlikely // ----------------- //! \def likely //! \brief Branch prediction information for the compiler //! //! Inform the compiler that this pbranch is very likely //! //! \sa unlikely //! \def unlikely //! \brief Branch prediction information for the compiler //! //! Inform the compiler that this branch is unlikely. //! //! \sa likely #if defined(__GNUC__) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) (x) #define unlikely(x) (x) #endif // Pure function // --------------- //! \def SPECMICP_PURE_F //! \brief Define a function to have no side effect (but can read global memory) #ifdef __GNUC__ #define SPECMICP_PURE_F __attribute__ ((pure)) #else #define SPECMICP_PURE_F #endif //! \def SPECMICP_CONST_F -//! \brief Define a function to have no side effect (but can read global memory) +//! \brief Define a function to have no side effect (but can NOT read global memory) #ifdef __GNUC__ #define SPECMICP_CONST_F __attribute__ ((const)) #else #define SPECMICP_CONST_F #endif #endif // SPECMCICP_MACROS_HPP diff --git a/src/specmicp_common/plugins/plugin_base.hpp b/src/specmicp_common/plugins/plugin_base.hpp index 107e778..ca780fc 100644 --- a/src/specmicp_common/plugins/plugin_base.hpp +++ b/src/specmicp_common/plugins/plugin_base.hpp @@ -1,124 +1,124 @@ /* ============================================================================= Copyright (c) 2014 - 2016 F. Georget Princeton University All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ============================================================================= */ #ifndef SPECMICP_PLUGIN_PLUGINBASE_HPP #define SPECMICP_PLUGIN_PLUGINBASE_HPP /*! \file plugin_base.hpp \brief Base class for a plugin Example of how to declare a new plugin : \code{.cpp} // necessary headers #include "utils/plugins/plugin_interface.h" #include "utils/plugins/plugin_base.hpp" #include "utils/plugins/module_base.hpp" // First declare the plugin class ExamplePlugin: public specmicp::plugins::PluginBase { public: ExamplePlugin(): PluginBase() { // set required version set_api_version(0, 1, 0); } ~ExamplePlugin() = default // The destructor can be used to free memory if needed bool initialize(const specmicp::plugins::PluginManagerServices& services) override { // register a new module auto test_module = specmicp::plugins::ModuleBase::create_module(); auto retcode = services.register_module("test_module", std::move(test_module)); if (not retcode) { return false; } // register a new object auto factory = []() {return new TestObject(); }; retcode = services.register_object("test_module", "test_object", factory); if (not retcode) { return false; } return true; } }; // Then we register the plugin SPECMICP_PLUGIN(BasicPlugin); \endcode */ #include "plugin_types.hpp" namespace specmicp { namespace plugins { //! \brief Base class for a plugin class SPECMICP_DLL_PUBLIC PluginBase { public: PluginBase() {} - //! \brief Ino + //! \brief Initialize the plugin virtual bool initialize(const PluginManagerServices& services) = 0; //! \brief Return the API version used by the plugin PluginAPIVersion get_api_version() { return m_version; } virtual ~PluginBase() = default; protected: //! \brief Set the API version void set_api_version( api_version_t major, api_version_t minor, api_version_t patch); private: //! \brief The API version PluginAPIVersion m_version; }; } //end namespace plugins } //end namespace specmicp #endif // SPECMICP_PLUGIN_PLUGINBASE_HPP