diff --git a/CHANGES/v2.4.md b/CHANGES/v2.4.md index a3c83b197d6e70f5667838d6b23b15c4d6cb8531..8d2a583be6b6fd80560e251c1a6ed264886d54de 100644 --- a/CHANGES/v2.4.md +++ b/CHANGES/v2.4.md @@ -206,6 +206,7 @@ For users: For users: - Fix some performances regression issue with OpenMP - Updated NAMD patches to version 2.12 and 2.13. Old patches have been removed. + - GROMACS patch for gromacs-2018.4. For developers: - Small fix in LDFLAGS when enabling coverage. diff --git a/configure b/configure index efcd29191ffb68295016646080ed6f0fe3835f80..ad51997a13292f317820c869305d36f0e2138472 100755 --- a/configure +++ b/configure @@ -5228,7 +5228,69 @@ $as_echo "$as_me: WARNING: Your compiler appears not to support C++11" >&2;} $as_echo "$as_me: WARNING: Please change compiler or make sure that everything works correctly" >&2;} fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ library supports C++11 exceptions" >&5 +$as_echo_n "checking whether C++ library supports C++11 exceptions... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <new> +#include <functional> +#include <memory> +#include <system_error> +#include <ios> +#include <future> +// capable to catch: +void func(void) { + try{ + } catch(std::bad_array_new_length &) { + } catch(std::bad_function_call &) { + } catch(std::bad_weak_ptr &) { + } catch(std::ios_base::failure &e) { + if(e.code().category()==std::generic_category()) {} + else if(e.code().category()==std::system_category()) {} + else if(e.code().category()==std::iostream_category()) {} + else if(e.code().category()==std::future_category()) {} + } catch(std::system_error &e) { + if(e.code().category()==std::generic_category()) {} + else if(e.code().category()==std::system_category()) {} + else if(e.code().category()==std::iostream_category()) {} + else if(e.code().category()==std::future_category()) {} + } +// capable to throw: + auto a=std::bad_array_new_length(); + auto b=std::bad_function_call(); + auto c=std::bad_weak_ptr(); + auto d=std::system_error(10,std::generic_category(),"msg"); + auto e=std::system_error(10,std::system_category(),"msg"); + auto f=std::system_error(10,std::iostream_category(),"msg"); + auto g=std::system_error(10,std::future_category(),"msg"); + auto h=std::ios_base::failure("msg",std::error_code(10,std::generic_category())); + auto i=std::ios_base::failure("msg",std::error_code(10,std::system_category())); + auto j=std::ios_base::failure("msg",std::error_code(10,std::iostream_category())); + auto k=std::ios_base::failure("msg",std::error_code(10,std::future_category())); +} + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + $as_echo "#define __PLUMED_LIBCXX11 1" >>confdefs.h + + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext diff --git a/configure.ac b/configure.ac index a1221ed6566ad29bb106a2aeada7b7b9088ee451..aaa860a430eb5612293bd14306e6e5c4ed66b9c2 100644 --- a/configure.ac +++ b/configure.ac @@ -389,7 +389,51 @@ then AC_MSG_WARN([Please change compiler or make sure that everything works correctly]) fi - +AC_MSG_CHECKING([whether C++ library supports C++11 exceptions]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include <new> +#include <functional> +#include <memory> +#include <system_error> +#include <ios> +#include <future> +// capable to catch: +void func(void) { + try{ + } catch(std::bad_array_new_length &) { + } catch(std::bad_function_call &) { + } catch(std::bad_weak_ptr &) { + } catch(std::ios_base::failure &e) { + if(e.code().category()==std::generic_category()) {} + else if(e.code().category()==std::system_category()) {} + else if(e.code().category()==std::iostream_category()) {} + else if(e.code().category()==std::future_category()) {} + } catch(std::system_error &e) { + if(e.code().category()==std::generic_category()) {} + else if(e.code().category()==std::system_category()) {} + else if(e.code().category()==std::iostream_category()) {} + else if(e.code().category()==std::future_category()) {} + } +// capable to throw: + auto a=std::bad_array_new_length(); + auto b=std::bad_function_call(); + auto c=std::bad_weak_ptr(); + auto d=std::system_error(10,std::generic_category(),"msg"); + auto e=std::system_error(10,std::system_category(),"msg"); + auto f=std::system_error(10,std::iostream_category(),"msg"); + auto g=std::system_error(10,std::future_category(),"msg"); + auto h=std::ios_base::failure("msg",std::error_code(10,std::generic_category())); + auto i=std::ios_base::failure("msg",std::error_code(10,std::system_category())); + auto j=std::ios_base::failure("msg",std::error_code(10,std::iostream_category())); + auto k=std::ios_base::failure("msg",std::error_code(10,std::future_category())); +} +])], +[ + AC_MSG_RESULT([yes]); + AC_DEFINE([__PLUMED_LIBCXX11]) +], +[AC_MSG_RESULT([no]) +]) AC_SUBST(disable_dependency_tracking) diff --git a/patches/gromacs-2018.3.config b/patches/gromacs-2018.4.config similarity index 100% rename from patches/gromacs-2018.3.config rename to patches/gromacs-2018.4.config diff --git a/patches/gromacs-2018.3.diff/src/gromacs/CMakeLists.txt b/patches/gromacs-2018.4.diff/src/gromacs/CMakeLists.txt similarity index 100% rename from patches/gromacs-2018.3.diff/src/gromacs/CMakeLists.txt rename to patches/gromacs-2018.4.diff/src/gromacs/CMakeLists.txt diff --git a/patches/gromacs-2018.3.diff/src/gromacs/CMakeLists.txt.preplumed b/patches/gromacs-2018.4.diff/src/gromacs/CMakeLists.txt.preplumed similarity index 100% rename from patches/gromacs-2018.3.diff/src/gromacs/CMakeLists.txt.preplumed rename to patches/gromacs-2018.4.diff/src/gromacs/CMakeLists.txt.preplumed diff --git a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp similarity index 96% rename from patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp rename to patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp index bc1e961f1c77387a4dd7f103c1fc51acbbca9119..07e1337af3e096184aa387b5d6caf66748fca510 100644 --- a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp +++ b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp @@ -3,7 +3,7 @@ * * Copyright (c) 1991-2000, University of Groningen, The Netherlands. * Copyright (c) 2001-2004, The GROMACS development team. - * Copyright (c) 2013,2014,2015,2016,2017, by the GROMACS development team, led by + * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, * and including many others, as listed in the AUTHORS file in the * top-level source directory and at http://www.gromacs.org. @@ -738,7 +738,6 @@ void sum_epot(gmx_grppairener_t *grpp, real *epot) void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda *fepvals) { int index; - double dlam; enerd->dvdl_lin[efptVDW] += enerd->term[F_DVDL_VDW]; /* include dispersion correction */ enerd->term[F_DVDL] = 0.0; @@ -786,13 +785,6 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda } } - /* Notes on the foreign lambda free energy difference evaluation: - * Adding the potential and ekin terms that depend linearly on lambda - * as delta lam * dvdl to the energy differences is exact. - * For the constraints this is not exact, but we have no other option - * without literally changing the lengths and reevaluating the energies at each step. - * (try to remedy this post 4.6 - MRS) - */ if (fepvals->separate_dvdl[efptBONDED]) { enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR]; @@ -801,7 +793,6 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda { enerd->term[F_DVDL] += enerd->term[F_DVDL_CONSTR]; } - enerd->term[F_DVDL_CONSTR] = 0; for (int i = 0; i < fepvals->n_lambda; i++) { @@ -814,20 +805,42 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda current lambda, because the contributions to the current lambda are automatically zeroed */ + double &enerpart_lambda = enerd->enerpart_lambda[i + 1]; + for (size_t j = 0; j < lambda.size(); j++) { /* Note that this loop is over all dhdl components, not just the separated ones */ - dlam = (fepvals->all_lambda[j][i] - lambda[j]); - enerd->enerpart_lambda[i+1] += dlam*enerd->dvdl_lin[j]; + const double dlam = fepvals->all_lambda[j][i] - lambda[j]; + + enerpart_lambda += dlam*enerd->dvdl_lin[j]; + + /* Constraints can not be evaluated at foreign lambdas, so we add + * a linear extrapolation. This is an approximation, but usually + * quite accurate since constraints change little between lambdas. + */ + if ((j == efptBONDED && fepvals->separate_dvdl[efptBONDED]) || + (j == efptFEP && !fepvals->separate_dvdl[efptBONDED])) + { + enerpart_lambda += dlam*enerd->term[F_DVDL_CONSTR]; + } + + if (j == efptMASS) + { + enerpart_lambda += dlam*enerd->term[F_DKDL]; + } + if (debug) { fprintf(debug, "enerdiff lam %g: (%15s), non-linear %f linear %f*%f\n", fepvals->all_lambda[j][i], efpt_names[j], - (enerd->enerpart_lambda[i+1] - enerd->enerpart_lambda[0]), + enerpart_lambda - enerd->enerpart_lambda[0], dlam, enerd->dvdl_lin[j]); } } } + + /* The constrain contribution is now included in other terms, so clear it */ + enerd->term[F_DVDL_CONSTR] = 0; } diff --git a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp.preplumed b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp.preplumed similarity index 96% rename from patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp.preplumed index f01ea4d333a52e51e9f192758e13b44295114843..c939c4e8a7f13553ec52fde621a8812dc35727b7 100644 --- a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/force.cpp.preplumed +++ b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/force.cpp.preplumed @@ -3,7 +3,7 @@ * * Copyright (c) 1991-2000, University of Groningen, The Netherlands. * Copyright (c) 2001-2004, The GROMACS development team. - * Copyright (c) 2013,2014,2015,2016,2017, by the GROMACS development team, led by + * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, * and including many others, as listed in the AUTHORS file in the * top-level source directory and at http://www.gromacs.org. @@ -724,7 +724,6 @@ void sum_epot(gmx_grppairener_t *grpp, real *epot) void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda *fepvals) { int index; - double dlam; enerd->dvdl_lin[efptVDW] += enerd->term[F_DVDL_VDW]; /* include dispersion correction */ enerd->term[F_DVDL] = 0.0; @@ -772,13 +771,6 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda } } - /* Notes on the foreign lambda free energy difference evaluation: - * Adding the potential and ekin terms that depend linearly on lambda - * as delta lam * dvdl to the energy differences is exact. - * For the constraints this is not exact, but we have no other option - * without literally changing the lengths and reevaluating the energies at each step. - * (try to remedy this post 4.6 - MRS) - */ if (fepvals->separate_dvdl[efptBONDED]) { enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR]; @@ -787,7 +779,6 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda { enerd->term[F_DVDL] += enerd->term[F_DVDL_CONSTR]; } - enerd->term[F_DVDL_CONSTR] = 0; for (int i = 0; i < fepvals->n_lambda; i++) { @@ -800,20 +791,42 @@ void sum_dhdl(gmx_enerdata_t *enerd, gmx::ArrayRef<const real> lambda, t_lambda current lambda, because the contributions to the current lambda are automatically zeroed */ + double &enerpart_lambda = enerd->enerpart_lambda[i + 1]; + for (size_t j = 0; j < lambda.size(); j++) { /* Note that this loop is over all dhdl components, not just the separated ones */ - dlam = (fepvals->all_lambda[j][i] - lambda[j]); - enerd->enerpart_lambda[i+1] += dlam*enerd->dvdl_lin[j]; + const double dlam = fepvals->all_lambda[j][i] - lambda[j]; + + enerpart_lambda += dlam*enerd->dvdl_lin[j]; + + /* Constraints can not be evaluated at foreign lambdas, so we add + * a linear extrapolation. This is an approximation, but usually + * quite accurate since constraints change little between lambdas. + */ + if ((j == efptBONDED && fepvals->separate_dvdl[efptBONDED]) || + (j == efptFEP && !fepvals->separate_dvdl[efptBONDED])) + { + enerpart_lambda += dlam*enerd->term[F_DVDL_CONSTR]; + } + + if (j == efptMASS) + { + enerpart_lambda += dlam*enerd->term[F_DKDL]; + } + if (debug) { fprintf(debug, "enerdiff lam %g: (%15s), non-linear %f linear %f*%f\n", fepvals->all_lambda[j][i], efpt_names[j], - (enerd->enerpart_lambda[i+1] - enerd->enerpart_lambda[0]), + enerpart_lambda - enerd->enerpart_lambda[0], dlam, enerd->dvdl_lin[j]); } } } + + /* The constrain contribution is now included in other terms, so clear it */ + enerd->term[F_DVDL_CONSTR] = 0; } diff --git a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp similarity index 99% rename from patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp rename to patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp index a268949cbb431f2cf68f8215456809c51adfe48a..05cd383814ae44eda5d9cba7de468684c207ccd5 100644 --- a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp +++ b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp @@ -2983,7 +2983,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, top_global, &state_work, top, inputrec, nrnb, wcycle, gstat, vsite, constr, fcd, graph, mdAtoms, fr, - mu_tot, enerd, vir, pres, atom*2+dx, FALSE); + mu_tot, enerd, vir, pres, aid*2+dx, FALSE); } cr->nnodes = nnodes; @@ -3019,7 +3019,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, } else { - for (node = 0; (node < nnodes && atom+node < atom_index.size()); node++) + for (node = 0; (node < nnodes && aid+node < atom_index.size()); node++) { if (node > 0) { @@ -3031,7 +3031,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, #endif } - row = (atom + node)*DIM + d; + row = (aid + node)*DIM + d; for (size_t j = 0; j < atom_index.size(); j++) { diff --git a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp.preplumed b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp.preplumed similarity index 99% rename from patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp.preplumed index d06da48c44742dfcca9f46349a0d2480914bca2f..2c385df11ce31a2f1790093540ff1793f2a02f68 100644 --- a/patches/gromacs-2018.3.diff/src/gromacs/mdlib/minimize.cpp.preplumed +++ b/patches/gromacs-2018.4.diff/src/gromacs/mdlib/minimize.cpp.preplumed @@ -2909,7 +2909,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, top_global, &state_work, top, inputrec, nrnb, wcycle, gstat, vsite, constr, fcd, graph, mdAtoms, fr, - mu_tot, enerd, vir, pres, atom*2+dx, FALSE); + mu_tot, enerd, vir, pres, aid*2+dx, FALSE); } cr->nnodes = nnodes; @@ -2945,7 +2945,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, } else { - for (node = 0; (node < nnodes && atom+node < atom_index.size()); node++) + for (node = 0; (node < nnodes && aid+node < atom_index.size()); node++) { if (node > 0) { @@ -2957,7 +2957,7 @@ double do_nm(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, #endif } - row = (atom + node)*DIM + d; + row = (aid + node)*DIM + d; for (size_t j = 0; j < atom_index.size(); j++) { diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp b/patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp similarity index 99% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp rename to patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp index 132d93d0dc1fed0bd8406176da9b8dc31eb9dbea..31f271a63de7822c7e4931635ef4361c8bbef70c 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp @@ -126,6 +126,7 @@ #include "gromacs/utility/logger.h" #include "gromacs/utility/real.h" #include "gromacs/utility/smalloc.h" +#include "gromacs/utility/stringutil.h" #include "deform.h" #include "membed.h" @@ -326,6 +327,19 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, gmx_membed_t *membed, gmx_walltime_accounting_t walltime_accounting) { + /* Workaround for 2 bugs in release-2018. + * NOTE: Proper fix is in release-2019, do not merge this change there. + */ + if (ir->bExpanded && (EI_VV(ir->eI) || + ir->expandedvals->nstexpanded % ir->nstcalcenergy != 0)) + { + ir->nstcalcenergy = 1; + std::string note = + gmx::formatString("NOTE: There are issues with expanded ensemble and certain combination of nstexpanded and nstcalcenergy, setting nstcalcenergy to %d", + ir->nstcalcenergy); + GMX_LOG(mdlog.warning).asParagraph().appendText(note); + } + gmx_mdoutf_t outf = nullptr; gmx_int64_t step, step_rel; double elapsed_time; diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp.preplumed similarity index 99% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp.preplumed index 1695e0b72d53729f4ece973596131895d9a122b8..de5a7031ff1093334ca547c574fdf64d5596c6ac 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/md.cpp.preplumed +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/md.cpp.preplumed @@ -125,6 +125,7 @@ #include "gromacs/utility/logger.h" #include "gromacs/utility/real.h" #include "gromacs/utility/smalloc.h" +#include "gromacs/utility/stringutil.h" #include "deform.h" #include "membed.h" @@ -315,6 +316,19 @@ double gmx::do_md(FILE *fplog, t_commrec *cr, const gmx::MDLogger &mdlog, gmx_membed_t *membed, gmx_walltime_accounting_t walltime_accounting) { + /* Workaround for 2 bugs in release-2018. + * NOTE: Proper fix is in release-2019, do not merge this change there. + */ + if (ir->bExpanded && (EI_VV(ir->eI) || + ir->expandedvals->nstexpanded % ir->nstcalcenergy != 0)) + { + ir->nstcalcenergy = 1; + std::string note = + gmx::formatString("NOTE: There are issues with expanded ensemble and certain combination of nstexpanded and nstcalcenergy, setting nstcalcenergy to %d", + ir->nstcalcenergy); + GMX_LOG(mdlog.warning).asParagraph().appendText(note); + } + gmx_mdoutf_t outf = nullptr; gmx_int64_t step, step_rel; double elapsed_time; diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp b/patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp similarity index 97% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp rename to patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp index 31ce2bc9c914be0f812a121b538c5ae74137d95c..2fa4b51dfbca01b83f35e26a1e930f879a2354a5 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp @@ -512,6 +512,22 @@ int Mdrunner::mainFunction(int argc, char *argv[]) handleRestart(cr, bTryToAppendFiles, nfile, fnm, &continuationOptions.appendFiles, &continuationOptions.startedFromCheckpoint); mdrunOptions.rerun = opt2bSet("-rerun", nfile, fnm); + + if (mdrunOptions.rerun) + { + /* Rerun can't work if an output file name is the same as the input file name. + * If this is the case, the user will get an error telling them what the issue is. + */ + if (strcmp(opt2fn("-rerun", nfile, fnm), opt2fn("-o", nfile, fnm)) == 0 || + strcmp(opt2fn("-rerun", nfile, fnm), opt2fn("-x", nfile, fnm)) == 0) + { + gmx_fatal(FARGS, "When using mdrun -rerun, the name of the input trajectory file " + "%s cannot be identical to the name of an output file (whether " + "given explicitly with -o or -x, or by default)", + opt2fn("-rerun", nfile, fnm)); + } + } + mdrunOptions.ntompOptionIsSet = opt2parg_bSet("-ntomp", asize(pa), pa); /* We postpone opening the log file if we are appending, so we can diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp.preplumed similarity index 97% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp.preplumed index 772aabda3833f87e24f6b67a76891308ec3c7ac9..a3ec5ac437710f1b2ebd8b629a0368969d668756 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/mdrun.cpp.preplumed +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/mdrun.cpp.preplumed @@ -499,6 +499,22 @@ int Mdrunner::mainFunction(int argc, char *argv[]) handleRestart(cr, bTryToAppendFiles, nfile, fnm, &continuationOptions.appendFiles, &continuationOptions.startedFromCheckpoint); mdrunOptions.rerun = opt2bSet("-rerun", nfile, fnm); + + if (mdrunOptions.rerun) + { + /* Rerun can't work if an output file name is the same as the input file name. + * If this is the case, the user will get an error telling them what the issue is. + */ + if (strcmp(opt2fn("-rerun", nfile, fnm), opt2fn("-o", nfile, fnm)) == 0 || + strcmp(opt2fn("-rerun", nfile, fnm), opt2fn("-x", nfile, fnm)) == 0) + { + gmx_fatal(FARGS, "When using mdrun -rerun, the name of the input trajectory file " + "%s cannot be identical to the name of an output file (whether " + "given explicitly with -o or -x, or by default)", + opt2fn("-rerun", nfile, fnm)); + } + } + mdrunOptions.ntompOptionIsSet = opt2parg_bSet("-ntomp", asize(pa), pa); /* We postpone opening the log file if we are appending, so we can diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.cpp b/patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.cpp similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.cpp rename to patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.cpp diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.cpp.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.cpp.preplumed similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.cpp.preplumed diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.h b/patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.h similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.h rename to patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.h diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.h.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.h.preplumed similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/repl_ex.h.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/repl_ex.h.preplumed diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp similarity index 99% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp rename to patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp index f1d993f28c7ebffbe54964aae6c9fa2bc80c18f0..25a8230eb42715d61e272b2852b176ef3d812a33 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp @@ -958,13 +958,17 @@ int Mdrunner::mdrunner() !thisRankHasDuty(cr, DUTY_PP), inputrec->cutoff_scheme == ecutsVERLET); -#ifndef NDEBUG - if (EI_TPI(inputrec->eI) && + // Disabled for the rest of the lifetime of release-2018 branch + // to prevent false positives. + /* + #ifndef NDEBUG + if (EI_TPI(inputrec->eI) && inputrec->cutoff_scheme == ecutsVERLET) - { + { gmx_feenableexcept(); - } -#endif + } + #endif + */ // Build a data structure that expresses which kinds of non-bonded // task are handled by this rank. diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp.preplumed similarity index 99% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp.preplumed index 917ada5687fa1e0c3a0f62fdf3df240ba1f7b113..813be6f3ead3deb828530c431430cfdf34d8364b 100644 --- a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.cpp.preplumed +++ b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.cpp.preplumed @@ -952,13 +952,17 @@ int Mdrunner::mdrunner() !thisRankHasDuty(cr, DUTY_PP), inputrec->cutoff_scheme == ecutsVERLET); -#ifndef NDEBUG - if (EI_TPI(inputrec->eI) && + // Disabled for the rest of the lifetime of release-2018 branch + // to prevent false positives. + /* + #ifndef NDEBUG + if (EI_TPI(inputrec->eI) && inputrec->cutoff_scheme == ecutsVERLET) - { + { gmx_feenableexcept(); - } -#endif + } + #endif + */ // Build a data structure that expresses which kinds of non-bonded // task are handled by this rank. diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.h b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.h similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/runner.h rename to patches/gromacs-2018.4.diff/src/programs/mdrun/runner.h diff --git a/patches/gromacs-2018.3.diff/src/programs/mdrun/runner.h.preplumed b/patches/gromacs-2018.4.diff/src/programs/mdrun/runner.h.preplumed similarity index 100% rename from patches/gromacs-2018.3.diff/src/programs/mdrun/runner.h.preplumed rename to patches/gromacs-2018.4.diff/src/programs/mdrun/runner.h.preplumed diff --git a/regtest/basic/rt-make-exceptions/main.cpp b/regtest/basic/rt-make-exceptions/main.cpp index 238b435e91b40d24ae11aebd18fcc768d39cbf1e..4de17f3819b77d086b6f8f8cc09b4cf37610f5e9 100644 --- a/regtest/basic/rt-make-exceptions/main.cpp +++ b/regtest/basic/rt-make-exceptions/main.cpp @@ -1,3 +1,8 @@ +#ifdef __PLUMED_LIBCXX11 +// In order to correctly catch the thrown C++11 exceptions, +// we notify the Plumed wrapper that those exceptions are recognized by the compiler. +#define __PLUMED_WRAPPER_LIBCXX11 1 +#endif #include "plumed/tools/Stopwatch.h" #include "plumed/wrapper/Plumed.h" #include <fstream> @@ -146,6 +151,66 @@ int main(){ test_this(ofs,plumed,"setMasses",&masses[0]); } + { + PLMD::Plumed p; + +#define TEST_STD(type) try { p.cmd("throw", #type " msg"); } catch (type & e ) { plumed_assert(std::string(e.what())== #type " msg"); } + TEST_STD(std::logic_error); + TEST_STD(std::invalid_argument); + TEST_STD(std::domain_error); + TEST_STD(std::length_error); + TEST_STD(std::out_of_range); + TEST_STD(std::runtime_error); + TEST_STD(std::range_error); + TEST_STD(std::overflow_error); + TEST_STD(std::underflow_error); + +#define TEST_STD_NOMSG(type) try { p.cmd("throw", #type);} catch (type & e ) { } + TEST_STD_NOMSG(std::bad_cast); +#ifdef __PLUMED_LIBCXX11 + TEST_STD_NOMSG(std::bad_weak_ptr); + TEST_STD_NOMSG(std::bad_function_call); +#endif + TEST_STD_NOMSG(std::bad_typeid); + TEST_STD_NOMSG(std::bad_alloc); +#ifdef __PLUMED_LIBCXX11 + TEST_STD_NOMSG(std::bad_array_new_length); +#endif + TEST_STD_NOMSG(std::bad_exception); + + + try { p.cmd("throw","PLMD::Exception msg"); } catch (PLMD::Plumed::Exception &e) { + } + try { p.cmd("throw","PLMD::ExceptionError msg"); } catch (PLMD::Plumed::ExceptionError &e) { + } + try { p.cmd("throw","PLMD::ExceptionDebug msg"); } catch (PLMD::Plumed::ExceptionDebug &e) { + } + try { p.cmd("throw","PLMD::lepton::Exception msg"); } catch (PLMD::Plumed::LeptonException &e) { + plumed_assert(std::string(e.what())=="PLMD::lepton::Exception msg"); + } +#ifdef __PLUMED_LIBCXX11 + try { p.cmd("throw","std::system_error std::generic_category 100"); } catch (std::system_error & e) { + plumed_assert(e.code().value()==100)<<" value="<<e.code().value(); + plumed_assert(e.code().category()==std::generic_category()); + } + try { p.cmd("throw","std::system_error std::system_category 200"); } catch (std::system_error & e) { + plumed_assert(e.code().value()==200); + plumed_assert(e.code().category()==std::system_category()); + } + try { p.cmd("throw","std::system_error std::iostream_category 300"); } catch (std::system_error & e) { + plumed_assert(e.code().value()==300); + plumed_assert(e.code().category()==std::iostream_category()); + } + try { p.cmd("throw","std::system_error std::future_category 400"); } catch (std::system_error & e) { + plumed_assert(e.code().value()==400); + plumed_assert(e.code().category()==std::future_category()); + } +#endif + try { p.cmd("throw","std::ios_base::failure"); } catch (std::ios_base::failure & e) { + } + + } + return 0; } diff --git a/regtest/basic/rt-make-wrappers/main.cpp b/regtest/basic/rt-make-wrappers/main.cpp index af84b9871cd82af1cd3aff8a84c2a7b047bd15dc..dbc508f29853b42697b539fc29a64a40b76d13a8 100644 --- a/regtest/basic/rt-make-wrappers/main.cpp +++ b/regtest/basic/rt-make-wrappers/main.cpp @@ -167,7 +167,7 @@ int main(){ // test valid { - PLMD::Plumed p(PLMD::Plumed::invalid()); + PLMD::Plumed p(PLMD::Plumed::makeInvalid()); if(p) plumed_error(); } diff --git a/src/core/Makefile b/src/core/Makefile index a5acc39c8e22c114330c137b110ba41a783db0da..47b809e16d3017266f6ca980da4c14f5f2bfcd4c 100644 --- a/src/core/Makefile +++ b/src/core/Makefile @@ -1,4 +1,4 @@ -USE=config tools +USE=config tools lepton # generic makefile include ../maketools/make.module diff --git a/src/core/PlumedMain.cpp b/src/core/PlumedMain.cpp index 4d31ce87dd84b2e9dec3483c8c78f04b2dc61ba3..e8739d5a0376f25ecb3292d6ea790d24572efbce 100644 --- a/src/core/PlumedMain.cpp +++ b/src/core/PlumedMain.cpp @@ -40,16 +40,87 @@ #include "tools/OpenMP.h" #include "tools/Tools.h" #include "tools/Stopwatch.h" +#include "lepton/Exception.h" #include "DataFetchingObject.h" #include <cstdlib> #include <cstring> #include <set> #include <unordered_map> +#include <exception> +#include <stdexcept> +#include <ios> +#include <new> +#include <typeinfo> +#ifdef __PLUMED_LIBCXX11 +#include <system_error> +#include <future> +#include <memory> +#include <functional> +#endif + using namespace std; namespace PLMD { +/// Small utility just used in this file to throw arbitrary exceptions +static void testThrow(const char* what) { + auto words=Tools::getWords(what); + plumed_assert(words.size()>0); +#define __PLUMED_THROW_NOMSG(type) if(words[0]==#type) throw type() +#define __PLUMED_THROW_MSG(type) if(words[0]==#type) throw type(what) + __PLUMED_THROW_MSG(PLMD::ExceptionError); + __PLUMED_THROW_MSG(PLMD::ExceptionDebug); + __PLUMED_THROW_MSG(PLMD::Exception); + __PLUMED_THROW_MSG(PLMD::lepton::Exception); + __PLUMED_THROW_NOMSG(std::bad_exception); +#ifdef __PLUMED_LIBCXX11 + __PLUMED_THROW_NOMSG(std::bad_array_new_length); +#endif + __PLUMED_THROW_NOMSG(std::bad_alloc); +#ifdef __PLUMED_LIBCXX11 + __PLUMED_THROW_NOMSG(std::bad_function_call); + __PLUMED_THROW_NOMSG(std::bad_weak_ptr); +#endif + __PLUMED_THROW_NOMSG(std::bad_cast); + __PLUMED_THROW_NOMSG(std::bad_typeid); + __PLUMED_THROW_MSG(std::underflow_error); + __PLUMED_THROW_MSG(std::overflow_error); + __PLUMED_THROW_MSG(std::range_error); + __PLUMED_THROW_MSG(std::runtime_error); + __PLUMED_THROW_MSG(std::out_of_range); + __PLUMED_THROW_MSG(std::length_error); + __PLUMED_THROW_MSG(std::domain_error); + __PLUMED_THROW_MSG(std::invalid_argument); + __PLUMED_THROW_MSG(std::logic_error); + +#ifdef __PLUMED_LIBCXX11 + if(words[0]=="std::system_error") { + plumed_assert(words.size()>2); + int error_code; + Tools::convert(words[2],error_code); + if(words[1]=="std::generic_category") throw std::system_error(error_code,std::generic_category(),what); + if(words[1]=="std::system_category") throw std::system_error(error_code,std::system_category(),what); + if(words[1]=="std::iostream_category") throw std::system_error(error_code,std::iostream_category(),what); + if(words[1]=="std::future_category") throw std::system_error(error_code,std::future_category(),what); + } +#endif + + if(words[0]=="std::ios_base::failure") { +#ifdef __PLUMED_LIBCXX11 + int error_code=0; + if(words.size()>2) Tools::convert(words[2],error_code); + if(words.size()>1 && words[1]=="std::generic_category") throw std::ios_base::failure(what,std::error_code(error_code,std::generic_category())); + if(words.size()>1 && words[1]=="std::system_category") throw std::ios_base::failure(what,std::error_code(error_code,std::system_category())); + if(words.size()>1 && words[1]=="std::iostream_category") throw std::ios_base::failure(what,std::error_code(error_code,std::iostream_category())); + if(words.size()>1 && words[1]=="std::future_category") throw std::ios_base::failure(what,std::error_code(error_code,std::future_category())); +#endif + throw std::ios_base::failure(what); + } + + plumed_error() << "unknown exception" << what; +} + PlumedMain::PlumedMain(): initialized(false), // automatically write on log in destructor @@ -388,6 +459,11 @@ void PlumedMain::cmd(const std::string & word,void*val) { CHECK_NOTNULL(val,word); OpenMP::setNumThreads(*static_cast<unsigned*>(val)); break; + /* ADDED WITH API==6 */ + /* only used for testing */ + case cmd_throw: + CHECK_NOTNULL(val,word); + testThrow((const char*) val); /* STOP API */ case cmd_setMDEngine: CHECK_NOTINIT(initialized,word); diff --git a/src/core/PlumedMainInitializer.cpp b/src/core/PlumedMainInitializer.cpp index 6ce26232858ec46a8048ebd8a79f5c33e461e167..aca124ac5fc4b4971a88c6da20dc7744e05a4120 100644 --- a/src/core/PlumedMainInitializer.cpp +++ b/src/core/PlumedMainInitializer.cpp @@ -22,12 +22,25 @@ #include "PlumedMainInitializer.h" #include "PlumedMain.h" #include "tools/Exception.h" +#include "lepton/Exception.h" #include <cstdlib> #include <cstring> #include <iostream> #if defined __PLUMED_HAS_DLOPEN #include <dlfcn.h> #endif +#include <exception> +#include <stdexcept> +#include <ios> +#include <new> +#include <typeinfo> +#ifdef __PLUMED_LIBCXX11 +#include <system_error> +#include <future> +#include <memory> +#include <functional> +#endif + using namespace std; @@ -48,21 +61,6 @@ extern "C" void*plumed_plumedmain_create() { } } -#define __PLUMED_CATCH(e,nothrow) \ - catch(PLMD::ExceptionError & e) { \ - nothrow.handler(nothrow.ptr,20200,e.what(),nullptr); \ - } catch(PLMD::ExceptionDebug & e) { \ - nothrow.handler(nothrow.ptr,20100,e.what(),nullptr); \ - } catch(PLMD::Exception & e) { \ - nothrow.handler(nothrow.ptr,20000,e.what(),nullptr); \ - } catch(std::runtime_error & e) { \ - nothrow.handler(nothrow.ptr,10200,e.what(),nullptr); \ - } catch(std::logic_error & e) { \ - nothrow.handler(nothrow.ptr,10100,e.what(),nullptr); \ - } catch(std::exception & e) { \ - nothrow.handler(nothrow.ptr,10000,e.what(),nullptr); \ - } - extern "C" void plumed_plumedmain_cmd(void*plumed,const char*key,const void*val) { plumed_massert(plumed,"trying to use a plumed object which is not initialized"); auto p=static_cast<PLMD::PlumedMain*>(plumed); @@ -76,7 +74,88 @@ extern "C" void plumed_plumedmain_cmd_nothrow(void*plumed,const char*key,const v try { plumed_massert(plumed,"trying to use a plumed object which is not initialized"); static_cast<PLMD::PlumedMain*>(plumed)->cmd(key,val);; - } __PLUMED_CATCH(e,nothrow) + } catch(PLMD::ExceptionError & e) { + nothrow.handler(nothrow.ptr,20200,e.what(),nullptr); + } catch(PLMD::ExceptionDebug & e) { + nothrow.handler(nothrow.ptr,20100,e.what(),nullptr); + } catch(PLMD::Exception & e) { + nothrow.handler(nothrow.ptr,20000,e.what(),nullptr); + } catch(PLMD::lepton::Exception & e) { + nothrow.handler(nothrow.ptr,19900,e.what(),nullptr); + // 11000 to 12000 are "bad exceptions". message will be copied without new allocations + } catch(bad_exception & e) { + nothrow.handler(nothrow.ptr,11500,e.what(),nullptr); +#ifdef __PLUMED_LIBCXX11 + } catch(bad_array_new_length & e) { + nothrow.handler(nothrow.ptr,11410,e.what(),nullptr); +#endif + } catch(bad_alloc & e) { + nothrow.handler(nothrow.ptr,11400,e.what(),nullptr); +#ifdef __PLUMED_LIBCXX11 + } catch(bad_function_call & e) { + nothrow.handler(nothrow.ptr,11300,e.what(),nullptr); + } catch(bad_weak_ptr & e) { + nothrow.handler(nothrow.ptr,11200,e.what(),nullptr); +#endif + } catch(bad_cast & e) { + nothrow.handler(nothrow.ptr,11100,e.what(),nullptr); + } catch(bad_typeid & e) { + nothrow.handler(nothrow.ptr,11000,e.what(),nullptr); + // not implemented yet: std::regex_error + // we do not allow regex yet due to portability problems with gcc 4.8 + // as soon as we transition to using <regex> it should be straightforward to add + } catch(std::ios_base::failure & e) { +#ifdef __PLUMED_LIBCXX11 + int value=e.code().value(); + const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part. + if(e.code().category()==generic_category()) nothrow.handler(nothrow.ptr,10230,e.what(),opt); + else if(e.code().category()==system_category()) nothrow.handler(nothrow.ptr,10231,e.what(),opt); + else if(e.code().category()==iostream_category()) nothrow.handler(nothrow.ptr,10232,e.what(),opt); + else if(e.code().category()==future_category()) nothrow.handler(nothrow.ptr,10233,e.what(),opt); + else +#endif + // 10239 represents std::ios_base::failure with default constructur + nothrow.handler(nothrow.ptr,10239,e.what(),nullptr); +#ifdef __PLUMED_LIBCXX11 + } catch(std::system_error & e) { + int value=e.code().value(); + const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part. + if(e.code().category()==generic_category()) nothrow.handler(nothrow.ptr,10220,e.what(),opt); + else if(e.code().category()==system_category()) nothrow.handler(nothrow.ptr,10221,e.what(),opt); + else if(e.code().category()==iostream_category()) nothrow.handler(nothrow.ptr,10222,e.what(),opt); + else if(e.code().category()==future_category()) nothrow.handler(nothrow.ptr,10223,e.what(),opt); + // fallback to generic runtime_error + else nothrow.handler(nothrow.ptr,10200,e.what(),nullptr); +#endif + } catch(std::underflow_error &e) { + nothrow.handler(nothrow.ptr,10215,e.what(),nullptr); + } catch(std::overflow_error &e) { + nothrow.handler(nothrow.ptr,10210,e.what(),nullptr); + } catch(std::range_error &e) { + nothrow.handler(nothrow.ptr,10205,e.what(),nullptr); + } catch(std::runtime_error & e) { + nothrow.handler(nothrow.ptr,10200,e.what(),nullptr); + // not implemented yet: std::future_error + // not clear how useful it would be. + } catch(std::out_of_range & e) { + nothrow.handler(nothrow.ptr,10120,e.what(),nullptr); + } catch(std::length_error & e) { + nothrow.handler(nothrow.ptr,10115,e.what(),nullptr); + } catch(std::domain_error & e) { + nothrow.handler(nothrow.ptr,10110,e.what(),nullptr); + } catch(std::invalid_argument & e) { + nothrow.handler(nothrow.ptr,10105,e.what(),nullptr); + } catch(std::logic_error & e) { + nothrow.handler(nothrow.ptr,10100,e.what(),nullptr); + // generic exception. message will be copied without new allocations + // reports all non caught exceptions that are derived from std::exception + // for instance, boost exceptions would end up here + } catch(std::exception & e) { + nothrow.handler(nothrow.ptr,10000,e.what(),nullptr); + } catch(...) { + std::cerr<<"+++ rethrowing an unknown error happened while using a plumed cmd"<<std::endl; + throw; + } } extern "C" void plumed_plumedmain_finalize(void*plumed) { diff --git a/src/wrapper/Plumed.h b/src/wrapper/Plumed.h index bd06dd37305d1fe43a1b45f707bcf96a643892f7..94b7b520d90c97e5bbad4647b3a6b9d13f1cc063 100644 --- a/src/wrapper/Plumed.h +++ b/src/wrapper/Plumed.h @@ -158,7 +158,7 @@ \endverbatim If you want to create on purpose an invalid Plumed object (useful in C++ to postpone - the loading of the library) you can use `Plumed p(Plumed::invalid());`. + the loading of the library) you can use `Plumed p(Plumed::makeInvalid());`. To know if the global object is valid instead you should use the following function: \verbatim @@ -255,6 +255,40 @@ When you compile the FORTRAN interface, wrapper functions are added with several possible name mangligs, so you should not experience problems linking the plumed library with a FORTRAN file. +\paragraph ReferencePlumedH-exceptions Error handling + + Bad things happen. In case an error is detected by PLUMED, either because of some user error, some internal bug, + or some mistake in using the library, an exception will be thrown. The behavior is different depending if you use + PLUMED from C/FORTRAN or from C++. + + First of all, notice that access to PLUMED goes through three functions: + - plumed_create: this, as of PLUMED 2.5, is guaranteed not to throw any exception. If there is a problem, it will + just return a NULL pointer + - plumed_cmd: this function might throw exceptions. + - plumed_finalize: this is a destructor and is guaranteed not to throw any exception. + + The following discussion concerns all the exceptions thrown by plumed_cmd. + + If you use C/FORTRAN, you will basically have no way to intercept the exception and the program will just terminate. + + If you use C++ but you are calling the C interface (e.g. \ref plumed_cmd), then you might be + able to catch the exceptions thrown by PLUMED. Notice that all the exceptions thrown by PLUMED inherit from std::exception, + so you might want to catch it by reference. Notice however that there is a C layer between your C++ code and the PLUMED + library. In principle, the stack unwinding performed during exception handling is undefined in C and might lead to problems + that are system and compiler dependent. In addition to this, there might be troubles when combining different compilers + or different standard libraries. E.g., if you MD code is linked against a given C++ library and PLUMED is linked against + another one, the two std::exception types will differ and you won't be able to catch exceptions raised by PLUMED. + + If you use C++ and you are calling the C++ interface (e.g. \ref Plumed::cmd), as of PLUMED 2.5 we implemented a complete + remapping of the exceptions thrown by PLUMED. This solves both the problems mentioned above. In particular: + - Instead of throwing an exception, PLUMED will return (using a \ref plumed_nothrow_handler) the details about the occurred error. + - An equivalent exception will be thrown within the inline PLUMED interface compiled with your MD code. + + As a consequence, you will be able to combine different compilers and avoid stack unwinding in the C layer. + + The remapping of exceptions takes care of all the standard C++ exceptions plus all the exceptions raised within + PLUMED. Unexpected exceptions that are derived from std::exception will be rethrown as std::exception. + \paragraph ReferencePlumedH-2-5 New in PLUMED 2.5 The wrappers in PLUMED 2.5 have been completely rewritten with several improvements. @@ -423,6 +457,34 @@ #define __PLUMED_WRAPPER_CXX_POLYMORPHIC 1 #endif +/* + 1: make the default constructor create an invalid object + 0: make the default constructor create a valid object + + Only for internal usage. +*/ +#ifndef __PLUMED_WRAPPER_CXX_DEFAULT_INVALID +#define __PLUMED_WRAPPER_CXX_DEFAULT_INVALID 0 +#endif + +/* + Size of a buffer used to store message for exceptions with noexcept constructor. + Should typically hold short messages. Anyway, as long as the stack size stays within the correct + limits it does not seem to affect efficiency. Notice that there cannot be recursive calls of + PLMD::Plumed::cmd, so that it should be in practice irrelevant. +*/ +#ifndef __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER +#define __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER 512 +#endif + + +/* + By default, assume C++11 compliant library is not available. +*/ + +#ifndef __PLUMED_WRAPPER_LIBCXX11 +#define __PLUMED_WRAPPER_LIBCXX11 0 +#endif /* The following macros are just to define shortcuts */ @@ -448,11 +510,13 @@ #define __PLUMED_WRAPPER_STD #endif -/* Allow using noexcept with C++11 compilers */ +/* Allow using noexcept and explicit with C++11 compilers */ #if __cplusplus > 199711L #define __PLUMED_WRAPPER_CXX_NOEXCEPT noexcept +#define __PLUMED_WRAPPER_CXX_EXPLICIT explicit #else #define __PLUMED_WRAPPER_CXX_NOEXCEPT throw() +#define __PLUMED_WRAPPER_CXX_EXPLICIT #endif /* Macros for anonymous namespace */ @@ -919,17 +983,26 @@ __PLUMED_WRAPPER_EXTERN_C_END /*}*/ #if defined( __cplusplus) && __PLUMED_WRAPPER_CXX /*{*/ -/* this is to include the NULL pointer */ #if __PLUMED_WRAPPER_CXX_STD -#include <cstdlib> +#include <cstdlib> /* NULL getenv */ +#include <cstring> /* strncat strlen */ #else #include <stdlib.h> +#include <string.h> #endif -/* these are to include standard exceptions */ -#include <exception> -#include <stdexcept> -#include <string> +#include <exception> /* exception bad_exception */ +#include <stdexcept> /* runtime_error logic_error invalid_argument domain_error length_error out_of_range range_error overflow_error underflow_error */ +#include <string> /* string */ +#include <ios> /* iostream_category (C++11) ios_base::failure (C++11 and C++<11) */ +#include <new> /* bad_alloc bad_array_new_length (C++11) */ +#include <typeinfo> /* bad_typeid bad_cast */ +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 +#include <system_error> /* system_error generic_category system_category */ +#include <future> /* future_category */ +#include <memory> /* bad_weak_ptr */ +#include <functional> /* bad_function_call */ +#endif /* C++ interface is hidden in PLMD namespace (same as plumed library) */ namespace PLMD { @@ -958,18 +1031,126 @@ class Plumed { */ struct NothrowHandler { + /** code used for translating messages */ int code; + /** short message buffer for non-throwing exceptions */ + char exception_buffer[__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER]; + /** if exception_buffer='\0', message stored as an allocatable string */ ::std::string what; + /** error code for system_error */ + int error_code; }; /** Callback function that sets the error handler. + + opt argument is interpreted as the pointer to a null terminated array of void*. + The number of non-null element is expected to be even, and there should be a null element + that follows. Every pair of pointers should point + to a char, identifying the type of argument passed, and an arbitrary object. + Currently used to (optionally) pass error_code. */ static void nothrow_handler(void*ptr,int code,const char*what,const void* opt) { NothrowHandler* h=(NothrowHandler*) ptr; h->code=code; - h->what=what; - (void) opt; /* not used yet */ + h->exception_buffer[0]='\0'; + h->what.clear(); + h->error_code=0; + /* + These codes correspond to exceptions that should not allocate a separate buffer but use the fixed one. + Notice that a mismatch between the exceptions using the stack buffer here and those implementing + the stack buffer would be in practice harmless. However, it makes sense to be consistent. + */ + if(code==10000 || (code>=11000 && code<12000)) { + __PLUMED_WRAPPER_STD strncat(h->exception_buffer,what,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); + } else { + h->what=what; + } + + /* interpret optional arguments */ + const void** options=(const void**)opt; + if(options) while(*options) { + if(*((char*)*options)=='c') h->error_code=*((int*)*(options+1)); + options+=2; + } + + static const char* debug=__PLUMED_WRAPPER_STD getenv("PLUMED_EXCEPTIONS_DEBUG"); + + if(debug) { + fprintf(stderr,"+++ PLUMED_EXCEPTIONS_DEBUG\n"); + fprintf(stderr,"+++ code: %d error_code: %d message:\n%s\n",h->code,h->error_code,what); + if(__PLUMED_WRAPPER_STD strlen(what) > __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1) fprintf(stderr,"+++ WARNING: message will be truncated\n"); + fprintf(stderr,"+++ END PLUMED_EXCEPTIONS_DEBUG\n"); + } + + } + + /** + Rethrow the exception. + */ + + static void rethrow(const NothrowHandler&h) { + /* The interpretation of the codes should be kept in sync with core/PlumedMainInitializer.cpp */ + /* check if we are using a full string or a fixes size buffer */ + const char* msg=(h.exception_buffer[0]?h.exception_buffer:h.what.c_str()); + if(h.code==1) throw Plumed::Invalid(msg); + /* logic errors */ + if(h.code>=10100 && h.code<10200) { + if(h.code>=10105 && h.code<10110) throw ::std::invalid_argument(msg); + if(h.code>=10110 && h.code<10115) throw ::std::domain_error(msg); + if(h.code>=10115 && h.code<10120) throw ::std::length_error(msg); + if(h.code>=10120 && h.code<10125) throw ::std::out_of_range(msg); + throw ::std::logic_error(msg); + } + /* runtime errors */ + if(h.code>=10200 && h.code<10300) { + if(h.code>=10205 && h.code<10210) throw ::std::range_error(msg); + if(h.code>=10210 && h.code<10215) throw ::std::overflow_error(msg); + if(h.code>=10215 && h.code<10220) throw ::std::underflow_error(msg); +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 + if(h.code==10220) throw ::std::system_error(h.error_code,::std::generic_category(),msg); + if(h.code==10221) throw ::std::system_error(h.error_code,::std::system_category(),msg); + if(h.code==10222) throw ::std::system_error(h.error_code,::std::iostream_category(),msg); + if(h.code==10223) throw ::std::system_error(h.error_code,::std::future_category(),msg); +#endif + if(h.code>=10230 && h.code<10240) { +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 +// These cases are probably useless as it looks like this should always be std::iostream_category + if(h.code==10230) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::generic_category())); + if(h.code==10231) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::system_category())); + if(h.code==10232) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::iostream_category())); + if(h.code==10233) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::future_category())); +#endif + throw ::std::ios_base::failure(msg); + } + throw ::std::runtime_error(msg); + } + /* "bad" errors */ + if(h.code>=11000 && h.code<11100) throw Plumed::std_bad_typeid(msg); + if(h.code>=11100 && h.code<11200) throw Plumed::std_bad_cast(msg); +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 + if(h.code>=11200 && h.code<11300) throw Plumed::std_bad_weak_ptr(msg); + if(h.code>=11300 && h.code<11400) throw Plumed::std_bad_function_call(msg); +#endif + if(h.code>=11400 && h.code<11500) { +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 + if(h.code>=11410 && h.code<11420) throw Plumed::std_bad_array_new_length(msg); +#endif + throw Plumed::std_bad_alloc(msg); + } + if(h.code>=11500 && h.code<11600) throw Plumed::std_bad_exception(msg); + /* lepton error */ + if(h.code>=19900 && h.code<20000) throw Plumed::LeptonException(msg); + /* plumed exceptions */ + if(h.code>=20000 && h.code<30000) { + /* debug - only raised with debug options */ + if(h.code>=20100 && h.code<20200) throw Plumed::ExceptionDebug(msg); + /* error - runtime check */ + if(h.code>=20200 && h.code<20300) throw Plumed::ExceptionError(msg); + throw Plumed::Exception(msg); + } + /* fallback for any other exception */ + throw Plumed::std_exception(msg); } public: @@ -983,7 +1164,7 @@ public: { ::std::string msg; public: - Exception(const char* msg): msg(msg) {} + __PLUMED_WRAPPER_CXX_EXPLICIT Exception(const char* msg): msg(msg) {} Exception(const Exception & other): msg(other.what()) {} const char* what() const __PLUMED_WRAPPER_CXX_NOEXCEPT {return msg.c_str();} ~Exception() __PLUMED_WRAPPER_CXX_NOEXCEPT {} @@ -996,7 +1177,7 @@ public: class ExceptionError : public Exception { public: - ExceptionError(const char* msg): Exception(msg) {} + __PLUMED_WRAPPER_CXX_EXPLICIT ExceptionError(const char* msg): Exception(msg) {} ExceptionError(const ExceptionError & other) : Exception(other.what()) {} ~ExceptionError() __PLUMED_WRAPPER_CXX_NOEXCEPT {} }; @@ -1008,7 +1189,7 @@ public: class ExceptionDebug : public Exception { public: - ExceptionDebug(const char* msg): Exception(msg) {} + __PLUMED_WRAPPER_CXX_EXPLICIT ExceptionDebug(const char* msg): Exception(msg) {} ExceptionDebug(const ExceptionDebug & other) : Exception(other.what()) {} ~ExceptionDebug() __PLUMED_WRAPPER_CXX_NOEXCEPT {} }; @@ -1020,11 +1201,75 @@ public: class Invalid : public Exception { public: - Invalid(const char* msg): Exception(msg) {} + __PLUMED_WRAPPER_CXX_EXPLICIT Invalid(const char* msg): Exception(msg) {} Invalid(const Invalid & other) : Exception(other.what()) {} ~Invalid() __PLUMED_WRAPPER_CXX_NOEXCEPT {} }; + /** + Class used to rethrow Lepton exceptions. + */ + + class LeptonException : + public ::std::exception + { + ::std::string msg; + public: + __PLUMED_WRAPPER_CXX_EXPLICIT LeptonException(const char* msg): msg(msg) {} + LeptonException(const LeptonException & other): msg(other.what()) {} + const char* what() const __PLUMED_WRAPPER_CXX_NOEXCEPT {return msg.c_str();} + ~LeptonException() __PLUMED_WRAPPER_CXX_NOEXCEPT {} + }; + +private: + /* + These exceptions are declared as private as they are not supposed to be + catched by value. they only exist to allow a buffer to be attached to + the std::exceptions that do not contain it already. + Notice that these exceptions are those whose constructor should never throw, and as + such they use a fixed size buffer. + */ + +#define __PLUMED_WRAPPER_NOSTRING_EXCEPTION(name) \ + class std_ ## name : \ + public ::std::name \ + { \ + char msg[__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER]; \ + public: \ + __PLUMED_WRAPPER_CXX_EXPLICIT std_ ## name(const char * msg) __PLUMED_WRAPPER_CXX_NOEXCEPT { \ + this->msg[0]='\0'; \ + __PLUMED_WRAPPER_STD strncat(this->msg,msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \ + static const char* debug=__PLUMED_WRAPPER_STD getenv("PLUMED_EXCEPTIONS_DEBUG"); \ + if(debug && __PLUMED_WRAPPER_STD strlen(msg) > __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1) fprintf(stderr,"+++ WARNING: message will be truncated\n"); \ + } \ + std_ ## name(const std_ ## name & other) __PLUMED_WRAPPER_CXX_NOEXCEPT { \ + msg[0]='\0'; \ + __PLUMED_WRAPPER_STD strncat(msg,other.msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \ + } \ + std_ ## name & operator=(const std_ ## name & other) __PLUMED_WRAPPER_CXX_NOEXCEPT { \ + if(this==&other) return *this;\ + msg[0]='\0'; \ + __PLUMED_WRAPPER_STD strncat(msg,other.msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \ + return *this; \ + } \ + const char* what() const __PLUMED_WRAPPER_CXX_NOEXCEPT {return msg;} \ + ~std_ ## name() __PLUMED_WRAPPER_CXX_NOEXCEPT {} \ + }; + + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_typeid) + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_cast) +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_weak_ptr) + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_function_call) +#endif + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_alloc) +#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11 + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_array_new_length) +#endif + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_exception) + __PLUMED_WRAPPER_NOSTRING_EXCEPTION(exception) + +public: /** Check if plumed is installed (for runtime binding) @@ -1121,7 +1366,7 @@ public: \return The Plumed global object */ static Plumed global() __PLUMED_WRAPPER_CXX_NOEXCEPT { - return plumed_global(); + return Plumed(plumed_global()); } #endif /*}*/ /** @@ -1133,7 +1378,11 @@ public: \note Performs the same task a plumed_create() */ Plumed()__PLUMED_WRAPPER_CXX_NOEXCEPT : +#if __PLUMED_WRAPPER_CXX_DEFAULT_INVALID + main(plumed_create_invalid()) +#else main(plumed_create()) +#endif { } @@ -1145,12 +1394,8 @@ Plumed()__PLUMED_WRAPPER_CXX_NOEXCEPT : The reference counter for the corresponding object will be increased to make sure that the object will be available after plumed_f_finalize is called if the created object is still in scope. - */ -// to have maximum portability of this file I do not use the explicit keyword here -// I thus add a suppress command for cppcheck -// cppcheck-suppress noExplicitConstructor -Plumed(const char*c)__PLUMED_WRAPPER_CXX_NOEXCEPT : +__PLUMED_WRAPPER_CXX_EXPLICIT Plumed(const char*c)__PLUMED_WRAPPER_CXX_NOEXCEPT : main(plumed_create_reference_f(c)) { } @@ -1158,10 +1403,7 @@ Plumed(const char*c)__PLUMED_WRAPPER_CXX_NOEXCEPT : /** Create a reference from a void* pointer. Available as of PLUMED 2.5. */ -// to have maximum portability of this file I do not use the explicit keyword here -// I thus add a suppress command for cppcheck -// cppcheck-suppress noExplicitConstructor -Plumed(void*v)__PLUMED_WRAPPER_CXX_NOEXCEPT : +__PLUMED_WRAPPER_CXX_EXPLICIT Plumed(void*v)__PLUMED_WRAPPER_CXX_NOEXCEPT : main(plumed_create_reference_v(v)) { } @@ -1175,10 +1417,7 @@ Plumed(void*v)__PLUMED_WRAPPER_CXX_NOEXCEPT : to make sure that the object will be available after plumed_finalize is called if the created object is still in scope. */ -// to have maximum portability of this file I do not use the explicit keyword here -// I thus add a suppress command for cppcheck -// cppcheck-suppress noExplicitConstructor -Plumed(plumed p)__PLUMED_WRAPPER_CXX_NOEXCEPT : +__PLUMED_WRAPPER_CXX_EXPLICIT Plumed(plumed p)__PLUMED_WRAPPER_CXX_NOEXCEPT : main(plumed_create_reference(p)) { } @@ -1285,17 +1524,30 @@ Plumed(Plumed&&p)__PLUMED_WRAPPER_CXX_NOEXCEPT : However, there will be some error reported related to the attempt to load the kernel when `p` is initialized. The following solution is the optimal one: \verbatim - Plumed p(Plumed::invalid()); + Plumed p(Plumed::makeInvalid()); setenv("PLUMED_KERNEL","/path/to/kernel/libplumedKernel.so",1); p=Plumed(); p.cmd("init") \endverbatim */ - static Plumed invalid() __PLUMED_WRAPPER_CXX_NOEXCEPT { + static Plumed makeInvalid() __PLUMED_WRAPPER_CXX_NOEXCEPT { // use decref to remove the extra reference return Plumed(plumed_create_invalid()).decref(); } + /** + Create a valid PLMD::Plumed object. + + Can be used to create a valid object e.g. when Plumed.h was compiled with + `-D__PLUMED_WRAPPER_CXX_DEFAULT_INVALID`. For internal usage. + */ + + static Plumed makeValid()__PLUMED_WRAPPER_CXX_NOEXCEPT { +// use decref to remove the extra reference + return Plumed(plumed_create()).decref(); + } + + /** Retrieve the C plumed structure for this object. @@ -1384,21 +1636,9 @@ Plumed(Plumed&&p)__PLUMED_WRAPPER_CXX_NOEXCEPT : h.code=0; plumed_nothrow_handler nothrow= {&h,nothrow_handler}; plumed_cmd_nothrow(main,key,val,nothrow); - /* The interpretation of the codes should be kept in sync with core/PlumedMainInitializer.cpp */ - if(h.code==0) return; - if(h.code==1) throw Plumed::Invalid(h.what.c_str()); - if(h.code>=10000 && h.code<20000) { - if(h.code>=10100 && h.code<10200) throw ::std::logic_error(h.what.c_str()); - if(h.code>=10200 && h.code<10300) throw ::std::runtime_error(h.what.c_str()); - } - if(h.code>=20000 && h.code<30000) { - if(h.code>=20100 && h.code<20200) throw Plumed::ExceptionDebug(h.what.c_str()); - if(h.code>=20200 && h.code<20300) throw Plumed::ExceptionError(h.what.c_str()); - throw Plumed::Exception(h.what.c_str()); - } - /* all other exception types are mapped to runtime_error */ - throw ::std::runtime_error(h.what.c_str()); + if(h.code!=0) rethrow(h); } + /** Destructor @@ -1423,7 +1663,7 @@ Plumed(Plumed&&p)__PLUMED_WRAPPER_CXX_NOEXCEPT : Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator==(const Plumed&a,const Plumed&b) { +bool operator==(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()==b.toVoid(); } @@ -1432,7 +1672,7 @@ bool operator==(const Plumed&a,const Plumed&b) { Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator!=(const Plumed&a,const Plumed&b) { +bool operator!=(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()!=b.toVoid(); } @@ -1441,7 +1681,7 @@ bool operator!=(const Plumed&a,const Plumed&b) { Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator<=(const Plumed&a,const Plumed&b) { +bool operator<=(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()<=b.toVoid(); } @@ -1450,7 +1690,7 @@ bool operator<=(const Plumed&a,const Plumed&b) { Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator<(const Plumed&a,const Plumed&b) { +bool operator<(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()<b.toVoid(); } @@ -1459,7 +1699,7 @@ bool operator<(const Plumed&a,const Plumed&b) { Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator>=(const Plumed&a,const Plumed&b) { +bool operator>=(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()>=b.toVoid(); } @@ -1468,7 +1708,7 @@ bool operator>=(const Plumed&a,const Plumed&b) { Comparison operator. Available as of PLUMED 2.5. */ inline -bool operator>(const Plumed&a,const Plumed&b) { +bool operator>(const Plumed&a,const Plumed&b) __PLUMED_WRAPPER_CXX_NOEXCEPT { return a.toVoid()>b.toVoid(); } @@ -1573,15 +1813,15 @@ __PLUMED_WRAPPER_ANONYMOUS_END /*}*/ #endif /*}*/ #ifdef __PLUMED_HAS_DLOPEN -#include <dlfcn.h> +#include <dlfcn.h> /* dlopen dlerror dlsym */ #endif #if __PLUMED_WRAPPER_CXX_STD -#include <cstdio> -#include <cstring> -#include <cassert> -#include <cstdlib> -#include <climits> +#include <cstdio> /* fprintf */ +#include <cstring> /* memcpy strlen strncpy memcmp memmove strcmp memcpy */ +#include <cassert> /* assert */ +#include <cstdlib> /* getenv malloc free abort exit */ +#include <climits> /* CHAR_BIT */ #else #include <stdio.h> #include <string.h> @@ -1725,7 +1965,7 @@ void* plumed_attempt_dlopen(const char*path,int mode) { while(pc>=pathcopy && __PLUMED_WRAPPER_STD memcmp(pc,"Kernel",6)) pc--; if(pc>=pathcopy) { __PLUMED_WRAPPER_STD memmove(pc, pc+6, __PLUMED_WRAPPER_STD strlen(pc)-5); - __PLUMED_FPRINTF(stderr,"+++ This error is expected if you are trying to load a kernel <=2.4"); + __PLUMED_FPRINTF(stderr,"+++ This error is expected if you are trying to load a kernel <=2.4\n"); __PLUMED_FPRINTF(stderr,"+++ Trying %s +++\n",pathcopy); p=dlopen(pathcopy,mode); if(!p) __PLUMED_FPRINTF(stderr,"+++ An error occurred. Message from dlopen(): %s +++\n",dlerror());