From f66ad7e7c1b36b510c45680b0b820f520d91e841 Mon Sep 17 00:00:00 2001 From: Paolo Tosco Date: Fri, 25 Oct 2024 08:17:07 +0200 Subject: [PATCH] Replace awful enum reflection macros with Better Enums (#7913) * - moved SMILES and RGroupDecomp JSON parsers to their own translation units - added missing DLL export decorators that had been previously forgotten - changed the signatures of MolToCXSmiles and updateCXSmilesFieldsFromJSON to replace enum parameters with the underlying types - updated ReleaseNotes.md * make sure cxSmilesFields is only updated if the JSON string contains keys belonging to the CXSmilesFields enum * added missing copyright notices --------- Co-authored-by: ptosco --- CMakeLists.txt | 12 +++ .../RGroupDecomposition/CMakeLists.txt | 32 ++++--- .../RGroupDecompJSONParsers.cpp | 89 ++++++++++++++++++ .../RGroupDecompJSONParsers.h | 24 +++++ .../RGroupDecompParams.cpp | 86 ----------------- .../RGroupDecomposition/RGroupDecompParams.h | 94 ++++++++----------- Code/GraphMol/SmilesParse/CMakeLists.txt | 5 +- .../SmilesParse/SmilesJSONParsers.cpp | 84 +++++++++++++++++ Code/GraphMol/SmilesParse/SmilesJSONParsers.h | 28 ++++++ Code/GraphMol/SmilesParse/SmilesWrite.cpp | 63 ------------- Code/GraphMol/SmilesParse/SmilesWrite.h | 78 +++++---------- Code/JavaWrappers/RGroupDecomposition.i | 3 + Code/JavaWrappers/SmilesWrite.i | 3 + Code/MinimalLib/cffiwrapper.cpp | 9 +- Code/MinimalLib/minilib.cpp | 12 ++- Code/RDGeneral/BetterEnums.h | 19 ++++ Code/RDGeneral/CMakeLists.txt | 6 ++ ReleaseNotes.md | 8 ++ 18 files changed, 374 insertions(+), 281 deletions(-) create mode 100644 Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.cpp create mode 100644 Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h create mode 100644 Code/GraphMol/SmilesParse/SmilesJSONParsers.cpp create mode 100644 Code/GraphMol/SmilesParse/SmilesJSONParsers.h create mode 100644 Code/RDGeneral/BetterEnums.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 189cec43f07..705e292b200 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,18 @@ if(NOT Catch2_FOUND) FetchContent_MakeAvailable(Catch2) endif() +#include better-enums +find_package(better_enums 0 QUIET) +if(NOT better_enums) + Include(FetchContent) + + FetchContent_Declare( + better_enums + GIT_REPOSITORY https://github.com/aantron/better-enums.git + GIT_TAG c35576bed0295689540b39873126129adfa0b4c8 # 0.11.3 + ) +endif() + if(RDK_INSTALL_INTREE) set(RDKit_BinDir "${CMAKE_SOURCE_DIR}/bin") set(RDKit_LibDir "${CMAKE_SOURCE_DIR}/lib") diff --git a/Code/GraphMol/RGroupDecomposition/CMakeLists.txt b/Code/GraphMol/RGroupDecomposition/CMakeLists.txt index 38efbe55ace..1fcf233c815 100644 --- a/Code/GraphMol/RGroupDecomposition/CMakeLists.txt +++ b/Code/GraphMol/RGroupDecomposition/CMakeLists.txt @@ -1,33 +1,35 @@ -rdkit_library(RGroupDecomposition RGroupDecomp.cpp RGroupDecompData.cpp RGroupData.cpp RGroupUtils.cpp RGroupCore.cpp - RGroupDecompParams.cpp RGroupScore.cpp RGroupFingerprintScore.cpp RGroupGa.cpp +rdkit_library(RGroupDecomposition RGroupDecomp.cpp RGroupDecompData.cpp + RGroupData.cpp RGroupUtils.cpp RGroupCore.cpp + RGroupDecompParams.cpp RGroupScore.cpp RGroupFingerprintScore.cpp + RGroupGa.cpp RGroupDecompJSONParsers.cpp LINK_LIBRARIES FMCS ChemTransforms SubstructMatch SmilesParse Fingerprints - GraphMol RDGeneral ga MolEnumerator TautomerQuery ) + GraphMol RDGeneral ga MolEnumerator TautomerQuery) target_compile_definitions(RGroupDecomposition PRIVATE RDKIT_RGROUPDECOMPOSITION_BUILD) rdkit_headers( - RGroupDecomp.h RGroupDecompParams.h - DEST GraphMol/RGroupDecomposition) + RGroupDecomp.h RGroupDecompParams.h RGroupDecompJSONParsers.h + DEST GraphMol/RGroupDecomposition) if(RDK_BUILD_PYTHON_WRAPPERS) -add_subdirectory(Wrap) + add_subdirectory(Wrap) endif() rdkit_test(testRGroupDecomp testRGroupDecomp.cpp - LINK_LIBRARIES RGroupDecomposition ) + LINK_LIBRARIES RGroupDecomposition) rdkit_test(testRGroupDecompInternals testRGroupInternals.cpp - LINK_LIBRARIES RGroupDecomposition ) + LINK_LIBRARIES RGroupDecomposition) rdkit_catch_test(rgroupCatchTests catch_rgd.cpp - LINK_LIBRARIES RGroupDecomposition ) + LINK_LIBRARIES RGroupDecomposition) find_package(Boost ${RDK_BOOST_VERSION} COMPONENTS program_options CONFIG) if(RDK_BUILD_CPP_TESTS AND Boost_FOUND) - add_executable(gaExample GaExample.cpp) - if(NOT Boost_USE_STATIC_LIBS) - target_compile_definitions(gaExample PUBLIC -DBOOST_PROGRAM_OPTIONS_DYN_LINK) - endif() - target_link_libraries(gaExample RGroupDecomposition ${Boost_PROGRAM_OPTIONS_LIBRARY}) -endif() \ No newline at end of file + add_executable(gaExample GaExample.cpp) + if(NOT Boost_USE_STATIC_LIBS) + target_compile_definitions(gaExample PUBLIC -DBOOST_PROGRAM_OPTIONS_DYN_LINK) + endif() + target_link_libraries(gaExample RGroupDecomposition ${Boost_PROGRAM_OPTIONS_LIBRARY}) +endif() diff --git a/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.cpp b/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.cpp new file mode 100644 index 00000000000..143cdb1dc6b --- /dev/null +++ b/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.cpp @@ -0,0 +1,89 @@ +// +// Copyright (C) 2024 Novartis Biomedical Research and other RDKit contributors +// +// @@ All Rights Reserved @@ +// This file is part of the RDKit. +// The contents are covered by the terms of the BSD license +// which is included in the file license.txt, found at the root +// of the RDKit source tree. +// + +#define USE_BETTER_ENUMS +#include "RGroupDecompJSONParsers.h" +#include +#include +#include +#include + +namespace RDKit { + +void updateRGroupDecompositionParametersFromJSON( + RGroupDecompositionParameters ¶ms, const std::string &details_json) { + updateRGroupDecompositionParametersFromJSON(params, details_json.c_str()); +} + +void updateRGroupDecompositionParametersFromJSON( + RGroupDecompositionParameters ¶ms, const char *details_json) { + if (details_json && strlen(details_json)) { + std::istringstream ss; + boost::property_tree::ptree pt; + ss.str(details_json); + boost::property_tree::read_json(ss, pt); + + std::string labels; + labels = pt.get("labels", labels); + if (RGroupLabels::_is_valid(labels.c_str())) { + params.labels = RGroupLabels::_from_string(labels.c_str()); + } + + std::string matchingStrategy; + matchingStrategy = + pt.get("matchingStrategy", matchingStrategy); + if (RGroupMatching::_is_valid(matchingStrategy.c_str())) { + params.matchingStrategy = + RGroupMatching::_from_string(matchingStrategy.c_str()); + } + + std::string scoreMethod; + scoreMethod = pt.get("scoreMethod", scoreMethod); + if (RGroupScore::_is_valid(scoreMethod.c_str())) { + params.scoreMethod = RGroupScore::_from_string(scoreMethod.c_str()); + } + + std::string rgroupLabelling; + rgroupLabelling = pt.get("rgroupLabelling", rgroupLabelling); + if (RGroupLabelling::_is_valid(rgroupLabelling.c_str())) { + params.rgroupLabelling = + RGroupLabelling::_from_string(rgroupLabelling.c_str()); + } + + std::string alignment; + alignment = pt.get("alignment", alignment); + if (RGroupCoreAlignment::_is_valid(alignment.c_str())) { + params.alignment = RGroupCoreAlignment::_from_string(alignment.c_str()); + } + + params.chunkSize = pt.get("chunkSize", params.chunkSize); + params.onlyMatchAtRGroups = + pt.get("onlyMatchAtRGroups", params.onlyMatchAtRGroups); + params.removeAllHydrogenRGroups = pt.get( + "removeAllHydrogenRGroups", params.removeAllHydrogenRGroups); + params.removeAllHydrogenRGroupsAndLabels = + pt.get("removeAllHydrogenRGroupsAndLabels", + params.removeAllHydrogenRGroupsAndLabels); + params.removeHydrogensPostMatch = pt.get( + "removeHydrogensPostMatch", params.removeHydrogensPostMatch); + params.allowNonTerminalRGroups = + pt.get("allowNonTerminalRGroups", params.allowNonTerminalRGroups); + params.allowMultipleRGroupsOnUnlabelled = + pt.get("allowMultipleRGroupsOnUnlabelled", + params.allowMultipleRGroupsOnUnlabelled); + params.doTautomers = pt.get("doTautomers", params.doTautomers); + params.doEnumeration = pt.get("doEnumeration", params.doEnumeration); + params.includeTargetMolInResults = pt.get( + "includeTargetMolInResults", params.includeTargetMolInResults); + params.timeout = pt.get("timeout", params.timeout); + } +} + +} // end namespace RDKit diff --git a/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h b/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h new file mode 100644 index 00000000000..23b102b572a --- /dev/null +++ b/Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h @@ -0,0 +1,24 @@ +// +// Copyright (C) 2024 Novartis Biomedical Research and other RDKit contributors +// +// @@ All Rights Reserved @@ +// This file is part of the RDKit. +// The contents are covered by the terms of the BSD license +// which is included in the file license.txt, found at the root +// of the RDKit source tree. +// + +#pragma once + +#include "RGroupDecompParams.h" + +namespace RDKit { + +RDKIT_RGROUPDECOMPOSITION_EXPORT void +updateRGroupDecompositionParametersFromJSON( + RGroupDecompositionParameters ¶ms, const std::string &details_json); +RDKIT_RGROUPDECOMPOSITION_EXPORT void +updateRGroupDecompositionParametersFromJSON( + RGroupDecompositionParameters ¶ms, const char *details_json); + +} // end namespace RDKit diff --git a/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.cpp b/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.cpp index 2a68e56b39e..dd34be28596 100644 --- a/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.cpp +++ b/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.cpp @@ -16,10 +16,6 @@ #include #include #include -#include -#include -#include -#include namespace RDKit { @@ -73,88 +69,6 @@ bool hasAttachedLabels(const ROMol &mol, const Atom *atom, } // namespace -void updateRGroupDecompositionParametersFromJSON( - RGroupDecompositionParameters ¶ms, const std::string &details_json) { - updateRGroupDecompositionParametersFromJSON(params, details_json.c_str()); -} - -void updateRGroupDecompositionParametersFromJSON( - RGroupDecompositionParameters ¶ms, const char *details_json) { - static const std::map rGroupLabelsMap{ - RGROUPLABELS_ENUM_ITEMS}; - static const std::map rGroupMatchingMap{ - RGROUPMATCHING_ENUM_ITEMS}; - static const std::map rGroupLabellingMap{ - RGROUPLABELLING_ENUM_ITEMS}; - static const std::map - rGroupCoreAlignmentMap{RGROUPCOREALIGNMENT_ENUM_ITEMS}; - static const std::map rGroupScoreMap{ - RGROUPSCORE_ENUM_ITEMS}; - if (details_json && strlen(details_json)) { - std::istringstream ss; - boost::property_tree::ptree pt; - ss.str(details_json); - boost::property_tree::read_json(ss, pt); - - std::string labels; - labels = pt.get("labels", labels); - auto rGroupLabelsMapIt = rGroupLabelsMap.find(labels); - if (rGroupLabelsMapIt != rGroupLabelsMap.end()) { - params.labels = rGroupLabelsMapIt->second; - } - - std::string matchingStrategy; - matchingStrategy = - pt.get("matchingStrategy", matchingStrategy); - auto rGroupMatchingMapIt = rGroupMatchingMap.find(matchingStrategy); - if (rGroupMatchingMapIt != rGroupMatchingMap.end()) { - params.matchingStrategy = rGroupMatchingMapIt->second; - } - - std::string scoreMethod; - scoreMethod = pt.get("scoreMethod", scoreMethod); - auto rGroupScoreMapIt = rGroupScoreMap.find(scoreMethod); - if (rGroupScoreMapIt != rGroupScoreMap.end()) { - params.scoreMethod = rGroupScoreMapIt->second; - } - - std::string rgroupLabelling; - rgroupLabelling = pt.get("rgroupLabelling", rgroupLabelling); - auto rGroupLabellingMapIt = rGroupLabellingMap.find(rgroupLabelling); - if (rGroupLabellingMapIt != rGroupLabellingMap.end()) { - params.rgroupLabelling = rGroupLabellingMapIt->second; - } - - std::string alignment; - alignment = pt.get("alignment", alignment); - auto rGroupCoreAlignmentMapIt = rGroupCoreAlignmentMap.find(alignment); - if (rGroupCoreAlignmentMapIt != rGroupCoreAlignmentMap.end()) { - params.alignment = rGroupCoreAlignmentMapIt->second; - } - - params.chunkSize = pt.get("chunkSize", params.chunkSize); - params.onlyMatchAtRGroups = - pt.get("onlyMatchAtRGroups", params.onlyMatchAtRGroups); - params.removeAllHydrogenRGroups = pt.get( - "removeAllHydrogenRGroups", params.removeAllHydrogenRGroups); - params.removeAllHydrogenRGroupsAndLabels = - pt.get("removeAllHydrogenRGroupsAndLabels", - params.removeAllHydrogenRGroupsAndLabels); - params.removeHydrogensPostMatch = pt.get( - "removeHydrogensPostMatch", params.removeHydrogensPostMatch); - params.allowNonTerminalRGroups = - pt.get("allowNonTerminalRGroups", params.allowNonTerminalRGroups); - params.allowMultipleRGroupsOnUnlabelled = - pt.get("allowMultipleRGroupsOnUnlabelled", - params.allowMultipleRGroupsOnUnlabelled); - params.doTautomers = pt.get("doTautomers", params.doTautomers); - params.doEnumeration = pt.get("doEnumeration", params.doEnumeration); - params.includeTargetMolInResults = pt.get( - "includeTargetMolInResults", params.includeTargetMolInResults); - params.timeout = pt.get("timeout", params.timeout); - } -} - unsigned int RGroupDecompositionParameters::autoGetLabels(const RWMol &core) { unsigned int autoLabels = 0; if (!onlyMatchAtRGroups) { diff --git a/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.h b/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.h index 085df57de9b..ba968df83c7 100644 --- a/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.h +++ b/Code/GraphMol/RGroupDecomposition/RGroupDecompParams.h @@ -14,59 +14,52 @@ #include "../RDKitBase.h" #include +#include namespace RDKit { -#define RGROUPLABELS_ENUM_ITEMS \ - RGD_ENUM_ITEM(IsotopeLabels, 1 << 0) \ - RGD_ENUM_ITEM(AtomMapLabels, 1 << 1) \ - RGD_ENUM_ITEM(AtomIndexLabels, 1 << 2) \ - RGD_ENUM_ITEM(RelabelDuplicateLabels, 1 << 3) \ - RGD_ENUM_ITEM(MDLRGroupLabels, 1 << 4) \ - RGD_ENUM_ITEM(DummyAtomLabels, \ - 1 << 5) /* These are rgroups but will get relabelled */ \ - RGD_ENUM_ITEM(AutoDetect, 0xFF) - -#define RGROUPMATCHING_ENUM_ITEMS \ - RGD_ENUM_ITEM(Greedy, 1 << 0) \ - RGD_ENUM_ITEM(GreedyChunks, 1 << 1) \ - RGD_ENUM_ITEM(Exhaustive, 1 << 2) /* not really useful for large sets */ \ - RGD_ENUM_ITEM(NoSymmetrization, 1 << 3) \ - RGD_ENUM_ITEM(GA, 1 << 4) - -#define RGROUPLABELLING_ENUM_ITEMS \ - RGD_ENUM_ITEM(AtomMap, 1 << 0) \ - RGD_ENUM_ITEM(Isotope, 1 << 1) \ - RGD_ENUM_ITEM(MDLRGroup, 1 << 2) - -#define RGROUPCOREALIGNMENT_ENUM_ITEMS \ - RGD_ENUM_ITEM(NoAlignment, 0) \ - RGD_ENUM_ITEM(MCS, 1 << 0) - -#define RGROUPSCORE_ENUM_ITEMS \ - RGD_ENUM_ITEM(Match, 1 << 0) \ - RGD_ENUM_ITEM(FingerprintVariance, 1 << 2) - -#define RGD_ENUM_ITEM(k, v) k = v, -typedef enum { RGROUPLABELS_ENUM_ITEMS } RGroupLabels; - -typedef enum { RGROUPMATCHING_ENUM_ITEMS } RGroupMatching; - -typedef enum { RGROUPLABELLING_ENUM_ITEMS } RGroupLabelling; - -typedef enum { RGROUPCOREALIGNMENT_ENUM_ITEMS } RGroupCoreAlignment; - -typedef enum { RGROUPSCORE_ENUM_ITEMS } RGroupScore; -#undef RGD_ENUM_ITEM -#define RGD_STD_MAP_ITEM(k) {#k, k}, -#define RGD_ENUM_ITEM(k, v) RGD_STD_MAP_ITEM(k) +BETTER_ENUM(RGroupLabels, unsigned int, + IsotopeLabels = 0x01, + AtomMapLabels = 0x02, + AtomIndexLabels = 0x04, + RelabelDuplicateLabels = 0x08, + MDLRGroupLabels = 0x10, + DummyAtomLabels = 0x20, // These are rgroups but will get relabelled + AutoDetect = 0xFF +); + +BETTER_ENUM(RGroupMatching, unsigned int, + Greedy = 0x01, + GreedyChunks = 0x02, + Exhaustive = 0x04, // not really useful for large sets + NoSymmetrization = 0x08, + GA = 0x10 +); + +BETTER_ENUM( + RGroupLabelling, unsigned int, + AtomMap = 0x01, + Isotope = 0x02, + MDLRGroup = 0x04 +); + +BETTER_ENUM(RGroupCoreAlignment, unsigned int, + NoAlignment = 0x0, + MCS = 0x01 +); + +BETTER_ENUM(RGroupScore, unsigned int, + Match = 0x1, + FingerprintVariance = 0x4 +); struct RDKIT_RGROUPDECOMPOSITION_EXPORT RGroupDecompositionParameters { - unsigned int labels = AutoDetect; - unsigned int matchingStrategy = GreedyChunks; - unsigned int scoreMethod = Match; - unsigned int rgroupLabelling = AtomMap | MDLRGroup; - unsigned int alignment = MCS; + unsigned int labels = RGroupLabels::AutoDetect; + unsigned int matchingStrategy = RGroupMatching::GreedyChunks; + unsigned int scoreMethod = RGroupScore::Match; + unsigned int rgroupLabelling = + RGroupLabelling::AtomMap | RGroupLabelling::MDLRGroup; + unsigned int alignment = RGroupCoreAlignment::MCS; unsigned int chunkSize = 5; //! only allow rgroup decomposition at the specified rgroups @@ -133,11 +126,6 @@ struct RDKIT_RGROUPDECOMPOSITION_EXPORT RGroupDecompositionParameters { void checkNonTerminal(const Atom &atom) const; }; -void updateRGroupDecompositionParametersFromJSON( - RGroupDecompositionParameters ¶ms, const std::string &details_json); -void updateRGroupDecompositionParametersFromJSON( - RGroupDecompositionParameters ¶ms, const char *details_json); - } // namespace RDKit #endif // RDKIT_RGROUPDECOMPPARAMS_H diff --git a/Code/GraphMol/SmilesParse/CMakeLists.txt b/Code/GraphMol/SmilesParse/CMakeLists.txt index cdfca7e1d61..e02bfbb345a 100644 --- a/Code/GraphMol/SmilesParse/CMakeLists.txt +++ b/Code/GraphMol/SmilesParse/CMakeLists.txt @@ -59,7 +59,7 @@ endif(BISON_EXECUTABLE) rdkit_library(SmilesParse SmilesParse.cpp SmilesParseOps.cpp SmilesWrite.cpp SmartsWrite.cpp CXSmilesOps.cpp - CanonicalizeStereoGroups.cpp + CanonicalizeStereoGroups.cpp SmilesJSONParsers.cpp ${BISON_OUTPUT_FILES} ${FLEX_OUTPUT_FILES} LINK_LIBRARIES GraphMol RDGeneral) @@ -70,7 +70,8 @@ rdkit_headers(primes.h SmilesParse.h SmilesParseOps.h SmilesWrite.h - CanonicalizeStereoGroups.h DEST GraphMol/SmilesParse) + CanonicalizeStereoGroups.h + SmilesJSONParsers.h DEST GraphMol/SmilesParse) rdkit_test(smiTest1 test.cpp LINK_LIBRARIES FileParsers SmilesParse ) rdkit_test(smiTest2 test2.cpp LINK_LIBRARIES SmilesParse ) diff --git a/Code/GraphMol/SmilesParse/SmilesJSONParsers.cpp b/Code/GraphMol/SmilesParse/SmilesJSONParsers.cpp new file mode 100644 index 00000000000..1a240b9f590 --- /dev/null +++ b/Code/GraphMol/SmilesParse/SmilesJSONParsers.cpp @@ -0,0 +1,84 @@ +// +// Copyright (C) 2024 Novartis Biomedical Research and other RDKit contributors +// +// @@ All Rights Reserved @@ +// This file is part of the RDKit. +// The contents are covered by the terms of the BSD license +// which is included in the file license.txt, found at the root +// of the RDKit source tree. +// + +#define USE_BETTER_ENUMS +#include +#include +#include +#include +#include "SmilesJSONParsers.h" + +namespace RDKit { + +void updateSmilesWriteParamsFromJSON(SmilesWriteParams ¶ms, + const char *details_json) { + if (details_json && strlen(details_json)) { + boost::property_tree::ptree pt; + std::istringstream ss; + ss.str(details_json); + boost::property_tree::read_json(ss, pt); + params.doIsomericSmiles = + pt.get("doIsomericSmiles", params.doIsomericSmiles); + params.doKekule = pt.get("doKekule", params.doKekule); + params.rootedAtAtom = pt.get("rootedAtAtom", params.rootedAtAtom); + params.canonical = pt.get("canonical", params.canonical); + params.allBondsExplicit = + pt.get("allBondsExplicit", params.allBondsExplicit); + params.allHsExplicit = pt.get("allHsExplicit", params.allHsExplicit); + params.doRandom = pt.get("doRandom", params.doRandom); + } +} + +void updateSmilesWriteParamsFromJSON(SmilesWriteParams ¶ms, + const std::string &details_json) { + updateSmilesWriteParamsFromJSON(params, details_json.c_str()); +} + +void updateCXSmilesFieldsFromJSON(std::uint32_t &cxSmilesFields, + unsigned int &restoreBondDirs, + const char *details_json) { + if (details_json && strlen(details_json)) { + boost::property_tree::ptree pt; + std::istringstream ss; + ss.str(details_json); + boost::property_tree::read_json(ss, pt); + auto cxSmilesFieldsFromJson = + (+SmilesWrite::CXSmilesFields::CX_NONE)._to_integral(); + bool haveCXSmilesFields = false; + for (const auto *key : SmilesWrite::CXSmilesFields::_names()) { + const auto it = pt.find(key); + if (it != pt.not_found()) { + haveCXSmilesFields = true; + if (it->second.get_value()) { + cxSmilesFieldsFromJson |= + SmilesWrite::CXSmilesFields::_from_string(key)._to_integral(); + } + } + } + if (haveCXSmilesFields) { + cxSmilesFields = cxSmilesFieldsFromJson; + } + std::string restoreBondDirOption; + restoreBondDirOption = pt.get("restoreBondDirOption", restoreBondDirOption); + if (RestoreBondDirOption::_is_valid(restoreBondDirOption.c_str())) { + restoreBondDirs = + RestoreBondDirOption::_from_string(restoreBondDirOption.c_str()); + } + } +} + +void updateCXSmilesFieldsFromJSON(std::uint32_t &cxSmilesFields, + unsigned int &restoreBondDirs, + const std::string &details_json) { + updateCXSmilesFieldsFromJSON(cxSmilesFields, restoreBondDirs, + details_json.c_str()); +} + +} // end namespace RDKit diff --git a/Code/GraphMol/SmilesParse/SmilesJSONParsers.h b/Code/GraphMol/SmilesParse/SmilesJSONParsers.h new file mode 100644 index 00000000000..4fe516b15c9 --- /dev/null +++ b/Code/GraphMol/SmilesParse/SmilesJSONParsers.h @@ -0,0 +1,28 @@ +// +// Copyright (C) 2024 Novartis Biomedical Research and other RDKit contributors +// +// @@ All Rights Reserved @@ +// This file is part of the RDKit. +// The contents are covered by the terms of the BSD license +// which is included in the file license.txt, found at the root +// of the RDKit source tree. +// + +#pragma once + +#include "SmilesWrite.h" + +namespace RDKit { + +RDKIT_SMILESPARSE_EXPORT void updateSmilesWriteParamsFromJSON( + SmilesWriteParams ¶ms, const std::string &details_json); +RDKIT_SMILESPARSE_EXPORT void updateSmilesWriteParamsFromJSON( + SmilesWriteParams ¶ms, const char *details_json); +RDKIT_SMILESPARSE_EXPORT void updateCXSmilesFieldsFromJSON( + std::uint32_t &cxSmilesFields, unsigned int &restoreBondDirs, + const std::string &details_json); +RDKIT_SMILESPARSE_EXPORT void updateCXSmilesFieldsFromJSON( + std::uint32_t &cxSmilesFields, unsigned int &restoreBondDirs, + const char *details_json); + +} // end namespace RDKit diff --git a/Code/GraphMol/SmilesParse/SmilesWrite.cpp b/Code/GraphMol/SmilesParse/SmilesWrite.cpp index cbce6a0e621..99c12b06bfc 100644 --- a/Code/GraphMol/SmilesParse/SmilesWrite.cpp +++ b/Code/GraphMol/SmilesParse/SmilesWrite.cpp @@ -33,69 +33,6 @@ namespace RDKit { -void updateSmilesWriteParamsFromJSON(SmilesWriteParams ¶ms, - const char *details_json) { - if (details_json && strlen(details_json)) { - boost::property_tree::ptree pt; - std::istringstream ss; - ss.str(details_json); - boost::property_tree::read_json(ss, pt); - params.doIsomericSmiles = - pt.get("doIsomericSmiles", params.doIsomericSmiles); - params.doKekule = pt.get("doKekule", params.doKekule); - params.rootedAtAtom = pt.get("rootedAtAtom", params.rootedAtAtom); - params.canonical = pt.get("canonical", params.canonical); - params.allBondsExplicit = - pt.get("allBondsExplicit", params.allBondsExplicit); - params.allHsExplicit = pt.get("allHsExplicit", params.allHsExplicit); - params.doRandom = pt.get("doRandom", params.doRandom); - } -} - -void updateSmilesWriteParamsFromJSON(SmilesWriteParams ¶ms, - const std::string &details_json) { - updateSmilesWriteParamsFromJSON(params, details_json.c_str()); -} - -void updateCXSmilesFieldsFromJSON(SmilesWrite::CXSmilesFields &cxSmilesFields, - RestoreBondDirOption &restoreBondDirs, - const char *details_json) { - static const auto cxSmilesFieldsKeyValuePairs = CXSMILESFIELDS_ITEMS_MAP; - static const auto restoreBondDirOptionKeyValuePairs = - RESTOREBONDDIROPTION_ITEMS_MAP; - if (details_json && strlen(details_json)) { - boost::property_tree::ptree pt; - std::istringstream ss; - ss.str(details_json); - boost::property_tree::read_json(ss, pt); - auto cxSmilesFieldsFromJson = - static_cast::type>( - SmilesWrite::CXSmilesFields::CX_NONE); - for (const auto &keyValuePair : cxSmilesFieldsKeyValuePairs) { - cxSmilesFieldsFromJson |= (pt.get(keyValuePair.first, false) - ? keyValuePair.second - : SmilesWrite::CXSmilesFields::CX_NONE); - } - if (cxSmilesFieldsFromJson) { - cxSmilesFields = - static_cast(cxSmilesFieldsFromJson); - } - std::string restoreBondDirOption; - restoreBondDirOption = pt.get("restoreBondDirOption", restoreBondDirOption); - auto it = restoreBondDirOptionKeyValuePairs.find(restoreBondDirOption); - if (it != restoreBondDirOptionKeyValuePairs.end()) { - restoreBondDirs = it->second; - } - } -} - -void updateCXSmilesFieldsFromJSON(SmilesWrite::CXSmilesFields &cxSmilesFields, - RestoreBondDirOption &restoreBondDirs, - const std::string &details_json) { - updateCXSmilesFieldsFromJSON(cxSmilesFields, restoreBondDirs, - details_json.c_str()); -} - namespace SmilesWrite { const int atomicSmiles[] = {0, 5, 6, 7, 8, 9, 15, 16, 17, 35, 53, -1}; bool inOrganicSubset(int atomicNumber) { diff --git a/Code/GraphMol/SmilesParse/SmilesWrite.h b/Code/GraphMol/SmilesParse/SmilesWrite.h index f1ed489bf29..cb0b60f489f 100644 --- a/Code/GraphMol/SmilesParse/SmilesWrite.h +++ b/Code/GraphMol/SmilesParse/SmilesWrite.h @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -50,32 +51,23 @@ struct RDKIT_SMILESPARSE_EXPORT SmilesWriteParams { namespace SmilesWrite { -#define CXSMILESFIELDS_ENUM_ITEMS \ - CXSMILESFIELDS_ENUM_ITEM(CX_NONE, 0) \ - CXSMILESFIELDS_ENUM_ITEM(CX_ATOM_LABELS, 1 << 0) \ - CXSMILESFIELDS_ENUM_ITEM(CX_MOLFILE_VALUES, 1 << 1) \ - CXSMILESFIELDS_ENUM_ITEM(CX_COORDS, 1 << 2) \ - CXSMILESFIELDS_ENUM_ITEM(CX_RADICALS, 1 << 3) \ - CXSMILESFIELDS_ENUM_ITEM(CX_ATOM_PROPS, 1 << 4) \ - CXSMILESFIELDS_ENUM_ITEM(CX_LINKNODES, 1 << 5) \ - CXSMILESFIELDS_ENUM_ITEM(CX_ENHANCEDSTEREO, 1 << 6) \ - CXSMILESFIELDS_ENUM_ITEM(CX_SGROUPS, 1 << 7) \ - CXSMILESFIELDS_ENUM_ITEM(CX_POLYMER, 1 << 8) \ - CXSMILESFIELDS_ENUM_ITEM(CX_BOND_CFG, 1 << 9) \ - CXSMILESFIELDS_ENUM_ITEM(CX_BOND_ATROPISOMER, 1 << 10) \ - CXSMILESFIELDS_ENUM_ITEM(CX_COORDINATE_BONDS, 1 << 11) \ - CXSMILESFIELDS_ENUM_ITEM(CX_ALL, 0x7fffffff) \ - CXSMILESFIELDS_ENUM_ITEM(CX_ALL_BUT_COORDS, CX_ALL ^ CX_COORDS) - -#define CXSMILESFIELDS_ENUM_ITEM(k, v) k = (v), -enum CXSmilesFields : uint32_t { CXSMILESFIELDS_ENUM_ITEMS }; -#undef CXSMILESFIELDS_ENUM_ITEM -#define CXSMILESFIELDS_STD_MAP_ITEM(k) {#k, SmilesWrite::CXSmilesFields::k}, -#define CXSMILESFIELDS_ENUM_ITEM(k, v) CXSMILESFIELDS_STD_MAP_ITEM(k) -#define CXSMILESFIELDS_ITEMS_MAP \ - std::map { \ - CXSMILESFIELDS_ENUM_ITEMS \ - } +BETTER_ENUM(CXSmilesFields, uint32_t, + CX_NONE = 0, + CX_ATOM_LABELS = 1 << 0, + CX_MOLFILE_VALUES = 1 << 1, + CX_COORDS = 1 << 2, + CX_RADICALS = 1 << 3, + CX_ATOM_PROPS = 1 << 4, + CX_LINKNODES = 1 << 5, + CX_ENHANCEDSTEREO = 1 << 6, + CX_SGROUPS = 1 << 7, + CX_POLYMER = 1 << 8, + CX_BOND_CFG = 1 << 9, + CX_BOND_ATROPISOMER = 1 << 10, + CX_COORDINATE_BONDS = 1 << 11, + CX_ALL = 0x7fffffff, + CX_ALL_BUT_COORDS = CX_ALL ^ CX_COORDS +); //! \brief returns the cxsmiles data for a molecule RDKIT_SMILESPARSE_EXPORT std::string getCXExtensions( @@ -268,28 +260,17 @@ inline std::string MolFragmentToSmiles( bondSymbols); } -#define RESTOREBONDDIROPTION_ENUM_ITEMS \ - RESTOREBONDDIROPTION_ENUM_ITEM(RestoreBondDirOptionTrue, \ - 0) /*!< DO restore bond dirs */ \ - RESTOREBONDDIROPTION_ENUM_ITEM(RestoreBondDirOptionClear, \ - 1) /*!< clear all bond dir information */ - -#define RESTOREBONDDIROPTION_ENUM_ITEM(k, v) k = v, -enum RestoreBondDirOption { RESTOREBONDDIROPTION_ENUM_ITEMS }; -#undef RESTOREBONDDIROPTION_ENUM_ITEM -#define RESTOREBONDDIROPTION_STD_MAP_ITEM(k) {#k, k}, -#define RESTOREBONDDIROPTION_ENUM_ITEM(k, v) \ - RESTOREBONDDIROPTION_STD_MAP_ITEM(k) -#define RESTOREBONDDIROPTION_ITEMS_MAP \ - std::map { \ - RESTOREBONDDIROPTION_ENUM_ITEMS \ - } +BETTER_ENUM(RestoreBondDirOption, unsigned int, + RestoreBondDirOptionTrue = 0, // +#include #include typedef std::vector STR_VECT; %} @@ -48,5 +49,7 @@ typedef std::vector STR_VECT; } } +%include %include +%include %include diff --git a/Code/JavaWrappers/SmilesWrite.i b/Code/JavaWrappers/SmilesWrite.i index 2e9cc9fc5fa..408b0320cef 100644 --- a/Code/JavaWrappers/SmilesWrite.i +++ b/Code/JavaWrappers/SmilesWrite.i @@ -33,6 +33,9 @@ %{ #include +#include %} +%include %include +%include diff --git a/Code/MinimalLib/cffiwrapper.cpp b/Code/MinimalLib/cffiwrapper.cpp index d4ff44eae22..d1762dd9f8b 100644 --- a/Code/MinimalLib/cffiwrapper.cpp +++ b/Code/MinimalLib/cffiwrapper.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -130,11 +131,11 @@ std::string cxsmiles_helper(const char *pkl, size_t pkl_sz, } auto params = smiles_helper(details_json); auto mol = mol_from_pkl(pkl, pkl_sz); - SmilesWrite::CXSmilesFields cxSmilesFields = - SmilesWrite::CXSmilesFields::CX_ALL; - RestoreBondDirOption restoreBondDirs = RestoreBondDirOptionClear; + std::uint32_t cxSmilesFields = SmilesWrite::CXSmilesFields::CX_ALL; + unsigned int restoreBondDirs = RestoreBondDirOptionClear; updateCXSmilesFieldsFromJSON(cxSmilesFields, restoreBondDirs, details_json); - return MolToCXSmiles(mol, params, cxSmilesFields, restoreBondDirs); + return MolToCXSmiles(mol, params, cxSmilesFields, + static_cast(restoreBondDirs)); } } // namespace extern "C" char *get_smiles(const char *pkl, size_t pkl_sz, diff --git a/Code/MinimalLib/minilib.cpp b/Code/MinimalLib/minilib.cpp index 2ceedda2290..177fafecc1b 100644 --- a/Code/MinimalLib/minilib.cpp +++ b/Code/MinimalLib/minilib.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,9 @@ #include #include #include +#ifdef RDK_BUILD_MINIMAL_LIB_RGROUPDECOMP +#include +#endif #ifdef RDK_BUILD_INCHI_SUPPORT #include @@ -95,11 +99,11 @@ std::string JSMolBase::get_cxsmiles() const { return MolToCXSmiles(get()); } std::string JSMolBase::get_cxsmiles(const std::string &details) const { SmilesWriteParams params; updateSmilesWriteParamsFromJSON(params, details); - SmilesWrite::CXSmilesFields cxSmilesFields = - SmilesWrite::CXSmilesFields::CX_ALL; - RestoreBondDirOption restoreBondDirs = RestoreBondDirOptionClear; + std::uint32_t cxSmilesFields = SmilesWrite::CXSmilesFields::CX_ALL; + unsigned int restoreBondDirs = RestoreBondDirOptionClear; updateCXSmilesFieldsFromJSON(cxSmilesFields, restoreBondDirs, details); - return MolToCXSmiles(get(), params, cxSmilesFields, restoreBondDirs); + return MolToCXSmiles(get(), params, cxSmilesFields, + static_cast(restoreBondDirs)); } std::string JSMolBase::get_smarts() const { return MolToSmarts(get()); } std::string JSMolBase::get_smarts(const std::string &details) const { diff --git a/Code/RDGeneral/BetterEnums.h b/Code/RDGeneral/BetterEnums.h new file mode 100644 index 00000000000..9d98579eebd --- /dev/null +++ b/Code/RDGeneral/BetterEnums.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2024 Novartis Biomedical Research and other RDKit contributors +// +// @@ All Rights Reserved @@ +// This file is part of the RDKit. +// The contents are covered by the terms of the BSD license +// which is included in the file license.txt, found at the root +// of the RDKit source tree. +// + +#pragma once + +#ifdef USE_BETTER_ENUMS +#include "enum.h" +#define BETTER_ENUM_CLASS BETTER_ENUM +#else +#define BETTER_ENUM(Enum, Underlying, ...) enum Enum : Underlying { __VA_ARGS__ } +#define BETTER_ENUM_CLASS(Enum, Underlying, ...) enum class Enum : Underlying { __VA_ARGS__ } +#endif diff --git a/Code/RDGeneral/CMakeLists.txt b/Code/RDGeneral/CMakeLists.txt index 332da9ae239..72fd72163e6 100644 --- a/Code/RDGeneral/CMakeLists.txt +++ b/Code/RDGeneral/CMakeLists.txt @@ -5,6 +5,10 @@ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/versions.h.cmake CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/RDConfig.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/RDConfig.h ) +FetchContent_MakeAvailable(better_enums) +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/enum.h) + file(COPY ${better_enums_SOURCE_DIR}/enum.h DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}) +endif(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/enum.h) rdkit_library(RDGeneral Invariant.cpp types.cpp utils.cpp RDGeneralExceptions.cpp RDLog.cpp @@ -45,6 +49,8 @@ rdkit_headers(Exceptions.h export.h test.h ConcurrentQueue.h + BetterEnums.h + enum.h DEST RDGeneral) if (NOT RDK_INSTALL_INTREE) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 60a9cf39f68..abf34d0fc5b 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -10,12 +10,20 @@ GitHub) ## Highlights ## Backwards incompatible changes +- SMILES and RGroupDecomp JSON parsers were moved to their own translation units. +This will require C++ code using those JSON parsers to be added #include directives +for GraphMol/SmilesParse/SmilesJSONParsers.h and +GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h, respectively. +- Replaced enums in the signatures of MolToCXSmiles and updateCXSmilesFieldsFromJSON +with the underlying types. This may require existing C++ code using those +functions to be updated accordingly. ## New Features and Enhancements: ## Bug Fixes: ## Cleanup work: +- Awful enum reflection macros were replaced with Better Enums ## Code removed in this release: - AtomPairs.Utils.NumPiElectrons was removed, please use Chem.GetNumPiElectrons instead.