diff --git a/Code/GraphMol/Atom.cpp b/Code/GraphMol/Atom.cpp index 447e6e2eb..90898f988 100644 --- a/Code/GraphMol/Atom.cpp +++ b/Code/GraphMol/Atom.cpp @@ -29,22 +29,22 @@ bool isEarlyAtom(int atomicNum) { return (4 - PeriodicTable::getTable()->getNouterElecs(atomicNum)) > 0; } } -Atom::Atom() { +Atom::Atom() : RDProps() { d_atomicNum = 0; initAtom(); } -Atom::Atom(unsigned int num) { +Atom::Atom(unsigned int num) : RDProps() { d_atomicNum = num; initAtom(); }; -Atom::Atom(const std::string &what) { +Atom::Atom(const std::string &what) : RDProps() { d_atomicNum = PeriodicTable::getTable()->getAtomicNumber(what); initAtom(); }; -Atom::Atom(const Atom &other) { +Atom::Atom(const Atom &other) : RDProps(other) { // NOTE: we do *not* copy ownership! d_atomicNum = other.d_atomicNum; dp_mol = 0; @@ -60,13 +60,6 @@ Atom::Atom(const Atom &other) { d_hybrid = other.d_hybrid; d_implicitValence = other.d_implicitValence; d_explicitValence = other.d_explicitValence; - if (other.dp_props) { - dp_props = new Dict(*other.dp_props); - } else { - dp_props = new Dict(); - STR_VECT computed; - dp_props->setVal(detail::computedPropName, computed); - } if (other.dp_monomerInfo) { dp_monomerInfo = other.dp_monomerInfo->copy(); } else { @@ -84,7 +77,6 @@ void Atom::initAtom() { d_chiralTag = CHI_UNSPECIFIED; d_hybrid = UNSPECIFIED; dp_mol = 0; - dp_props = new Dict(); dp_monomerInfo = 0; d_implicitValence = -1; @@ -92,10 +84,6 @@ void Atom::initAtom() { } Atom::~Atom() { - if (dp_props) { - delete dp_props; - dp_props = 0; - } if (dp_monomerInfo) { delete dp_monomerInfo; } diff --git a/Code/GraphMol/Atom.h b/Code/GraphMol/Atom.h index 7089fe98d..76448f6ff 100644 --- a/Code/GraphMol/Atom.h +++ b/Code/GraphMol/Atom.h @@ -22,7 +22,7 @@ // ours #include #include -#include +#include #include namespace RDKit { @@ -64,7 +64,7 @@ class AtomMonomerInfo; at the *end* of the list of other bonds. */ -class Atom { +class Atom : public RDProps { friend class MolPickler; //!< the pickler needs access to our privates friend class ROMol; friend class RWMol; @@ -304,139 +304,6 @@ class Atom { return Match(what.get()); }; - // ------------------------------------ - // Local Property Dict functionality - // all setProp functions are const because they - // are not meant to change the atom chemically - // ------------------------------------ - //! returns a list with the names of our \c properties - STR_VECT getPropList() const { return dp_props->keys(); } - - //! sets a \c property value - /*! - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param val the value to be stored - \param computed (optional) allows the \c property to be flagged - \c computed. - */ - template - void setProp(const char *key, T val, bool computed = false) const { - // if(!dp_props) dp_props = new Dict(); - std::string what(key); - setProp(what, val, computed); - } - - //! \overload - template - void setProp(const std::string &key, T val, bool computed = false) const { - if (computed) { - STR_VECT compLst; - getPropIfPresent(detail::computedPropName, compLst); - if (std::find(compLst.begin(), compLst.end(), key) == compLst.end()) { - compLst.push_back(key); - dp_props->setVal(detail::computedPropName, compLst); - } - } - // setProp(key.c_str(),val); - dp_props->setVal(key, val); - } - - //! allows retrieval of a particular property value - /*! - - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param res a reference to the storage location for the value. - - Notes: - - if no \c property with name \c key exists, a KeyErrorException will be - thrown. - - the \c boost::lexical_cast machinery is used to attempt type - conversions. - If this fails, a \c boost::bad_lexical_cast exception will be thrown. - - */ - template - void getProp(const char *key, T &res) const { - dp_props->getVal(key, res); - } - //! \overload - template - void getProp(const std::string &key, T &res) const { - dp_props->getVal(key, res); - } - - //! \overload - template - T getProp(const char *key) const { - return dp_props->getVal(key); - } - //! \overload - template - T getProp(const std::string &key) const { - return dp_props->getVal(key); - } - - //! returns whether or not we have a \c property with name \c key - //! and assigns the value if we do - template - bool getPropIfPresent(const char *key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - //! \overload - template - bool getPropIfPresent(const std::string &key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - - //! returns whether or not we have a \c property with name \c key - bool hasProp(const char *key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - }; - //! \overload - bool hasProp(const std::string &key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - }; - - //! clears the value of a \c property - /*! - Notes: - - if no \c property with name \c key exists, a KeyErrorException - will be thrown. - - if the \c property is marked as \c computed, it will also be removed - from our list of \c computedProperties - */ - void clearProp(const char *key) const { - std::string what(key); - clearProp(what); - }; - //! \overload - void clearProp(const std::string &key) const { - STR_VECT compLst; - if (getPropIfPresent(detail::computedPropName, compLst)) { - STR_VECT_I svi = std::find(compLst.begin(), compLst.end(), key); - if (svi != compLst.end()) { - compLst.erase(svi); - dp_props->setVal(detail::computedPropName, compLst); - } - } - dp_props->clearVal(key); - }; - - //! clears all of our \c computed \c properties - void clearComputedProps() const { - STR_VECT compLst; - if (getPropIfPresent(detail::computedPropName, compLst)) { - BOOST_FOREACH (const std::string &sv, compLst) { dp_props->clearVal(sv); } - compLst.clear(); - dp_props->setVal(detail::computedPropName, compLst); - } - } //! returns the perturbation order for a list of integers /*! @@ -516,7 +383,6 @@ class Atom { boost::uint16_t d_isotope; ROMol *dp_mol; - Dict *dp_props; AtomMonomerInfo *dp_monomerInfo; void initAtom(); }; diff --git a/Code/GraphMol/Bond.cpp b/Code/GraphMol/Bond.cpp index 5c83440e7..4f5c8d3bc 100644 --- a/Code/GraphMol/Bond.cpp +++ b/Code/GraphMol/Bond.cpp @@ -15,18 +15,16 @@ namespace RDKit { -Bond::Bond() { +Bond::Bond() : RDProps() { initBond(); - dp_props = new Dict(); }; -Bond::Bond(BondType bT) { +Bond::Bond(BondType bT) : RDProps() { initBond(); d_bondType = bT; - dp_props = new Dict(); }; -Bond::Bond(const Bond &other) { +Bond::Bond(const Bond &other) : RDProps(other) { // NOTE: we do *not* copy ownership! dp_mol = 0; d_bondType = other.d_bondType; @@ -42,15 +40,9 @@ Bond::Bond(const Bond &other) { df_isAromatic = other.df_isAromatic; df_isConjugated = other.df_isConjugated; d_index = other.d_index; - if (other.dp_props) { - dp_props = new Dict(*other.dp_props); - } else { - dp_props = new Dict(); - } } Bond::~Bond() { - delete dp_props; delete dp_stereoAtoms; } @@ -68,11 +60,7 @@ Bond &Bond::operator=(const Bond &other) { df_isAromatic = other.df_isAromatic; df_isConjugated = other.df_isConjugated; d_index = other.d_index; - if (other.dp_props) { - dp_props = new Dict(*other.dp_props); - } else { - dp_props = new Dict(); - } + dp_props = other.dp_props; return *this; } diff --git a/Code/GraphMol/Bond.h b/Code/GraphMol/Bond.h index 3b7e77299..aa7575e78 100644 --- a/Code/GraphMol/Bond.h +++ b/Code/GraphMol/Bond.h @@ -17,6 +17,7 @@ // FIX: grn... #include #include +#include #include #include @@ -43,7 +44,7 @@ typedef boost::shared_ptr ATOM_SPTR; clients who need to store extra data on Bond objects. */ -class Bond { +class Bond : public RDProps{ friend class RWMol; friend class ROMol; @@ -288,141 +289,6 @@ class Bond { return *dp_stereoAtoms; }; - // ------------------------------------ - // Local Property Dict functionality - // FIX: at some point this stuff should go in a mixin class - // ------------------------------------ - //! returns a list with the names of our \c properties - STR_VECT getPropList() const { return dp_props->keys(); } - - //! sets a \c property value - /*! - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param val the value to be stored - \param computed (optional) allows the \c property to be flagged - \c computed. - */ - template - void setProp(const char *key, T val, bool computed = false) const { - // if(!dp_props) dp_props = new Dict(); - std::string what(key); - setProp(what, val, computed); - } - //! \overload - template - void setProp(const std::string &key, T val, bool computed = false) const { - // setProp(key.c_str(),val); - if (computed) { - STR_VECT compLst; - getPropIfPresent(detail::computedPropName, compLst); - if (std::find(compLst.begin(), compLst.end(), key) == compLst.end()) { - compLst.push_back(key); - dp_props->setVal(detail::computedPropName, compLst); - } - } - dp_props->setVal(key, val); - } - - //! allows retrieval of a particular property value - /*! - - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param res a reference to the storage location for the value. - - Notes: - - if no \c property with name \c key exists, a KeyErrorException will be - thrown. - - the \c boost::lexical_cast machinery is used to attempt type - conversions. - If this fails, a \c boost::bad_lexical_cast exception will be thrown. - - */ - template - void getProp(const char *key, T &res) const { - PRECONDITION(dp_props, "getProp called on empty property dict"); - dp_props->getVal(key, res); - } - //! \overload - template - void getProp(const std::string &key, T &res) const { - PRECONDITION(dp_props, "getProp called on empty property dict"); - dp_props->getVal(key, res); - } - - //! \Overload - template - T getProp(const char *key) const { - return dp_props->getVal(key); - } - //! \overload - template - T getProp(const std::string &key) const { - return dp_props->getVal(key); - } - - //! returns whether or not we have a \c property with name \c key - //! and assigns the value if we do - - template - bool getPropIfPresent(const char *key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - //! \overload - template - bool getPropIfPresent(const std::string &key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - - //! returns whether or not we have a \c property with name \c key - bool hasProp(const char *key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - }; - //! \overload - bool hasProp(const std::string &key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - }; - - //! clears the value of a \c property - /*! - Notes: - - if no \c property with name \c key exists, a KeyErrorException - will be thrown. - - if the \c property is marked as \c computed, it will also be removed - from our list of \c computedProperties - */ - void clearProp(const char *key) const { - std::string what(key); - clearProp(what); - }; - //! \overload - void clearProp(const std::string &key) const { - STR_VECT compLst; - if (getPropIfPresent(detail::computedPropName, compLst)) { - STR_VECT_I svi = std::find(compLst.begin(), compLst.end(), key); - if (svi != compLst.end()) { - compLst.erase(svi); - dp_props->setVal(detail::computedPropName, compLst); - } - } - dp_props->clearVal(key); - } - - //! clears all of our \c computed \c properties - void clearComputedProps() const { - STR_VECT compLst; - if (getPropIfPresent(detail::computedPropName, compLst)) { - BOOST_FOREACH (const std::string &sv, compLst) { dp_props->clearVal(sv); } - compLst.clear(); - dp_props->setVal(detail::computedPropName, compLst); - } - } - //! calculates any of our lazy \c properties /*! Notes: @@ -443,7 +309,6 @@ class Bond { atomindex_t d_index; atomindex_t d_beginAtomIdx, d_endAtomIdx; ROMol *dp_mol; - Dict *dp_props; INT_VECT *dp_stereoAtoms; void initBond(); diff --git a/Code/GraphMol/Canon.cpp b/Code/GraphMol/Canon.cpp index 041df5f5b..a2a3bd207 100644 --- a/Code/GraphMol/Canon.cpp +++ b/Code/GraphMol/Canon.cpp @@ -923,7 +923,8 @@ void canonicalizeFragment(ROMol &mol, int atomIdx, if (!mol.getRingInfo()->isInitialized()) { MolOps::findSSSR(mol); } - mol.getAtomWithIdx(atomIdx)->setProp("_TraversalStartPoint", true); + mol.getAtomWithIdx(atomIdx)->setProp( + common_properties::_TraversalStartPoint, true); VECT_INT_VECT atomRingClosures(nAtoms); std::vector atomTraversalBondOrder(nAtoms); diff --git a/Code/GraphMol/ForceFieldHelpers/MMFF/AtomTyper.cpp b/Code/GraphMol/ForceFieldHelpers/MMFF/AtomTyper.cpp index 729dde38c..e2659436f 100644 --- a/Code/GraphMol/ForceFieldHelpers/MMFF/AtomTyper.cpp +++ b/Code/GraphMol/ForceFieldHelpers/MMFF/AtomTyper.cpp @@ -3651,14 +3651,14 @@ bool MMFFMolProperties::getMMFFVdWParams(const unsigned int idx1, const MMFFVdW *mmffVdWParamsIAtom = (*mmffVdW)(iAtomType); const MMFFVdW *mmffVdWParamsJAtom = (*mmffVdW)(jAtomType); if (mmffVdWParamsIAtom && mmffVdWParamsJAtom) { - mmffVdWParams.R_ij_starUnscaled = Utils::calcUnscaledVdWMinimum( + mmffVdWParams.R_ij_starUnscaled = MMFF::Utils::calcUnscaledVdWMinimum( mmffVdW, mmffVdWParamsIAtom, mmffVdWParamsJAtom); - mmffVdWParams.epsilonUnscaled = Utils::calcUnscaledVdWWellDepth( + mmffVdWParams.epsilonUnscaled = MMFF::Utils::calcUnscaledVdWWellDepth( mmffVdWParams.R_ij_starUnscaled, mmffVdWParamsIAtom, mmffVdWParamsJAtom); mmffVdWParams.R_ij_star = mmffVdWParams.R_ij_starUnscaled; mmffVdWParams.epsilon = mmffVdWParams.epsilonUnscaled; - Utils::scaleVdWParams(mmffVdWParams.R_ij_star, mmffVdWParams.epsilon, + MMFF::Utils::scaleVdWParams(mmffVdWParams.R_ij_star, mmffVdWParams.epsilon, mmffVdW, mmffVdWParamsIAtom, mmffVdWParamsJAtom); res = true; } diff --git a/Code/GraphMol/ForceFieldHelpers/MMFF/Builder.cpp b/Code/GraphMol/ForceFieldHelpers/MMFF/Builder.cpp index 3800b4b9f..87ba782b5 100644 --- a/Code/GraphMol/ForceFieldHelpers/MMFF/Builder.cpp +++ b/Code/GraphMol/ForceFieldHelpers/MMFF/Builder.cpp @@ -73,7 +73,7 @@ void addBonds(const ROMol &mol, MMFFMolProperties *mmffMolProperties, const Atom *iAtom = (*bi)->getBeginAtom(); const Atom *jAtom = (*bi)->getEndAtom(); const double dist = field->distance(idx1, idx2); - const double bondStretchEnergy = Utils::calcBondStretchEnergy( + const double bondStretchEnergy = MMFF::Utils::calcBondStretchEnergy( mmffBondParams.r0, mmffBondParams.kb, dist); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << iAtom->getSymbol() << " #" @@ -303,10 +303,10 @@ void addAngles(const ROMol &mol, MMFFMolProperties *mmffMolProperties, (*(points[idx[2]]))[1], (*(points[idx[2]]))[2]); const double cosTheta = - Utils::calcCosTheta(p1, p2, p3, field->distance(idx[0], idx[1]), - field->distance(idx[1], idx[2])); + MMFF::Utils::calcCosTheta(p1, p2, p3, field->distance(idx[0], idx[1]), + field->distance(idx[1], idx[2])); const double theta = RAD2DEG * acos(cosTheta); - const double angleBendEnergy = Utils::calcAngleBendEnergy( + const double angleBendEnergy = MMFF::Utils::calcAngleBendEnergy( mmffAngleParams.theta0, mmffAngleParams.ka, mmffPropParamsCentralAtom->linh, cosTheta); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { @@ -431,12 +431,12 @@ void addStretchBend(const ROMol &mol, MMFFMolProperties *mmffMolProperties, (*(points[idx[2]]))[1], (*(points[idx[2]]))[2]); const double cosTheta = - Utils::calcCosTheta(p1, p2, p3, dist1, dist2); + MMFF::Utils::calcCosTheta(p1, p2, p3, dist1, dist2); const double theta = RAD2DEG * acos(cosTheta); const std::pair forceConstants = - Utils::calcStbnForceConstants(&mmffStbnParams); + MMFF::Utils::calcStbnForceConstants(&mmffStbnParams); const std::pair stretchBendEnergies = - Utils::calcStretchBendEnergy( + MMFF::Utils::calcStretchBendEnergy( dist1 - mmffBondParams[0].r0, dist2 - mmffBondParams[1].r0, theta - mmffAngleParams.theta0, forceConstants); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { @@ -584,9 +584,9 @@ void addOop(const ROMol &mol, MMFFMolProperties *mmffMolProperties, const RDGeom::Point3D p4((*(points[idx[n[3]]]))[0], (*(points[idx[n[3]]]))[1], (*(points[idx[n[3]]]))[2]); - const double chi = Utils::calcOopChi(p1, p2, p3, p4); + const double chi = MMFF::Utils::calcOopChi(p1, p2, p3, p4); const double oopBendEnergy = - Utils::calcOopBendEnergy(chi, mmffOopParams.koop); + MMFF::Utils::calcOopBendEnergy(chi, mmffOopParams.koop); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { oStream << std::left << std::setw(2) << atom[0]->getSymbol() << " #" << std::setw(5) << idx[n[0]] + 1 << std::setw(2) @@ -716,8 +716,8 @@ void addTorsions(const ROMol &mol, MMFFMolProperties *mmffMolProperties, (*(points[idx4]))[1], (*(points[idx4]))[2]); const double cosPhi = - Utils::calcTorsionCosPhi(p1, p2, p3, p4); - const double torsionEnergy = Utils::calcTorsionEnergy( + MMFF::Utils::calcTorsionCosPhi(p1, p2, p3, p4); + const double torsionEnergy = MMFF::Utils::calcTorsionEnergy( mmffTorParams.V1, mmffTorParams.V2, mmffTorParams.V3, cosPhi); if (mmffMolProperties->getMMFFVerbosity() == @@ -814,7 +814,7 @@ void addVdW(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, if (mmffMolProperties->getMMFFVerbosity()) { const Atom *iAtom = mol.getAtomWithIdx(i); const Atom *jAtom = mol.getAtomWithIdx(j); - const double vdWEnergy = Utils::calcVdWEnergy( + const double vdWEnergy = MMFF::Utils::calcVdWEnergy( dist, mmffVdWParams.R_ij_star, mmffVdWParams.epsilon); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { unsigned int iAtomType = mmffMolProperties->getMMFFAtomType(i); @@ -904,7 +904,7 @@ void addEle(const ROMol &mol, int confId, MMFFMolProperties *mmffMolProperties, const unsigned int jAtomType = mmffMolProperties->getMMFFAtomType(j); const Atom *iAtom = mol.getAtomWithIdx(i); const Atom *jAtom = mol.getAtomWithIdx(j); - const double eleEnergy = Utils::calcEleEnergy( + const double eleEnergy = MMFF::Utils::calcEleEnergy( i, j, dist, chargeTerm, dielModel, getTwoBitCell(neighborMatrix, i * nAtoms + j) == RELATION_1_4); if (mmffMolProperties->getMMFFVerbosity() == MMFF_VERBOSITY_HIGH) { diff --git a/Code/GraphMol/ForceFieldHelpers/UFF/AtomTyper.cpp b/Code/GraphMol/ForceFieldHelpers/UFF/AtomTyper.cpp index 8fcf7b122..cbfd91645 100644 --- a/Code/GraphMol/ForceFieldHelpers/UFF/AtomTyper.cpp +++ b/Code/GraphMol/ForceFieldHelpers/UFF/AtomTyper.cpp @@ -400,8 +400,8 @@ bool getUFFBondStretchParams(const ROMol &mol, unsigned int idx1, if (res) { double bondOrder = bond->getBondTypeAsDouble(); uffBondStretchParams.r0 = - Utils::calcBondRestLength(bondOrder, paramVect[0], paramVect[1]); - uffBondStretchParams.kb = Utils::calcBondForceConstant( + UFF::Utils::calcBondRestLength(bondOrder, paramVect[0], paramVect[1]); + uffBondStretchParams.kb = UFF::Utils::calcBondForceConstant( uffBondStretchParams.r0, paramVect[0], paramVect[1]); } return res; @@ -432,7 +432,7 @@ bool getUFFAngleBendParams(const ROMol &mol, unsigned int idx1, double bondOrder12 = bond[0]->getBondTypeAsDouble(); double bondOrder23 = bond[1]->getBondTypeAsDouble(); uffAngleBendParams.theta0 = RAD2DEG * paramVect[1]->theta0; - uffAngleBendParams.ka = Utils::calcAngleForceConstant( + uffAngleBendParams.ka = UFF::Utils::calcAngleForceConstant( paramVect[1]->theta0, bondOrder12, bondOrder23, paramVect[0], paramVect[1], paramVect[2]); } @@ -477,8 +477,8 @@ bool getUFFTorsionParams(const ROMol &mol, unsigned int idx1, unsigned int idx2, // general case: uffTorsionParams.V = sqrt(paramVect[0]->V1 * paramVect[1]->V1); // special case for single bonds between group 6 elements: - if (((int)(bondOrder * 10) == 10) && Utils::isInGroup6(atNum[0]) && - Utils::isInGroup6(atNum[1])) { + if (((int)(bondOrder * 10) == 10) && UFF::Utils::isInGroup6(atNum[0]) && + UFF::Utils::isInGroup6(atNum[1])) { double V2 = 6.8; double V3 = 6.8; if (atNum[0] == 8) V2 = 2.0; @@ -487,18 +487,18 @@ bool getUFFTorsionParams(const ROMol &mol, unsigned int idx1, unsigned int idx2, } } else if ((hyb[0] == RDKit::Atom::SP2) && (hyb[1] == RDKit::Atom::SP2)) { uffTorsionParams.V = - Utils::equation17(bondOrder, paramVect[0], paramVect[1]); + UFF::Utils::equation17(bondOrder, paramVect[0], paramVect[1]); } else { // SP2 - SP3, this is, by default, independent of atom type in UFF: uffTorsionParams.V = 1.0; if ((int)(bondOrder * 10) == 10) { // special case between group 6 sp3 and non-group 6 sp2: - if (((hyb[0] == RDKit::Atom::SP3) && Utils::isInGroup6(atNum[0]) && - (!Utils::isInGroup6(atNum[1]))) || - ((hyb[1] == RDKit::Atom::SP3) && Utils::isInGroup6(atNum[1]) && - (!Utils::isInGroup6(atNum[0])))) { + if (((hyb[0] == RDKit::Atom::SP3) && UFF::Utils::isInGroup6(atNum[0]) && + (!UFF::Utils::isInGroup6(atNum[1]))) || + ((hyb[1] == RDKit::Atom::SP3) && UFF::Utils::isInGroup6(atNum[1]) && + (!UFF::Utils::isInGroup6(atNum[0])))) { uffTorsionParams.V = - Utils::equation17(bondOrder, paramVect[0], paramVect[1]); + UFF::Utils::equation17(bondOrder, paramVect[0], paramVect[1]); } // special case for sp3 - sp2 - sp2 // (i.e. the sp2 has another sp2 neighbor, like propene) @@ -550,7 +550,7 @@ bool getUFFInversionParams(const ROMol &mol, unsigned int idx1, if (res) { isBoundToSP2O = (isBoundToSP2O && (at2AtomicNum == 6)); boost::tuple invCoeffForceCon = - Utils::calcInversionCoefficientsAndForceConstant(at2AtomicNum, + UFF::Utils::calcInversionCoefficientsAndForceConstant(at2AtomicNum, isBoundToSP2O); uffInversionParams.K = boost::tuples::get<0>(invCoeffForceCon); } @@ -571,8 +571,8 @@ bool getUFFVdWParams(const ROMol &mol, unsigned int idx1, unsigned int idx2, res = (paramVect[i] ? true : false); } if (res) { - uffVdWParams.x_ij = Utils::calcNonbondedMinimum(paramVect[0], paramVect[1]); - uffVdWParams.D_ij = Utils::calcNonbondedDepth(paramVect[0], paramVect[1]); + uffVdWParams.x_ij = UFF::Utils::calcNonbondedMinimum(paramVect[0], paramVect[1]); + uffVdWParams.D_ij = UFF::Utils::calcNonbondedDepth(paramVect[0], paramVect[1]); } return res; } diff --git a/Code/GraphMol/ForceFieldHelpers/UFF/Builder.cpp b/Code/GraphMol/ForceFieldHelpers/UFF/Builder.cpp index 7e66d59cc..6137e7b85 100644 --- a/Code/GraphMol/ForceFieldHelpers/UFF/Builder.cpp +++ b/Code/GraphMol/ForceFieldHelpers/UFF/Builder.cpp @@ -450,7 +450,7 @@ void addNonbonded(const ROMol &mol, int confId, const AtomicParamVect ¶ms, if (getTwoBitCell(neighborMatrix, i * nAtoms + j) >= RELATION_1_4) { double dist = (conf.getAtomPos(i) - conf.getAtomPos(j)).length(); if (dist < - vdwThresh * Utils::calcNonbondedMinimum(params[i], params[j])) { + vdwThresh * UFF::Utils::calcNonbondedMinimum(params[i], params[j])) { vdWContrib *contrib; contrib = new vdWContrib(field, i, j, params[i], params[j]); field->contribs().push_back(ForceFields::ContribPtr(contrib)); diff --git a/Code/GraphMol/MolDiscriminators.cpp b/Code/GraphMol/MolDiscriminators.cpp index bec1e1025..d311ed399 100644 --- a/Code/GraphMol/MolDiscriminators.cpp +++ b/Code/GraphMol/MolDiscriminators.cpp @@ -57,7 +57,7 @@ double computeBalabanJ(const ROMol &mol, bool useBO, bool force, const std::vector *bondPath, bool cacheIt) { RDUNUSED_PARAM(useBO); double res = 0.0; - if (!force && mol.hasProp(common_properties::BalanbanJ)) { + if (!force && mol.hasProp(common_properties::BalabanJ)) { mol.getProp(common_properties::BalabanJ, res); } else { double *dMat; diff --git a/Code/GraphMol/QueryBond.cpp b/Code/GraphMol/QueryBond.cpp index d1d1feaa6..1b989c7aa 100644 --- a/Code/GraphMol/QueryBond.cpp +++ b/Code/GraphMol/QueryBond.cpp @@ -30,8 +30,7 @@ QueryBond &QueryBond::operator=(const QueryBond &other) { dp_mol = 0; d_bondType = other.d_bondType; dp_query = other.dp_query->copy(); - delete dp_props; - if (other.dp_props) dp_props = new Dict(*other.dp_props); + dp_props = other.dp_props; return *this; } diff --git a/Code/GraphMol/ROMol.cpp b/Code/GraphMol/ROMol.cpp index ad232efd8..92bc0737b 100644 --- a/Code/GraphMol/ROMol.cpp +++ b/Code/GraphMol/ROMol.cpp @@ -34,17 +34,14 @@ void ROMol::destroy() { d_atomBookmarks.clear(); d_bondBookmarks.clear(); d_graph.clear(); - if (dp_props) { - delete dp_props; - dp_props = 0; - } + if (dp_ringInfo) { delete dp_ringInfo; dp_ringInfo = 0; } }; -ROMol::ROMol(const std::string &pickle) { +ROMol::ROMol(const std::string &pickle) : RDProps() { initMol(); MolPickler::molFromPickle(pickle, *this); } @@ -75,8 +72,6 @@ void ROMol::initFromOther(const ROMol &other, bool quickCopy, int confId) { dp_ringInfo = new RingInfo(); } - if (dp_props) delete dp_props; - dp_props = 0; if (!quickCopy) { // copy conformations for (ConstConformerIterator ci = other.beginConformers(); @@ -86,10 +81,8 @@ void ROMol::initFromOther(const ROMol &other, bool quickCopy, int confId) { this->addConformer(conf); } } - - if (other.dp_props) { - dp_props = new Dict(*other.dp_props); - } + + dp_props = other.dp_props; // Bookmarks should be copied as well: BOOST_FOREACH (ATOM_BOOKMARK_MAP::value_type abmI, other.d_atomBookmarks) { @@ -102,18 +95,17 @@ void ROMol::initFromOther(const ROMol &other, bool quickCopy, int confId) { setBondBookmark(getBondWithIdx(bptr->getIdx()), bbmI.first); } } - } - if (!dp_props) { - dp_props = new Dict(); + } else { + dp_props.reset(); STR_VECT computed; - dp_props->setVal(detail::computedPropName, computed); + dp_props.setVal(detail::computedPropName, computed); } // std::cerr<<"--------- done init from other: "<setVal(detail::computedPropName, computed); + dp_props.setVal(detail::computedPropName, computed); } unsigned int ROMol::getAtomDegree(const Atom *at) const { @@ -477,12 +469,8 @@ void ROMol::clearComputedProps(bool includeRings) const { // the SSSR information: if (includeRings) this->dp_ringInfo->reset(); - STR_VECT compLst; - if (getPropIfPresent(detail::computedPropName, compLst)) { - BOOST_FOREACH (std::string &sv, compLst) { dp_props->clearVal(sv); } - compLst.clear(); - } - dp_props->setVal(detail::computedPropName, compLst); + RDProps::clearComputedProps(); + for (ConstAtomIterator atomIt = this->beginAtoms(); atomIt != this->endAtoms(); ++atomIt) { (*atomIt)->clearComputedProps(); diff --git a/Code/GraphMol/ROMol.h b/Code/GraphMol/ROMol.h index c1e49792b..80c7b72fa 100644 --- a/Code/GraphMol/ROMol.h +++ b/Code/GraphMol/ROMol.h @@ -29,6 +29,7 @@ // our stuff #include +#include #include "Atom.h" #include "Bond.h" @@ -99,7 +100,7 @@ extern const int ci_ATOM_HOLDER; */ -class ROMol { +class ROMol : public RDProps { public: friend class MolPickler; friend class RWMol; @@ -174,7 +175,7 @@ class ROMol { //@} //! \endcond - ROMol() { initMol(); } + ROMol() : RDProps() { initMol(); } //! copy constructor with a twist /*! @@ -187,8 +188,7 @@ class ROMol { only the specified conformer from \c other. */ - ROMol(const ROMol &other, bool quickCopy = false, int confId = -1) { - dp_props = 0; + ROMol(const ROMol &other, bool quickCopy = false, int confId = -1) : RDProps() { dp_ringInfo = 0; initFromOther(other, quickCopy, confId); }; @@ -553,140 +553,6 @@ class ROMol { //! \name Properties //@{ - //! returns a list with the names of our \c properties - STR_VECT getPropList(bool includePrivate = true, - bool includeComputed = true) const { - const STR_VECT &tmp = dp_props->keys(); - STR_VECT res, computed; - if (!includeComputed && - getPropIfPresent(detail::computedPropName, computed)) { - computed.push_back(detail::computedPropName); - } - - STR_VECT::const_iterator pos = tmp.begin(); - while (pos != tmp.end()) { - if ((includePrivate || (*pos)[0] != '_') && - std::find(computed.begin(), computed.end(), *pos) == computed.end()) { - res.push_back(*pos); - } - pos++; - } - return res; - } - - //! sets a \c property value - /*! - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param val the value to be stored - \param computed (optional) allows the \c property to be flagged - \c computed. - */ - template - void setProp(const char *key, T val, bool computed = false) const { - std::string what(key); - setProp(what, val, computed); - } - //! \overload - template - void setProp(const std::string &key, T val, bool computed = false) const { - if (computed) { - STR_VECT compLst; - dp_props->getVal(detail::computedPropName, compLst); - if (std::find(compLst.begin(), compLst.end(), key) == compLst.end()) { - compLst.push_back(key); - dp_props->setVal(detail::computedPropName, compLst); - } - } - dp_props->setVal(key, val); - } - - //! allows retrieval of a particular property value - /*! - - \param key the name under which the \c property should be stored. - If a \c property is already stored under this name, it will be - replaced. - \param res a reference to the storage location for the value. - - Notes: - - if no \c property with name \c key exists, a KeyErrorException will be - thrown. - - the \c boost::lexical_cast machinery is used to attempt type - conversions. - If this fails, a \c boost::bad_lexical_cast exception will be thrown. - - */ - template - void getProp(const char *key, T &res) const { - dp_props->getVal(key, res); - } - //! \overload - template - void getProp(const std::string &key, T &res) const { - dp_props->getVal(key, res); - } - //! \overload - template - T getProp(const char *key) const { - return dp_props->getVal(key); - } - //! \overload - template - T getProp(const std::string &key) const { - return dp_props->getVal(key); - } - - //! returns whether or not we have a \c property with name \c key - //! and assigns the value if we do - template - bool getPropIfPresent(const char *key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - //! \overload - template - bool getPropIfPresent(const std::string &key, T &res) const { - return dp_props->getValIfPresent(key, res); - } - - //! returns whether or not we have a \c property with name \c key - bool hasProp(const char *key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - } - //! \overload - bool hasProp(const std::string &key) const { - if (!dp_props) return false; - return dp_props->hasVal(key); - // return hasProp(key.c_str()); - } - - //! clears the value of a \c property - /*! - Notes: - - if no \c property with name \c key exists, a KeyErrorException - will be thrown. - - if the \c property is marked as \c computed, it will also be removed - from our list of \c computedProperties - */ - void clearProp(const char *key) const { - std::string what(key); - clearProp(what); - }; - //! \overload - void clearProp(const std::string &key) const { - STR_VECT compLst; - getProp(detail::computedPropName, compLst); - STR_VECT_I svi = std::find(compLst.begin(), compLst.end(), key); - if (svi != compLst.end()) { - compLst.erase(svi); - dp_props->setVal(detail::computedPropName, compLst); - } - - dp_props->clearVal(key); - }; - //! clears all of our \c computed \c properties void clearComputedProps(bool includeRings = true) const; //! calculates any of our lazy \c properties @@ -720,7 +586,6 @@ class ROMol { MolGraph d_graph; ATOM_BOOKMARK_MAP d_atomBookmarks; BOND_BOOKMARK_MAP d_bondBookmarks; - Dict *dp_props; RingInfo *dp_ringInfo; CONF_SPTR_LIST d_confs; ROMol &operator=( diff --git a/Code/GraphMol/RWMol.h b/Code/GraphMol/RWMol.h index ee45b58c5..56e0f5dcd 100644 --- a/Code/GraphMol/RWMol.h +++ b/Code/GraphMol/RWMol.h @@ -214,11 +214,10 @@ class RWMol : public ROMol { d_bondBookmarks.clear(); d_graph.clear(); d_confs.clear(); - if (dp_props) { - dp_props->reset(); - STR_VECT computed; - dp_props->setVal(detail::computedPropName, computed); - } + dp_props.reset(); + STR_VECT computed; + dp_props.setVal(detail::computedPropName, computed); + if (dp_ringInfo) dp_ringInfo->reset(); }; diff --git a/Code/GraphMol/Wrap/Atom.cpp b/Code/GraphMol/Wrap/Atom.cpp index 176ae11f0..986d2b35a 100644 --- a/Code/GraphMol/Wrap/Atom.cpp +++ b/Code/GraphMol/Wrap/Atom.cpp @@ -360,7 +360,10 @@ struct atom_wrapper { .def("GetPropNames", &Atom::getPropList, (python::arg("self")), "Returns a list of the properties set on the Atom.\n\n") - .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self")), + .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self"), + python::arg("includePrivate") = true, + python::arg("includeComputed") = true + ), "Returns a dictionary of the properties set on the Atom.\n" " n.b. some properties cannot be converted to python types.\n") diff --git a/Code/GraphMol/Wrap/Bond.cpp b/Code/GraphMol/Wrap/Bond.cpp index b732ef3d5..bc3638a32 100644 --- a/Code/GraphMol/Wrap/Bond.cpp +++ b/Code/GraphMol/Wrap/Bond.cpp @@ -293,7 +293,10 @@ struct bond_wrapper { .def("GetPropNames", &Bond::getPropList, (python::arg("self")), "Returns a list of the properties set on the Bond.\n\n") - .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self")), + .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self"), + python::arg("includePrivate") = true, + python::arg("includeComputed") = true + ), "Returns a dictionary of the properties set on the Bond.\n" " n.b. some properties cannot be converted to python types.\n") diff --git a/Code/GraphMol/Wrap/Mol.cpp b/Code/GraphMol/Wrap/Mol.cpp index 08ca7ef19..0dc6ec66d 100644 --- a/Code/GraphMol/Wrap/Mol.cpp +++ b/Code/GraphMol/Wrap/Mol.cpp @@ -158,29 +158,6 @@ void MolClearProp(const ROMol &mol, const char *key) { mol.clearProp(key); } -// specialized for include private/computed... -boost::python::dict MolGetPropsAsDict(const ROMol &mol, - bool includePrivate=false, - bool includeComputed=false) { - boost::python::dict dict; - // precedence double, int, unsigned, std::vector, - // std::vector, std::vector, string - STR_VECT keys = mol.getPropList(includePrivate, includeComputed); - for(size_t i=0;i(mol, dict, keys[i])) continue; - if (AddToDict(mol, dict, keys[i])) continue; - if (AddToDict(mol, dict, keys[i])) continue; - if (AddToDict(mol, dict, keys[i])) continue; - if (AddToDict >(mol, dict, keys[i])) continue; - if (AddToDict >(mol, dict, keys[i])) continue; - if (AddToDict >(mol, dict, keys[i])) continue; - // if (AddToDict >(mol, dict, keys[i])) continue; - if (AddToDict(mol, dict, keys[i])) continue; - if (AddToDict >(mol, dict, keys[i])) continue; - } - return dict; -} - void MolClearComputedProps(const ROMol &mol) { mol.clearComputedProps(); } void MolDebug(const ROMol &mol) { mol.debugMol(std::cout); } @@ -566,7 +543,7 @@ struct mol_wrapper { " Defaults to 0.\n\n" " RETURNS: a tuple of strings\n") - .def("GetPropsAsDict", MolGetPropsAsDict, + .def("GetPropsAsDict", GetPropsAsDict, (python::arg("self"), python::arg("includePrivate") = false, python::arg("includeComputed") = false), "Returns a dictionary populated with the molecules properties.\n" diff --git a/Code/GraphMol/Wrap/props.hpp b/Code/GraphMol/Wrap/props.hpp index a18acc695..ed82a6ef7 100644 --- a/Code/GraphMol/Wrap/props.hpp +++ b/Code/GraphMol/Wrap/props.hpp @@ -69,11 +69,13 @@ bool AddToDict(const U& ob, boost::python::dict &dict, const std::string &key) { } template -boost::python::dict GetPropsAsDict(const T &obj) { +boost::python::dict GetPropsAsDict(const T &obj, + bool includePrivate, + bool includeComputed) { boost::python::dict dict; // precedence double, int, unsigned, std::vector, // std::vector, std::vector, string - STR_VECT keys = obj.getPropList(); + STR_VECT keys = obj.getPropList(includePrivate, includeComputed); for(size_t i=0;i(obj, dict, keys[i])) continue; if (AddToDict(obj, dict, keys[i])) continue; @@ -82,6 +84,7 @@ boost::python::dict GetPropsAsDict(const T &obj) { if (AddToDict >(obj, dict, keys[i])) continue; if (AddToDict >(obj, dict, keys[i])) continue; if (AddToDict >(obj, dict, keys[i])) continue; + if (AddToDict >(obj, dict, keys[i])) continue; if (AddToDict(obj, dict, keys[i])) continue; } return dict; diff --git a/Code/GraphMol/Wrap/rough_test.py b/Code/GraphMol/Wrap/rough_test.py index aa50f1c37..336b12906 100755 --- a/Code/GraphMol/Wrap/rough_test.py +++ b/Code/GraphMol/Wrap/rough_test.py @@ -1,4 +1,3 @@ -# $Id$ # # Copyright (C) 2003-2013 Greg Landrum and Rational Discovery LLC # All Rights Reserved @@ -493,9 +492,6 @@ class TestCase(unittest.TestCase): m.SetUnsignedProp("c", 2000) m.SetIntProp("d", -2) m.SetUnsignedProp("e", 2, True) - self.assertEquals(m.GetPropsAsDict(), - {'a': False, 'c': 2000, 'b': 1000.0, - 'd': -2, 'prop1': 'foob'}) self.assertEquals(m.GetPropsAsDict(False, True), {'a': False, 'c': 2000, 'b': 1000.0, 'e': 2, 'd': -2, 'prop1': 'foob'}) @@ -3317,7 +3313,12 @@ CAS<~> def testAtomBondProps(self): m = Chem.MolFromSmiles('c1ccccc1') for atom in m.GetAtoms(): - self.assertEquals(atom.GetPropsAsDict(), {'_CIPRank': 0}) + d = atom.GetPropsAsDict() + self.assertEquals(set(d.keys()), set(['_CIPRank', '__computedProps'])) + self.assertEquals(d['_CIPRank'], 0) + self.assertEquals(list(d['__computedProps']), ['_CIPRank']) + + for bond in m.GetBonds(): self.assertEquals(bond.GetPropsAsDict(), {}) diff --git a/Code/GraphMol/new_canon.cpp b/Code/GraphMol/new_canon.cpp index 8ea584e16..8a7df3bee 100644 --- a/Code/GraphMol/new_canon.cpp +++ b/Code/GraphMol/new_canon.cpp @@ -415,7 +415,7 @@ void advancedInitCanonAtom(const ROMol &mol, Canon::canon_atom &atom, atom.isRingStereoAtom = (atom.atom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CW || atom.atom->getChiralTag() == Atom::CHI_TETRAHEDRAL_CCW) && - atom.atom->hasProp("_ringStereoAtoms"); + atom.atom->hasProp(common_properties::_ringStereoAtoms); atom.hasRingNbr = hasRingNbr(mol, atom.atom); } } // end anonymous namespace diff --git a/Code/JavaWrappers/Atom.i b/Code/JavaWrappers/Atom.i index 2ab210bcb..18449368f 100644 --- a/Code/JavaWrappers/Atom.i +++ b/Code/JavaWrappers/Atom.i @@ -58,9 +58,6 @@ #endif %include -/* For the time being, assume all properties will be strings */ -%template(setProp) RDKit::Atom::setProp; - %newobject RDKit::Atom::getProp; %extend RDKit::Atom { std::string getProp(const std::string key){ diff --git a/Code/JavaWrappers/Bond.i b/Code/JavaWrappers/Bond.i index 86cb2900c..409a8d660 100644 --- a/Code/JavaWrappers/Bond.i +++ b/Code/JavaWrappers/Bond.i @@ -49,9 +49,6 @@ %include -/* For the time being, assume all properties will be strings */ -%template(setProp) RDKit::Bond::setProp; - %extend RDKit::Bond { std::string getProp(const std::string key){ std::string res; diff --git a/Code/JavaWrappers/RDProps.i b/Code/JavaWrappers/RDProps.i new file mode 100644 index 000000000..557b33c6e --- /dev/null +++ b/Code/JavaWrappers/RDProps.i @@ -0,0 +1,42 @@ +/* +* +* Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of Novartis Institutes for BioMedical Research Inc. +* nor the names of its contributors may be used to endorse or promote +* products derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +%{ +#include +#include +%} + + +%include + +/* For the time being, assume all properties will be strings */ +%template(setProp) RDKit::RDProps::setProp; diff --git a/Code/JavaWrappers/ROMol.i b/Code/JavaWrappers/ROMol.i index 707fe40eb..3017c3365 100644 --- a/Code/JavaWrappers/ROMol.i +++ b/Code/JavaWrappers/ROMol.i @@ -109,9 +109,6 @@ %} %include -/* For the time being, assume all properties will be strings */ -%template(setProp) RDKit::ROMol::setProp; - %newobject removeHs; %newobject addHs; diff --git a/Code/JavaWrappers/gmwrapper/GraphMolJava.i b/Code/JavaWrappers/gmwrapper/GraphMolJava.i index 87a58fcd5..f75e52fc1 100644 --- a/Code/JavaWrappers/gmwrapper/GraphMolJava.i +++ b/Code/JavaWrappers/gmwrapper/GraphMolJava.i @@ -93,6 +93,7 @@ typedef unsigned long long int uintmax_t; #endif %shared_ptr(std::exception) +%shared_ptr(RDKit::RDProps) %shared_ptr(RDKit::ROMol) %shared_ptr(RDKit::RWMol) %shared_ptr(RDKit::Atom) @@ -154,6 +155,7 @@ typedef unsigned long long int uintmax_t; %include "../types.i" // Conformer seems to need to come before ROMol %include "../Conformer.i" +%include "../RDProps.i" %include "../ROMol.i" %include "../RWMol.i" %include "../Bond.i" diff --git a/Code/RDGeneral/CMakeLists.txt b/Code/RDGeneral/CMakeLists.txt index ccc7bf1a3..be53b37ba 100644 --- a/Code/RDGeneral/CMakeLists.txt +++ b/Code/RDGeneral/CMakeLists.txt @@ -15,7 +15,12 @@ rdkit_headers(Exceptions.h Dict.h FileParseException.h Invariant.h + RDAny.h + RDValue.h + RDValue-doublemagic.h + RDValue-taggedunion.h RDLog.h + RDProps.h StreamOps.h types.h utils.h @@ -29,4 +34,5 @@ if (NOT RDK_INSTALL_INTREE) PATTERN ".svn" EXCLUDE) endif (NOT RDK_INSTALL_INTREE) rdkit_test(testDict testDict.cpp LINK_LIBRARIES RDGeneral) +rdkit_test(testRDValue testRDValue.cpp LINK_LIBRARIES RDGeneral) diff --git a/Code/RDGeneral/Dict.cpp b/Code/RDGeneral/Dict.cpp index 79bc90736..d99797379 100644 --- a/Code/RDGeneral/Dict.cpp +++ b/Code/RDGeneral/Dict.cpp @@ -10,42 +10,7 @@ // #include "Dict.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace RDKit { -namespace { -template -std::string vectToString(const boost::any &val) { - const std::vector &tv = boost::any_cast >(val); - std::ostringstream sstr; - sstr << "["; - std::copy(tv.begin(), tv.end(), std::ostream_iterator(sstr, ",")); - sstr << "]"; - return sstr.str(); -} -std::string intVectToString(const boost::any &val) { - return vectToString(val); -} -std::string uintVectToString(const boost::any &val) { - return vectToString(val); -} -std::string doubleVectToString(const boost::any &val) { - // there is a performance cost associated with the - // LocaleSwitcher, so make sure we only instantiate it when - // it's really necessary (this was github #703) - Utils::LocaleSwitcher ls; - return vectToString(val); -} -} void Dict::getVal(const std::string &what, std::string &res) const { // @@ -58,38 +23,15 @@ void Dict::getVal(const std::string &what, std::string &res) const { // little bit by trying that and, if the cast fails, attempting a couple of // other casts, which will then be lexically cast to type T. // - if (!hasVal(what)) throw KeyErrorException(what); - const boost::any &val = _data.find(what)->second; - try { - res = boost::any_cast(val); - } catch (const boost::bad_any_cast &) { - if (val.type() == typeid(int)) { - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(unsigned int)) { - res = - boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(long)) { - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(unsigned long)) { - res = - boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(float)) { - Utils::LocaleSwitcher ls; // late instantiation of LocaleSwitcher - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(double)) { - Utils::LocaleSwitcher ls; // late instantiation of LocaleSwitcher - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(const char *)) { - res = std::string(boost::any_cast(val)); - } else if (val.type() == typeid(std::vector)) { - res = uintVectToString(val); - } else if (val.type() == typeid(std::vector)) { - res = intVectToString(val); - } else { - throw; + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + rdvalue_tostring(_data[i].val, res); + return; } + } -}; + throw KeyErrorException(what); +} bool Dict::getValIfPresent(const std::string &what, std::string &res) const { // @@ -102,129 +44,13 @@ bool Dict::getValIfPresent(const std::string &what, std::string &res) const { // little bit by trying that and, if the cast fails, attempting a couple of // other casts, which will then be lexically cast to type T. // - DataType::const_iterator pos = _data.find(what); - if (pos == _data.end()) return false; - const boost::any &val = pos->second; - try { - res = boost::any_cast(val); - } catch (const boost::bad_any_cast &) { - if (val.type() == typeid(int)) { - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(unsigned int)) { - res = - boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(long)) { - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(unsigned long)) { - res = - boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(float)) { - Utils::LocaleSwitcher ls; // late instantiation of LocaleSwitcher - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(double)) { - Utils::LocaleSwitcher ls; // late instantiation of LocaleSwitcher - res = boost::lexical_cast(boost::any_cast(val)); - } else if (val.type() == typeid(const char *)) { - res = std::string(boost::any_cast(val)); - } else if (val.type() == typeid(std::vector)) { - res = uintVectToString(val); - } else if (val.type() == typeid(std::vector)) { - res = intVectToString(val); - } else { - return false; + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + rdvalue_tostring(_data[i].val, res); + return true; } } - return true; -}; - -namespace { -template -typename boost::enable_if, T>::type _fromany( - const boost::any &arg) { - T res; - if (arg.type() == typeid(std::string) || arg.type() == typeid(const char *)) { - Utils::LocaleSwitcher ls; // late instantiation of LocaleSwitcher - try { - res = boost::any_cast(arg); - } catch (const boost::bad_any_cast &exc) { - try { - res = boost::lexical_cast(boost::any_cast(arg)); - } catch (...) { - throw exc; - } - } - } else { - res = boost::any_cast(arg); - } - return res; + return false; } -template -typename boost::enable_if, T>::type _fromany( - const boost::any &arg) { - T res; - if (arg.type() == typeid(std::string) || arg.type() == typeid(const char *)) { - try { - res = boost::any_cast(arg); - } catch (const boost::bad_any_cast &exc) { - try { - res = boost::lexical_cast(boost::any_cast(arg)); - } catch (...) { - throw exc; - } - } - } else { - res = boost::any_cast(arg); - } - return res; -} -template -typename boost::disable_if, T>::type _fromany( - const boost::any &arg) { - return boost::any_cast(arg); -} -} -template -T Dict::fromany(const boost::any &arg) const { - return _fromany(arg); -}; -template -boost::any Dict::toany(T arg) const { - return boost::any(arg); -}; - -#define ANY_FORCE(T) \ - template T Dict::fromany(const boost::any &arg) const; \ - template boost::any Dict::toany(T arg) const; - -ANY_FORCE(bool); -ANY_FORCE(boost::shared_array); -ANY_FORCE(boost::shared_array); -ANY_FORCE(double); -ANY_FORCE(int); -ANY_FORCE(std::list); -ANY_FORCE(std::string); -ANY_FORCE(std::vector >); -ANY_FORCE(std::vector >); -ANY_FORCE(std::vector); -ANY_FORCE(std::vector); -ANY_FORCE(std::vector >); -ANY_FORCE(std::vector); -ANY_FORCE(std::vector >); -ANY_FORCE(std::vector >); -ANY_FORCE(std::vector); -ANY_FORCE(std::vector); -ANY_FORCE(unsigned int); - -template const std::string &Dict::fromany( - const boost::any &arg) const; - -typedef boost::tuples::tuple - uint32_t_tuple; -typedef boost::tuples::tuple double_tuple; - -template uint32_t_tuple Dict::fromany( - const boost::any &arg) const; -template boost::any Dict::toany(uint32_t_tuple arg) const; -template double_tuple Dict::fromany(const boost::any &arg) const; -template boost::any Dict::toany(double_tuple arg) const; + } diff --git a/Code/RDGeneral/Dict.h b/Code/RDGeneral/Dict.h index 6e902e0a7..acd620eff 100644 --- a/Code/RDGeneral/Dict.h +++ b/Code/RDGeneral/Dict.h @@ -18,8 +18,8 @@ #include #include #include -#include -#include +#include "RDValue.h" +#include "Exceptions.h" #include namespace RDKit { @@ -28,29 +28,61 @@ typedef std::vector STR_VECT; //! \brief The \c Dict class can be used to store objects of arbitrary //! type keyed by \c strings. //! -//! The actual storage is done using \c boost::any objects. +//! The actual storage is done using \c RDValue objects. //! class Dict { - public: - typedef std::map DataType; - Dict() { _data.clear(); }; + struct Pair { + std::string key; + RDValue val; - Dict(const Dict &other) : _data(other._data){}; + Pair() : key(), val() {} + Pair(const std::string &s, const RDValue &v) : key(s), val(v) { + } + }; + + typedef std::vector DataType; +public: + Dict() : _data(), _hasNonPodData(false) { }; + Dict(const Dict &other) : _data(other._data) { + _hasNonPodData = other._hasNonPodData; + if (_hasNonPodData) { + std::vector data(other._data.size()); + _data.swap(data); + for (size_t i=0; i< _data.size(); ++i) { + _data[i].key = other._data[i].key; + copy_rdvalue(_data[i].val, other._data[i].val); + } + } + } + + ~Dict() { + reset(); // to clear pointers if necessary + } + Dict &operator=(const Dict &other) { - _data = other._data; + _hasNonPodData = other._hasNonPodData; + if (_hasNonPodData) { + std::vector data(other._data.size()); + _data.swap(data); + for (size_t i=0; i< _data.size(); ++i) { + _data[i].key = other._data[i].key; + copy_rdvalue(_data[i].val, other._data[i].val); + } + } else { + _data = other._data; + } return *this; }; //---------------------------------------------------------- //! \brief Returns whether or not the dictionary contains a particular //! key. - bool hasVal(const char *what) const { - std::string key(what); - return hasVal(key); - }; bool hasVal(const std::string &what) const { - return _data.find(what) != _data.end(); + for(size_t i=0 ; i< _data.size(); ++i) { + if (_data[i].key == what ) return true; + } + return false; }; //---------------------------------------------------------- @@ -62,7 +94,7 @@ class Dict { STR_VECT res; DataType::const_iterator item; for (item = _data.begin(); item != _data.end(); item++) { - res.push_back(item->first); + res.push_back(item->key); } return res; } @@ -87,33 +119,16 @@ class Dict { //! \overload template T getVal(const std::string &what) const { - DataType::const_iterator pos = _data.find(what); - if (pos == _data.end()) throw KeyErrorException(what); - const boost::any &val = pos->second; - return fromany(val); + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + return from_rdvalue(_data[i].val); + } + } + throw KeyErrorException(what); } - //! \overload - template - T getVal(const char *what, - T &res) const { // FIX: it doesn't really fit that this returns T - std::string key(what); - res = getVal(key); - return res; - }; - //! \overload - template - T getVal(const char *what) const { - std::string key(what); - return getVal(key); - }; - //! \overload void getVal(const std::string &what, std::string &res) const; - //! \overload - void getVal(const char *what, std::string &res) const { - getVal(std::string(what), res); - }; //---------------------------------------------------------- //! \brief Potentially gets the value associated with a particular key @@ -132,25 +147,18 @@ class Dict { template bool getValIfPresent(const std::string &what, T &res) const { - DataType::const_iterator pos = _data.find(what); - if (pos == _data.end()) return false; - const boost::any &val = pos->second; - res = fromany(val); - return true; + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + res = from_rdvalue(_data[i].val); + return true; + } + } + return false; }; - template - bool getValIfPresent(const char *what, T &res) const { - std::string key(what); - return getValIfPresent(key, res); - }; //! \overload bool getValIfPresent(const std::string &what, std::string &res) const; - //! \overload - bool getValIfPresent(const char *what, std::string &res) const { - return getValIfPresent(std::string(what), res); - }; //---------------------------------------------------------- //! \brief Sets the value associated with a key @@ -167,14 +175,48 @@ class Dict { */ template void setVal(const std::string &what, T &val) { - _data[what] = toany(val); + _hasNonPodData = true; + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + _data[i].val = val; + return; + } + } + _data.push_back(Pair(what, val)); }; - //! \overload + template - void setVal(const char *what, T &val) { - std::string key = what; - setVal(key, val); + void setPODVal(const std::string &what, T val) { + _hasNonPodData = true; + for(size_t i=0; i< _data.size(); ++i) { + if (_data[i].key == what) { + _data[i].val = val; + return; + } + } + _data.push_back(Pair(what, val)); }; + + void setVal(const std::string &what, bool val) { + setPODVal(what, val); + } + + void setVal(const std::string &what, double val) { + setPODVal(what, val); + } + + void setVal(const std::string &what, float val) { + setPODVal(what, val); + } + + void setVal(const std::string &what, int val) { + setPODVal(what, val); + } + + void setVal(const std::string &what, unsigned int val) { + setPODVal(what, val); + } + //! \overload void setVal(const std::string &what, const char *val) { std::string h(val); @@ -193,43 +235,58 @@ class Dict { a KeyErrorException will be thrown. */ void clearVal(const std::string &what) { - if (!this->hasVal(what)) throw KeyErrorException(what); - _data.erase(what); - }; - - //! \overload - void clearVal(const char *what) { - std::string key = what; - clearVal(key); + for(DataType::iterator it = _data.begin(); it < _data.end() ; ++it) { + if (it->key == what) { + _data.erase(it); + return; + } + } + throw KeyErrorException(what); }; //---------------------------------------------------------- //! \brief Clears all keys (and values) from the dictionary. //! - void reset() { _data.clear(); }; + void reset() { + if (_hasNonPodData) { + for (size_t i=0; i< _data.size(); ++i) { + RDValue::cleanup_rdvalue(_data[i].val); + } + } + DataType data; + _data.swap(data); + }; //---------------------------------------------------------- - //! Converts a \c boost::any to type \c T + //! Converts a \c RDAny to type \c T /*! - \param arg a \c boost::any reference + \param arg a \c RDAny reference \returns the converted object of type \c T */ + /* template - T fromany(const boost::any &arg) const; - + T fromany(const RDAny &arg) const { + return from_rdany(arg); + } + */ //---------------------------------------------------------- - //! Converts an instance of type \c T to \c boost::any + //! Converts an instance of type \c T to \c RDAny /*! \param arg the object to be converted - \returns a \c boost::any instance + \returns a \c RDAny instance */ + /* template - boost::any toany(T arg) const; - + RDAny toany(T arg) const { + return RDAny(arg); + }; + */ private: DataType _data; //!< the actual dictionary + bool _hasNonPodData; // if true, need a deep copy + // (copy_rdvalue) }; } #endif diff --git a/Code/RDGeneral/Invariant.h b/Code/RDGeneral/Invariant.h index 9ec53bcca..e274a1670 100644 --- a/Code/RDGeneral/Invariant.h +++ b/Code/RDGeneral/Invariant.h @@ -17,7 +17,7 @@ #include #include -#include +#include "RDLog.h" #ifdef RDDEBUG // Enable RDDEBUG for testing whether rdcast diff --git a/Code/RDGeneral/RDAny.h b/Code/RDGeneral/RDAny.h new file mode 100644 index 000000000..ed5681386 --- /dev/null +++ b/Code/RDGeneral/RDAny.h @@ -0,0 +1,216 @@ +// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Novartis Institutes for BioMedical Research Inc. +// nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef RDKIT_RDANY_H +#define RDKIT_RDANY_H +#include +#include +#include +#include "LocaleSwitcher.h" +#include "RDValue.h" +#include +#include +#include +#include +namespace RDKit { + +// RDValue does not dynamically create POD types (kind of like +// cdiggins::any) However, it doesn't use RTTI type info +// directly, it uses a companion short valued type +// to determine what to do. +// For unregistered types, it falls back to boost::any. +// The Size of an RDAny is (sizeof(double) + sizeof(short) == 10 bytes) +// +// For the sake of compatibility, errors throw boost::bad_any_cast +// +// Examples: +// +// RDAny v(2.); +// v = 1; +// std::vector d; +// v == d; +// v.asDoubleVect().push_back(4.) +// rdany_cast(v).push_back(4.) +// +// Falls back to boost::any for non registered types +// v = boost::shared_ptr(new ROMol(m)); +// + +// Safe container for RDValue -- cleans up memory and copy constructs +struct RDAny { + RDValue m_value; + + RDAny() : m_value() {} + template RDAny(const T &d) : m_value(d) {} + /* + explicit RDAny(bool v) : m_value(v) {} + template + explicit RDAny(std::vector *v) : m_value(v) {} + template + explicit RDAny(const boost::shared_ptr &v) : m_value(v) {} + */ + RDAny(const RDAny &rhs) { + copy_rdvalue(m_value, rhs.m_value); + } + + ~RDAny() { RDValue::cleanup_rdvalue(m_value); } + + // For easy of use: + // RDAny v; + // v = 2.0; + // v = std::string("foo..."); + + RDAny &operator=(const RDAny &rhs) { + copy_rdvalue(m_value, rhs.m_value); + return *this; + } + + RDAny &operator=(float d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(int d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(unsigned int d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(bool d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const std::string &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const std::vector &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const std::vector &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const std::vector &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const std::vector &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = d; + return *this; + } + + RDAny &operator=(const std::vector &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d); + return *this; + } + + RDAny &operator=(const boost::any &d) { + RDValue::cleanup_rdvalue(m_value); + m_value = RDValue(d);//new boost::any(d); + return *this; + } + + template + RDAny &operator=(const T &d) { + RDValue::cleanup_rdvalue(m_value); + boost::any *v = new boost::any(d); + m_value = RDValue(v); + return *this; + } + +}; + +//////////////////////////////////////////////////////////////// +// rdany_cast +//////////////////////////////////////////////////////////////// + + +// Const Access +template +const T rdany_cast(const RDAny &d) { + return rdvalue_cast(d.m_value); +} + +// Direct access +template +T rdany_cast(RDAny &d) { + return rdvalue_cast(d.m_value); +} + +template +typename boost::enable_if, T>::type from_rdany( + const RDAny &arg) { + T res; + if (arg.m_value.getTag() == RDTypeTag::StringTag) { + Utils::LocaleSwitcher ls; + try { + res = rdany_cast(arg); + } catch (const boost::bad_any_cast &exc) { + try { + res = boost::lexical_cast(rdany_cast(arg)); + } catch (...) { + throw exc; + } + } + } else { + res = rdany_cast(arg); + } + return res; +} + +template +typename boost::disable_if, T>::type from_rdany( + const RDAny &arg) { + return rdany_cast(arg); +} + +} +#endif diff --git a/Code/RDGeneral/RDProps.h b/Code/RDGeneral/RDProps.h new file mode 100644 index 000000000..50832d11a --- /dev/null +++ b/Code/RDGeneral/RDProps.h @@ -0,0 +1,150 @@ +#ifndef RDKIT_RDPROPS_H +#define RDKIT_RDPROPS_H +#include "Dict.h" +#include "types.h" +#include + +namespace RDKit { + +class RDProps { +protected: + mutable Dict dp_props; + // It is a quirk of history that this is mutable + // as the RDKit allows properties to be set + // on const obects. + + public: + RDProps() : dp_props() {} + RDProps(const RDProps &rhs) : dp_props(rhs.dp_props) {} + RDProps& operator=(const RDProps &rhs) { + dp_props = rhs.dp_props; + return *this; + } + void clear() { + dp_props.reset(); + } + // ------------------------------------ + // Local Property Dict functionality + // all setProp functions are const because they + // are not meant to change the atom chemically + // ------------------------------------ + //! returns a list with the names of our \c properties + STR_VECT getPropList(bool includePrivate = true, + bool includeComputed = true) const { + const STR_VECT &tmp = dp_props.keys(); + STR_VECT res, computed; + if (!includeComputed && + getPropIfPresent(detail::computedPropName, computed)) { + computed.push_back(detail::computedPropName); + } + + STR_VECT::const_iterator pos = tmp.begin(); + while (pos != tmp.end()) { + if ((includePrivate || (*pos)[0] != '_') && + std::find(computed.begin(), computed.end(), *pos) == computed.end()) { + res.push_back(*pos); + } + pos++; + } + return res; + } + + //! sets a \c property value + /*! + \param key the name under which the \c property should be stored. + If a \c property is already stored under this name, it will be + replaced. + \param val the value to be stored + \param computed (optional) allows the \c property to be flagged + \c computed. + */ + + //! \overload + template + void setProp(const std::string &key, T val, bool computed = false) const { + if (computed) { + STR_VECT compLst; + getPropIfPresent(detail::computedPropName, compLst); + if (std::find(compLst.begin(), compLst.end(), key) == compLst.end()) { + compLst.push_back(key); + dp_props.setVal(detail::computedPropName, compLst); + } + } + // setProp(key.c_str(),val); + dp_props.setVal(key, val); + } + + //! allows retrieval of a particular property value + /*! + + \param key the name under which the \c property should be stored. + If a \c property is already stored under this name, it will be + replaced. + \param res a reference to the storage location for the value. + + Notes: + - if no \c property with name \c key exists, a KeyErrorException will be + thrown. + - the \c boost::lexical_cast machinery is used to attempt type + conversions. + If this fails, a \c boost::bad_lexical_cast exception will be thrown. + + */ + //! \overload + template + void getProp(const std::string &key, T &res) const { + dp_props.getVal(key, res); + } + + //! \overload + template + T getProp(const std::string &key) const { + return dp_props.getVal(key); + } + + //! returns whether or not we have a \c property with name \c key + //! and assigns the value if we do + //! \overload + template + bool getPropIfPresent(const std::string &key, T &res) const { + return dp_props.getValIfPresent(key, res); + } + + //! \overload + bool hasProp(const std::string &key) const { + return dp_props.hasVal(key); + }; + + //! clears the value of a \c property + /*! + Notes: + - if no \c property with name \c key exists, a KeyErrorException + will be thrown. + - if the \c property is marked as \c computed, it will also be removed + from our list of \c computedProperties + */ + //! \overload + void clearProp(const std::string &key) const { + STR_VECT compLst; + if (getPropIfPresent(detail::computedPropName, compLst)) { + STR_VECT_I svi = std::find(compLst.begin(), compLst.end(), key); + if (svi != compLst.end()) { + compLst.erase(svi); + dp_props.setVal(detail::computedPropName, compLst); + } + } + dp_props.clearVal(key); + }; + + //! clears all of our \c computed \c properties + void clearComputedProps() const { + STR_VECT compLst; + if (getPropIfPresent(detail::computedPropName, compLst)) { + BOOST_FOREACH (const std::string &sv, compLst) { dp_props.clearVal(sv); } + compLst.clear(); + dp_props.setVal(detail::computedPropName, compLst); + } + } +}; +} +#endif diff --git a/Code/RDGeneral/RDValue-doublemagic.h b/Code/RDGeneral/RDValue-doublemagic.h new file mode 100644 index 000000000..837123c6d --- /dev/null +++ b/Code/RDGeneral/RDValue-doublemagic.h @@ -0,0 +1,416 @@ +// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Novartis Institutes for BioMedical Research Inc. +// nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef RDKIT_RDVALUE_PTRMAGIC_H +#define RDKIT_RDVALUE_PTRMAGIC_H + +#include +#include +#include +#include "Invariant.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "LocaleSwitcher.h" + +#define RDVALUE_HASBOOL + +namespace RDKit { + + // Inspired by + // https://nikic.github.io/2012/02/02/Pointer-magic-for-efficient-dynamic-value-representations.html +// 16 bit storage for value types using Quiet NaN spaces in +// doubles +// Won't work on Solaris and some other os's as mmaping maps from +// top memory down +// Example check: +// std::string *pointer = new std::string(v); +// assert((reinterpret_cast(pointer) & StringTag) == 0); + +// implementations, need a typedef at compile time to figure this out. +// current implementation is probably little endian, need to check. + +/* + Encoding for storing other things as a double. Use + Quiet NaN + Quiet NaN: // used to encode types + F F F 1XXX < - X = type bits (first bit is set to one) + + seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm + s1111111|11111ppp|pppppppp|pppppppp|pppppppp|pppppppp|pppppppp|pppppppp + ^- first mantissa bit 1 everything else is "payload" -^ + ^- exponent bits all 1 and mustn't be all-zero (as it + ^- any sign bit would be INF then) + + Available + 8 = 1000 MaxDouble // Not really a tag, is a sentinel + 9 = 1001 Float + b = 1010 Int32 + a = 1011 Uint32 + C = 1100 + D = 1101 + E = 1110 + F = 1111 PtrTag (look at lower 3 bits for type) +*/ + +namespace RDTypeTag { + static const uint64_t NaN = 0xfff7FFFFFFFFFFFF; // signalling NaN + static const uint64_t MaxDouble = 0xfff8000000000000; // + static const uint64_t DoubleTag = 0xfff8000000000000; // + static const uint64_t FloatTag = 0xfff9000000000000; // + static const uint64_t IntTag = 0xfffa000000000000; // + static const uint64_t UnsignedIntTag = 0xfffb000000000000; // + static const uint64_t BoolTag = 0xfffc000000000000; // + + // PTR Tags use the last 3 bits for typing info + static const uint64_t PtrTag = 0xffff000000000000; + static const uint64_t StringTag = 0xffff000000000001; //001 + static const uint64_t VecDoubleTag = 0xffff000000000002; //010 + static const uint64_t VecFloatTag = 0xffff000000000003; //011 + static const uint64_t VecIntTag = 0xffff000000000004; //100 + static const uint64_t VecUnsignedIntTag = 0xffff000000000005; //101 + static const uint64_t VecStringTag = 0xffff000000000006; //110 + static const uint64_t AnyTag = 0xffff000000000007; //111 + + // Retrieves the tag (and PtrMask) from the type + template inline uint64_t GetTag() { return AnyTag; } + template<> inline uint64_t GetTag() { return MaxDouble; } + template<> inline uint64_t GetTag() { return FloatTag; } + template<> inline uint64_t GetTag() { return IntTag; } + template<> inline uint64_t GetTag() { return UnsignedIntTag; } + template<> inline uint64_t GetTag() { return BoolTag; } + template<> inline uint64_t GetTag() { return StringTag; } + template<> inline uint64_t GetTag >() { return VecDoubleTag; } + template<> inline uint64_t GetTag >() { return VecFloatTag; } + template<> inline uint64_t GetTag >() { return VecIntTag; } + template<> inline uint64_t GetTag >() { return VecUnsignedIntTag; } + template<> inline uint64_t GetTag >() { return VecStringTag; } + template<> inline uint64_t GetTag() { return AnyTag; } +} + + +struct RDValue { + // Bit Twidling for conversion from the Tag to a Pointer + static const uint64_t TagMask = 0xFFFF000000000000; + static const uint64_t PointerTagMask = 0xFFFF000000000007; + static const uint64_t ApplyMask = 0x0000FFFFFFFFFFFF; + static const uint64_t ApplyPtrMask = 0x0000FFFFFFFFFFF8; + + union { + double doubleBits; + uint64_t otherBits; + }; + + inline RDValue() : doubleBits(0.0) {} + + inline RDValue(double number) { + if (boost::math::isnan(number)) { + // Store a signalling NaN for NaN's. + // quiet NaNs are used for other types. + otherBits = RDTypeTag::NaN; + assert(boost::math::isnan(doubleBits)); + } + else + doubleBits = number; + } + + inline RDValue(float number) { + otherBits = 0 | RDTypeTag::FloatTag; + memcpy(((char*)&otherBits), &number, sizeof(float)); + } + + inline RDValue(int32_t number) { + otherBits = (((uint64_t)number) & ApplyMask ) | RDTypeTag::IntTag; + } + + inline RDValue(unsigned int number) { + otherBits = (((uint64_t)number) & ApplyMask ) | RDTypeTag::UnsignedIntTag; + } + + inline RDValue(bool number) { + otherBits = (static_cast(number) & ApplyMask) | RDTypeTag::BoolTag; + } + + inline RDValue(boost::any *pointer) { + // ensure that the pointer really is only 48 bit + assert((reinterpret_cast(pointer) & RDTypeTag::AnyTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::AnyTag; + } + + inline RDValue(const boost::any &any) { + // ensure that the pointer really is only 48 bit + boost::any *pointer = new boost::any(any); + assert((reinterpret_cast(pointer) & RDTypeTag::AnyTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::AnyTag; + } + + // Unknown types are stored as boost::any + template + inline RDValue(const T&v) { + boost::any *pointer = new boost::any(v); + assert((reinterpret_cast(pointer) & RDTypeTag::AnyTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::AnyTag; + } + + inline RDValue(const std::string &v) { + std::string *pointer = new std::string(v); + assert((reinterpret_cast(pointer) & RDTypeTag::StringTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::StringTag; + } + + inline RDValue(const std::vector &v) { + std::vector *pointer = new std::vector(v); + assert((reinterpret_cast(pointer) & RDTypeTag::VecDoubleTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::VecDoubleTag; + } + + inline RDValue(const std::vector &v) { + std::vector *pointer = new std::vector(v); + assert((reinterpret_cast(pointer) & RDTypeTag::VecFloatTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::VecFloatTag; + } + + inline RDValue(const std::vector &v) { + std::vector *pointer = new std::vector(v); + assert((reinterpret_cast(pointer) & RDTypeTag::VecIntTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::VecIntTag; + } + + inline RDValue(const std::vector &v) { + std::vector *pointer = new std::vector(v); + assert((reinterpret_cast(pointer) & RDTypeTag::VecIntTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::VecUnsignedIntTag; + } + + inline RDValue(const std::vector &v) { + std::vector *pointer = new std::vector(v); + assert((reinterpret_cast(pointer) & RDTypeTag::VecStringTag) == 0); + otherBits = reinterpret_cast(pointer) | RDTypeTag::VecStringTag; + } + + uint64_t getTag() const { + if (otherBits < RDTypeTag::MaxDouble || + (otherBits & RDTypeTag::NaN) == RDTypeTag::NaN) { + return RDTypeTag::DoubleTag; + } + + uint64_t tag = otherBits & TagMask; + if (tag == RDTypeTag::PtrTag) + return otherBits & PointerTagMask; + return tag; + } + + // ptrCast - unsafe, use rdvalue_cast instead. + template + inline T* ptrCast() const { + return reinterpret_cast(otherBits & ~RDTypeTag::GetTag()); + } + + // RDValue doesn't have an explicit destructor, it must + // be wrapped in a container. + // The idea is that POD types don't need to be destroyed + // and this allows the container optimization possibilities. + inline void destroy() { + switch(getTag()) { + case RDTypeTag::StringTag: + delete ptrCast(); + break; + case RDTypeTag::VecDoubleTag: + delete ptrCast >(); + break; + case RDTypeTag::VecFloatTag: + delete ptrCast >(); + break; + case RDTypeTag::VecIntTag: + delete ptrCast >(); + break; + case RDTypeTag::VecUnsignedIntTag: + delete ptrCast >(); + break; + case RDTypeTag::VecStringTag: + delete ptrCast >(); + break; + case RDTypeTag::AnyTag: + delete ptrCast(); + break; + default: + break; + } + } + + static + inline void cleanup_rdvalue(RDValue v) { v.destroy(); } + +}; + +///////////////////////////////////////////////////////////////////////////////////// +// Given two RDValue::Values - copy the appropriate structure +// RDValue doesn't have a copy constructor, the default +// copy act's like a move for better value semantics. +// Containers may need to copy though. +inline void copy_rdvalue(RDValue &dest, + const RDValue &src) { + dest.destroy(); + switch(src.getTag()) { + case RDTypeTag::StringTag: + dest = RDValue(*src.ptrCast()); + break; + case RDTypeTag::VecDoubleTag: + dest = RDValue(*src.ptrCast >()); + break; + case RDTypeTag::VecFloatTag: + dest = RDValue(*src.ptrCast >()); + break; + case RDTypeTag::VecIntTag: + dest = RDValue(*src.ptrCast >()); + break; + case RDTypeTag::VecUnsignedIntTag: + dest = RDValue(*src.ptrCast >()); + break; + case RDTypeTag::VecStringTag: + dest = RDValue(*src.ptrCast >()); + break; + case RDTypeTag::AnyTag: + dest = RDValue(*src.ptrCast()); + break; + default: + dest = src; + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// rdvalue_is + +template +inline bool rdvalue_is(RDValue v) { + return v.getTag() == RDTypeTag::GetTag::type>(); +} + +template<> +inline bool rdvalue_is(RDValue v) { + return v.otherBits < RDTypeTag::MaxDouble || + (v.otherBits & RDTypeTag::NaN) == RDTypeTag::NaN; +} + +template<> +inline bool rdvalue_is(RDValue v) { + return rdvalue_is(v); +} + +/* +template<> +inline bool rdvalue_is(RDValue v) { + return (v.getTag() == RDTypeTag::IntTag && + (static_cast(v.otherBits & ~RDTypeTag::IntTag) == 1 || + static_cast(v.otherBits & ~RDTypeTag::IntTag) == 0 )); +} + +template<> +inline bool rdvalue_is(RDValue v) { + return rdvalue_is(v); +} +*/ + +///////////////////////////////////////////////////////////////////////////////////// +// rdvalue_cast +// +// POD types do not support reference semantics. Other types do. +// rdvalue_cast &>(RDValue); // ok +// rdvalue_cast(RDValue); // bad_any_cast + + typedef RDValue RDValue_cast_t; +// Get stuff stored in boost any +template +inline T rdvalue_cast(RDValue v) { + // Disable reference and pointer casts to POD data. + BOOST_STATIC_ASSERT( !( + (boost::is_pointer::value && ( + boost::is_integral::type>::value || + boost::is_floating_point::type>::value)) || + (boost::is_reference::value && ( + boost::is_integral::type>::value || + boost::is_floating_point::type>::value)) + )); + + if (rdvalue_is(v)) { + return boost::any_cast(*v.ptrCast()); + } + throw boost::bad_any_cast(); +} + +// POD casts +template<> +inline double rdvalue_cast(RDValue v) { + if (rdvalue_is(v)) return v.doubleBits; + throw boost::bad_any_cast(); +} + +template<> +inline float rdvalue_cast(RDValue v) { + if (rdvalue_is(v)) { + float f; + memcpy(&f, ((char*)&v.otherBits), sizeof(float)); + return f; + } + throw boost::bad_any_cast(); +} + +// n.b. with const expressions, could use ~RDTagTypes::GetTag() +// and enable_if +template<> +inline int rdvalue_cast(RDValue v) { + if (rdvalue_is(v)) return static_cast(v.otherBits & + ~RDTypeTag::IntTag); + throw boost::bad_any_cast(); +} +template<> +inline unsigned int rdvalue_cast(RDValue v) { + if (rdvalue_is(v)) return static_cast( + v.otherBits & ~RDTypeTag::UnsignedIntTag); + throw boost::bad_any_cast(); +} + +template<> +inline bool rdvalue_cast(RDValue v) { + if (rdvalue_is(v)) return static_cast( + v.otherBits & ~RDTypeTag::BoolTag); + throw boost::bad_any_cast(); +} + +} // namespace rdkit +#endif + diff --git a/Code/RDGeneral/RDValue-taggedunion.h b/Code/RDGeneral/RDValue-taggedunion.h new file mode 100644 index 000000000..533c8fc90 --- /dev/null +++ b/Code/RDGeneral/RDValue-taggedunion.h @@ -0,0 +1,355 @@ +// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Novartis Institutes for BioMedical Research Inc. +// nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef RDKIT_RDVALUE_TAGGED_UNION_H +#define RDKIT_RDVALUE_TAGGED_UNION_H + +#include +#include +#include +#include "Invariant.h" +#include +#include +#include +#include +#include +#include +#include "LocaleSwitcher.h" + +#define RDVALUE_HASBOOL + +namespace RDKit { + +// RDValue does not dynamically create POD types (kind of like +// cdiggins::any) However, it doesn't use RTTI type info +// directly, it uses a companion short valued type +// to determine what to do. +// For unregistered types, it falls back to boost::any. +// The Size of an RDAny is (sizeof(double) + sizeof(short) == 10 bytes +// (aligned to actually 16 so hard to pass as value type) +// +// For the sake of compatibility, errors throw boost::bad_any_cast +// +// Examples: +// +// RDAny v(2.); +// v = 1; +// std::vector d; +// v == d; +// v.asDoubleVect().push_back(4.) +// rdany_cast(v).push_back(4.) +// +// Falls back to boost::any for non registered types +// v = boost::shared_ptr(new ROMol(m)); +// + +// RDValue does not manange memory of non-pod data +// this must be done externally (string, Any, vector...) +// Tagged union + +namespace RDTypeTag { + const short EmptyTag = 0; + const short IntTag = 1; + const short DoubleTag = 2; + const short StringTag = 3; + const short FloatTag = 4; + const short BoolTag = 5; + const short UnsignedIntTag = 6; + const short AnyTag = 7; + const short VecDoubleTag = 8; + const short VecFloatTag = 9; + const short VecIntTag = 10; + const short VecUnsignedIntTag = 11; + const short VecStringTag = 12; + template inline short GetTag() { return AnyTag; } + template<> inline short GetTag() { return DoubleTag; } + template<> inline short GetTag() { return FloatTag; } + template<> inline short GetTag() { return IntTag; } + template<> inline short GetTag() { return UnsignedIntTag; } + template<> inline short GetTag() { return BoolTag; } + template<> inline short GetTag() { return StringTag; } + template<> inline short GetTag >() { return VecDoubleTag; } + template<> inline short GetTag >() { return VecFloatTag; } + template<> inline short GetTag >() { return VecIntTag; } + template<> inline short GetTag >() { return VecUnsignedIntTag; } + template<> inline short GetTag >() { return VecStringTag; } + template<> inline short GetTag() { return AnyTag; } + + namespace detail { + union Value { + double d; + float f; + int i; + unsigned u; + bool b; + std::string *s; + boost::any *a; + std::vector *vd; + std::vector *vf; + std::vector *vi; + std::vector *vu; + std::vector *vs; + + inline Value() {} + inline Value(double v) : d(v) {} + inline Value(float v) : f(v) {} + inline Value(int v) : i(v) {} + inline Value(unsigned int v) : u(v) {} + inline Value(bool v) : b(v) {} + inline Value(std::string *v) : s(v) {} + inline Value(boost::any *v) : a(v) {} + inline Value(std::vector *v) : vd(v) {} + inline Value(std::vector *v) : vf(v) {} + inline Value(std::vector *v) : vi(v) {} + inline Value(std::vector *v) : vu(v) {} + inline Value(std::vector *v) : vs(v) {} + }; + + template + inline T* valuePtrCast(Value value) { + return boost::any_cast(*value.a); + } + template<> + inline boost::any *valuePtrCast(Value value) { return value.a; } + + template<> + inline std::string *valuePtrCast(Value value) { return value.s; } + template<> + inline std::vector *valuePtrCast >(Value value) { + return value.vd; } + template<> + inline std::vector *valuePtrCast >(Value value) { + return value.vf; } + template<> + inline std::vector *valuePtrCast >(Value value) { + return value.vi; } + template<> + inline std::vector *valuePtrCast >( + Value value) { + return value.vu; } + template<> + inline std::vector *valuePtrCast >( + Value value) { + return value.vs; } + } +} + +struct RDValue { + RDTypeTag::detail::Value value; + short type; + short reserved_tag; // 16 bit alignment + + inline RDValue(): value(0.0), type(RDTypeTag::EmptyTag) {} + // Pod Style (Direct storage) + inline RDValue(double v) : value(v), type(RDTypeTag::DoubleTag) {} + inline RDValue(float v) : value(v), type(RDTypeTag::FloatTag) {} + inline RDValue(int v) : value(v), type(RDTypeTag::IntTag) {} + inline RDValue(unsigned v) : value(v), type(RDTypeTag::UnsignedIntTag) {} + inline RDValue(bool v) : value(v), type(RDTypeTag::BoolTag) {} + + inline RDValue(boost::any *v) : value(v),type(RDTypeTag::AnyTag) {} + + // Copies passed in pointers + inline RDValue(const boost::any &v) : value(new boost::any(v)),type(RDTypeTag::AnyTag) {} + inline RDValue(const std::string &v) : value(new std::string(v)),type(RDTypeTag::StringTag){}; + template + inline RDValue(const T &v) : value(new boost::any(v)),type(RDTypeTag::AnyTag) {} + + inline RDValue(const std::vector &v) : value(new std::vector(v)), + type(RDTypeTag::VecDoubleTag) {} + inline RDValue(const std::vector &v) : value(new std::vector(v)), + type(RDTypeTag::VecFloatTag) {} + inline RDValue(const std::vector &v) : value(new std::vector(v)), + type(RDTypeTag::VecIntTag) {} + inline RDValue(const std::vector &v) : + value(new std::vector(v)), + type(RDTypeTag::VecUnsignedIntTag) {} + inline RDValue(const std::vector &v) : + value(new std::vector(v)), + type(RDTypeTag::VecStringTag) {} + + short getTag() const { return type; } + + // ptrCast - unsafe, use rdvalue_cast instead. + template + inline T* ptrCast() const { + return RDTypeTag::detail::valuePtrCast(value); + } + + // RDValue doesn't have an explicit destructor, it must + // be wrapped in a container. + // The idea is that POD types don't need to be destroyed + // and this allows the container optimization possibilities. + void destroy() { + switch (type) { + case RDTypeTag::StringTag: + delete value.s; + break; + case RDTypeTag::AnyTag: + delete value.a; + break; + case RDTypeTag::VecDoubleTag: + delete value.vd; + break; + case RDTypeTag::VecFloatTag: + delete value.vf; + break; + case RDTypeTag::VecIntTag: + delete value.vi; + break; + case RDTypeTag::VecUnsignedIntTag: + delete value.vu; + break; + case RDTypeTag::VecStringTag: + delete value.vs; + break; + default: + break; + } + type = RDTypeTag::EmptyTag; + } + + static // Given a type and an RDAnyValue - delete the appropriate structure + inline void cleanup_rdvalue(RDValue &rdvalue) { + rdvalue.destroy(); + } +}; + +///////////////////////////////////////////////////////////////////////////////////// +// Given two RDValue::Values - copy the appropriate structure +// RDValue doesn't have a copy constructor, the default +// copy act's like a move for better value semantics. +// Containers may need to copy though. +inline void copy_rdvalue(RDValue &dest, + const RDValue &src) { + dest.destroy(); + dest.type = src.type; + switch (src.type) { + case RDTypeTag::StringTag: + dest.value.s = new std::string(*src.value.s); + break; + case RDTypeTag::AnyTag: + dest.value.a = new boost::any(*src.value.a); + break; + case RDTypeTag::VecDoubleTag: + dest.value.vd = new std::vector(*src.value.vd); + break; + case RDTypeTag::VecFloatTag: + dest.value.vf = new std::vector(*src.value.vf); + break; + case RDTypeTag::VecIntTag: + dest.value.vi = new std::vector(*src.value.vi); + break; + case RDTypeTag::VecUnsignedIntTag: + dest.value.vu = new std::vector(*src.value.vu); + break; + case RDTypeTag::VecStringTag: + dest.value.vs = new std::vector(*src.value.vs); + break; + default: + dest = src; + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// rdvalue_is + +template +inline bool rdvalue_is(RDValue v) { + return v.getTag() == RDTypeTag::GetTag::type>(); +} + +///////////////////////////////////////////////////////////////////////////////////// +// rdvalue_cast +// +// POD types do not support reference semantics. Other types do. +// rdvalue_cast &>(RDValue); // ok +// rdvalue_cast(RDValue); // bad_any_cast + +#ifdef RDK_32BIT_BUILD + // avoid register pressure and spilling on 32 bit systems + typedef const RDValue& RDValue_cast_t; +#else + typedef RDValue RDValue_cast_t; +#endif + +// Get stuff stored in boost any +template +inline T rdvalue_cast(RDValue_cast_t v) { + // Disable reference and pointer casts to POD data. + BOOST_STATIC_ASSERT( !( + (boost::is_pointer::value && ( + boost::is_integral::type>::value || + boost::is_floating_point::type>::value)) || + (boost::is_reference::value && ( + boost::is_integral::type>::value || + boost::is_floating_point::type>::value)) + )); + + if (rdvalue_is(v)) { + return boost::any_cast(*v.ptrCast()); + } + throw boost::bad_any_cast(); +} + +// POD casts +template<> +inline double rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return v.value.d; + throw boost::bad_any_cast(); +} + +template<> +inline float rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return v.value.f; + throw boost::bad_any_cast(); +} + +template<> +inline int rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return v.value.i; + throw boost::bad_any_cast(); +} +template<> +inline unsigned int rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return v.value.u; + throw boost::bad_any_cast(); +} + +template<> +inline bool rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return v.value.b; + throw boost::bad_any_cast(); +} + + +} // namespace rdkit +#endif + diff --git a/Code/RDGeneral/RDValue.h b/Code/RDGeneral/RDValue.h new file mode 100644 index 000000000..eee3e4223 --- /dev/null +++ b/Code/RDGeneral/RDValue.h @@ -0,0 +1,257 @@ +// Copyright (c) 2015, Novartis Institutes for BioMedical Research Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Novartis Institutes for BioMedical Research Inc. +// nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#ifndef RDKIT_RDVALUE_H +#define RDKIT_RDVALUE_H + +//#define UNSAFE_RDVALUE +#ifdef UNSAFE_RDVALUE +#include "RDValue-doublemagic.h" +#else +#include "RDValue-taggedunion.h" +#endif + +namespace RDKit { +// Common Casts (POD Casts are implementation dependent) +// string casts +template<> +inline std::string rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return *v.ptrCast(); + throw boost::bad_any_cast(); +} + +template<> +inline std::string & rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) return *v.ptrCast(); + throw boost::bad_any_cast(); +} + +// Special Vecor Casts +template<> +inline std::vector rdvalue_cast >(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector &rdvalue_cast &>(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector rdvalue_cast >(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector &rdvalue_cast &>(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector rdvalue_cast >(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector &rdvalue_cast &>(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector rdvalue_cast >(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector &rdvalue_cast &>(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector rdvalue_cast >(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +template<> +inline std::vector &rdvalue_cast &>(RDValue_cast_t v) { + if(rdvalue_is >(v)) + return *v.ptrCast >(); + throw boost::bad_any_cast(); +} + +// Get boost any +template<> +inline boost::any rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) { + return *v.ptrCast(); + } + throw boost::bad_any_cast(); +} + +template<> +inline boost::any &rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) { + return *v.ptrCast(); + } + throw boost::bad_any_cast(); +} + +template<> +inline const boost::any &rdvalue_cast(RDValue_cast_t v) { + if (rdvalue_is(v)) { + return *v.ptrCast(); + } + throw boost::bad_any_cast(); +} + +///////////////////////////////////////////////////////////////////////////////////// +// lexical casts... +template + std::string vectToString(RDValue val) { + const std::vector &tv = rdvalue_cast&>(val); + std::ostringstream sstr; + sstr.imbue(std::locale("C")); + sstr << std::setprecision(17); + sstr << "["; + std::copy(tv.begin(), tv.end(), std::ostream_iterator(sstr, ",")); + sstr << "]"; + return sstr.str(); +} + +inline bool rdvalue_tostring(RDValue_cast_t val, std::string &res) { + Utils::LocaleSwitcher ls; // for lexical cast... + switch (val.getTag()) { + case RDTypeTag::StringTag: + res = rdvalue_cast(val); + break; + case RDTypeTag::IntTag: + res = boost::lexical_cast(rdvalue_cast(val)); + break; + case RDTypeTag::DoubleTag: + res = boost::lexical_cast(rdvalue_cast(val)); + break; + case RDTypeTag::UnsignedIntTag: + res = boost::lexical_cast(rdvalue_cast(val)); + break; +#ifdef RDVALUE_HASBOOL + case RDTypeTag::BoolTag: + res = boost::lexical_cast(rdvalue_cast(val)); + break; +#endif + case RDTypeTag::FloatTag: + res = boost::lexical_cast(rdvalue_cast(val)); + break; + case RDTypeTag::VecDoubleTag: + res = vectToString(val); + break; + case RDTypeTag::VecFloatTag: + res = vectToString(val); + break; + case RDTypeTag::VecIntTag: + res = vectToString(val); + break; + case RDTypeTag::VecUnsignedIntTag: + res = vectToString(val); + break; + case RDTypeTag::VecStringTag: + res = vectToString(val); + break; + case RDTypeTag::AnyTag: + try { + res = boost::any_cast(rdvalue_cast(val)); + } catch (const boost::bad_any_cast &) { + if (rdvalue_cast(val).type() == typeid(long)) { + res = boost::lexical_cast(boost::any_cast( + rdvalue_cast(val))); + } else if (rdvalue_cast(val).type() == typeid(unsigned long)) { + res = + boost::lexical_cast( + boost::any_cast(rdvalue_cast(val))); + } else { + throw; + return false; + } + } + break; + default: + res = ""; + } + return true; +} + +// from_rdvalue -> converts string values to appropriate types +template +typename boost::enable_if, T>::type from_rdvalue( + RDValue_cast_t arg) { + T res; + if (arg.getTag() == RDTypeTag::StringTag) { + Utils::LocaleSwitcher ls; + try { + res = rdvalue_cast(arg); + } catch (const boost::bad_any_cast &exc) { + try { + res = boost::lexical_cast(rdvalue_cast(arg)); + } catch (...) { + throw exc; + } + } + } else { + res = rdvalue_cast(arg); + } + return res; +} + +template +typename boost::disable_if, T>::type from_rdvalue( + RDValue_cast_t arg) { + return rdvalue_cast(arg); +} +} +#endif + + diff --git a/Code/RDGeneral/testDict.cpp b/Code/RDGeneral/testDict.cpp index b5a9906b2..ac6cacc11 100644 --- a/Code/RDGeneral/testDict.cpp +++ b/Code/RDGeneral/testDict.cpp @@ -12,13 +12,317 @@ #include "types.h" #include +#include +#include #include #include +#include +#include #include using namespace RDKit; using namespace std; +struct Foo { + int bar; + float baz; + ~Foo() { std::cerr << "deleted!" << std::endl; } +}; + +void testRDAny() { + std::cerr << "Testing RDValue" << std::endl; + { + RDAny v(-2147450880); + } + + { + int vi = 0; + RDValue v(0); + for (int i=0; i<100; ++i) { + vi += i; + v = rdvalue_cast(v) + i; + TEST_ASSERT(vi == rdvalue_cast(v)); + } + + } + std::cerr << "Testing RDAny" << std::endl; + { + RDAny a(1); + RDAny b = a; + TEST_ASSERT(rdany_cast(a) == 1); + TEST_ASSERT(rdany_cast(b) == 1); + } + + + { + RDAny a(1); + RDAny b = a; + TEST_ASSERT(rdany_cast(a) == 1); + TEST_ASSERT(rdany_cast(b) == rdany_cast(a)); + std::map foo; + foo["foo"] = a; + foo["bar"] = std::string("This is a test"); + TEST_ASSERT(rdany_cast(foo["foo"]) == 1); + TEST_ASSERT(rdany_cast(foo["foo"]) == rdany_cast(a)); + TEST_ASSERT(rdany_cast(foo["bar"]) == "This is a test"); + } + + { + bool a = true; + RDValue v(a); + TEST_ASSERT(rdvalue_cast(v) == true); + v = (int) 10; + TEST_ASSERT(rdvalue_cast(v) == 10); + } + + { + Dict d; + bool a=true; + d.setVal("foo", a); + d.getVal("foo"); + } + + { // tests computed props + STR_VECT computed; + Dict d; + d.setVal(detail::computedPropName, computed); + computed.push_back("foo"); + d.setVal(detail::computedPropName, computed); + STR_VECT computed2 = d.getVal(detail::computedPropName); + TEST_ASSERT(computed2[0] == "foo"); + Dict d2(d); + computed2 = d2.getVal(detail::computedPropName); + TEST_ASSERT(computed2[0] == "foo"); + } + + { + Dict d; + //int v=1; + //d.setVal("foo", v); + //TEST_ASSERT(d.getVal("foo") == 1, "bad getval"); + + std::vector fooV; + fooV.resize(3); + fooV[0] = 1; + fooV[1] = 2; + fooV[2] = 3; + if (0) { + std::vector fooV2; + std::cerr << "send int vect" << std::endl; + RDAny a(fooV); + std::cerr << "retrieve int vect" << std::endl; + fooV2 = rdany_cast >(a); + TEST_ASSERT(fooV == fooV2); + } + + { + std::vector fooV2; + std::cerr << "dict set int vect" << std::endl; + d.setVal("bar", fooV); + std::cerr << "dict get int vect" << std::endl; + d.getVal("bar", fooV2); + TEST_ASSERT(fooV == fooV2); + } + } + + { + std::vector v; + for(int i=0;i<4;++i) + v.push_back(i); + + RDAny foo(v); + RDAny bar = foo; + RDAny baz(foo); + + for(int i=0;i<4;++i) { + TEST_ASSERT(rdany_cast >(foo)[i] == i); + TEST_ASSERT(rdany_cast >(bar)[i] == i); + TEST_ASSERT(rdany_cast >(baz)[i] == i); + } + + } + + { + std::vector v; + for(double i=0;i<4;++i) + v.push_back(i); + + RDAny foo(v); + + for(int i=0;i<4;++i) { + TEST_ASSERT(rdany_cast >(foo)[i] == i); + } + + RDAny b = foo; + + for(int i=0;i<4;++i) { + TEST_ASSERT(rdany_cast >(b)[i] == i); + } + } + const int loops = 10000000; + { + std::clock_t clock1 = std::clock(); + boost::any v; + for(int i=0;i(*v) + i: i); + delete v; + v = vv; + } + delete vv; + std::clock_t clock2 = std::clock(); + + std::cout << "dynamic boost any:" << (double)(clock2-clock1)/CLOCKS_PER_SEC << " s" << std::endl; + } + + { + std::clock_t clock1 = std::clock(); + RDAny v; + for(int i=0;i(*v) + i : i); + delete v; + v = vv; + } + delete vv; + std::clock_t clock2 = std::clock(); + + std::cout << "dynamic RDAny:" << (double)(clock2-clock1)/CLOCKS_PER_SEC << " s" << std::endl; + } + + { + std::clock_t clock1 = std::clock(); + RDValue v; + for(int i=0;i(v) + i); + } + + std::clock_t clock2 = std::clock(); + + std::cout << "dynamic RDValue:" << (double)(clock2-clock1)/CLOCKS_PER_SEC << " s" << std::endl; + } + + { // checks replacement with vector + RDAny vv(2.0); + TEST_ASSERT(rdany_cast(vv) == 2.0); + + + std::vector vect; + vect.push_back(1); + vv = vect; + TEST_ASSERT(rdany_cast >(vv)[0] == 1); + + // tests copy + RDAny vvv(vv); + + TEST_ASSERT(rdany_cast >(vvv)[0] == 1); + } + + { + // Checks fallback to Any + std::vector > pvect; + pvect.push_back(std::make_pair(2,2)); + boost::any any1(pvect); + boost::any_cast > >(any1); + boost::any_cast > &>(any1); + boost::any_cast > &>(any1); + + RDAny vv(pvect); + boost::any &any = rdany_cast(vv); + boost::any_cast > >(any); + boost::any_cast > &>(any); + boost::any_cast > &>(any); + + const std::vector > &pv = rdany_cast > >(vv); + TEST_ASSERT(pv[0].first == 2); + RDAny vvv(vv); + TEST_ASSERT((rdany_cast > >(vvv)[0].first == + 2)); + + } + + { + // Check pointers -- RDAny doesn't delete these, must do them manually + std::vector *p = new std::vector(); + p->push_back(100); + RDAny v(p); + RDAny vv(v); + try { + rdany_cast >(v); +#ifndef UNSAFE_RDVALUE + PRECONDITION(0, "Should throw bad cast"); +#endif + } catch (boost::bad_any_cast &e) { + } + + TEST_ASSERT((*rdany_cast *>(vv))[0] == 100); + TEST_ASSERT((*rdany_cast *>((const RDAny&)vv))[0] == 100); + delete p; + + std::map *m = new std::map(); + (*m)[0] = 1; + RDAny mv(m); + // leaks + std::map *anym = rdany_cast *>(mv); + TEST_ASSERT(anym->find(0) != anym->end()); + delete anym; + } + + { + // check shared ptrs -- boost::any deletes these :) + typedef boost::shared_ptr > vptr; + vptr p(new std::vector()); + p->push_back(100); + RDAny v(p); + RDAny vv(v); + TEST_ASSERT((*rdany_cast(v))[0] == 100); + TEST_ASSERT((*rdany_cast(vv))[0] == 100); + TEST_ASSERT((*rdany_cast((const RDAny&)vv))[0] == 100); + + typedef boost::shared_ptr > mptr; + mptr m(new std::map()); + (*m)[0] = 1; + RDAny mv(m); + // leaks + mptr anym = rdany_cast(mv); + TEST_ASSERT(anym->find(0) != anym->end()); + + RDAny any3(boost::shared_ptr( new Foo )); + TEST_ASSERT(any3.m_value.getTag() == RDTypeTag::AnyTag); + } +} + + class DictCon { public: DictCon() { d.reset(); }; @@ -91,18 +395,26 @@ void testVectToString() { { Dict d; std::vector v; - v.push_back(1); + v.push_back(1.2); v.push_back(0); d.setVal("foo", v); - bool ok = false; - try { - std::string sv; - d.getVal("foo", sv); - } catch (const boost::bad_any_cast &) { - ok = true; - } - TEST_ASSERT(ok); + std::string sv; + d.getVal("foo", sv); + std::cerr << sv << std::endl; + TEST_ASSERT(sv == "[1.2,0,]"); } + { + Dict d; + std::vector v; + v.push_back(10001.f); + v.push_back(0); + d.setVal("foo", v); + std::string sv; + d.getVal("foo", sv); + std::cerr << sv << std::endl; + TEST_ASSERT(sv == "[10001,0,]"); + } + BOOST_LOG(rdErrorLog) << "\tdone" << std::endl; } @@ -111,10 +423,10 @@ void testConstReturns() { BOOST_LOG(rdErrorLog) << "Testing returning const references." << std::endl; { std::string v = "foo"; - boost::any anyv(v); + RDAny anyv(v); - std::string tgt = boost::any_cast(anyv); - const std::string &ctgt = boost::any_cast(anyv); + std::string tgt = rdany_cast(anyv); + const std::string &ctgt = rdany_cast(anyv); TEST_ASSERT(ctgt != ""); } @@ -144,7 +456,7 @@ void testConstReturns() { ls=0; BOOST_LOG(rdErrorLog) << "ref" << std::endl; for(int i=0;i<100000000;++i){ - const std::string &nv=d.getVal("foo"); + const std::string &nv=d.getVal("foo"); ls+= nv.size(); } BOOST_LOG(rdErrorLog) << "done: "<(anyv); + const std::string &nv = rdany_cast(anyv); ls += nv.size(); } end = std::clock(); @@ -177,7 +489,7 @@ void testConstReturns() { BOOST_LOG(rdErrorLog) << "copy" << std::endl; start = std::clock(); for (int i = 0; i < nreps; ++i) { - std::string nv = d.fromany(anyv); + std::string nv = rdany_cast(anyv); ls += nv.size(); } end = std::clock(); @@ -189,7 +501,7 @@ void testConstReturns() { BOOST_LOG(rdErrorLog) << "ref" << std::endl; start = std::clock(); for (int i = 0; i < nreps; ++i) { - const std::string &nv = d.fromany(anyv); + const std::string &nv = rdany_cast(anyv); ls += nv.size(); } end = std::clock(); @@ -201,7 +513,7 @@ void testConstReturns() { BOOST_LOG(rdErrorLog) << "dict" << std::endl; start = std::clock(); for (int i = 0; i < nreps; ++i) { - const std::string &nv = d.getVal("foo"); + const std::string &nv = d.getVal("foo"); ls += nv.size(); } end = std::clock(); @@ -215,7 +527,7 @@ void testConstReturns() { std::string k = "foo"; for (int i = 0; i < nreps; ++i) { if (d.hasVal(k)) { - const std::string &nv = d.fromany(anyv); + const std::string &nv = rdany_cast(anyv); ls += nv.size(); } } @@ -234,93 +546,95 @@ void testConstReturns() { int main() { RDLog::InitLogs(); + testRDAny(); + #if 1 Dict d; INT_VECT fooV; fooV.resize(3); BOOST_LOG(rdInfoLog) << "dict test" << std::endl; - CHECK_INVARIANT(!d.hasVal("foo"), "bad init"); + TEST_ASSERT(!d.hasVal("foo")); int x = 1; d.setVal("foo", x); - CHECK_INVARIANT(d.hasVal("foo"), "should be there"); - CHECK_INVARIANT(!d.hasVal("bar"), "bad other key"); + TEST_ASSERT(d.hasVal("foo")); + TEST_ASSERT(!d.hasVal("bar")); int v, v2; d.getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); v2 = d.getVal("foo"); - CHECK_INVARIANT(v2 == v, "bad val"); + TEST_ASSERT(v2 == v); d.setVal("bar", fooV); d.getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); v2 = d.getVal("foo"); - CHECK_INVARIANT(v2 == v, "bad val"); + TEST_ASSERT(v2 == v); INT_VECT fooV2, fooV3; d.getVal("bar", fooV2); fooV3 = d.getVal("bar"); - CHECK_INVARIANT(fooV == fooV2, "bad getVal"); - CHECK_INVARIANT(fooV2 == fooV3, "bad getVal"); + TEST_ASSERT(fooV == fooV2); + TEST_ASSERT(fooV2 == fooV3); VECT_INT_VECT fooV4; fooV4.resize(3); - CHECK_INVARIANT(!d.hasVal("baz"), "bad get"); + TEST_ASSERT(!d.hasVal("baz")); d.setVal("baz", fooV4); - CHECK_INVARIANT(d.hasVal("baz"), "bad get"); + TEST_ASSERT(d.hasVal("baz")); DictCon dc1; - CHECK_INVARIANT(!dc1.getDict()->hasVal("foo"), "bad init"); + TEST_ASSERT(!dc1.getDict()->hasVal("foo")); int y = 1; dc1.getDict()->setVal("foo", y); - CHECK_INVARIANT(dc1.getDict()->hasVal("foo"), "should be there"); - CHECK_INVARIANT(!dc1.getDict()->hasVal("bar"), "bad other key"); + TEST_ASSERT(dc1.getDict()->hasVal("foo")); + TEST_ASSERT(!dc1.getDict()->hasVal("bar")); dc1.getDict()->setVal("bar", fooV); dc1.getDict()->getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); dc1.getDict()->getVal("bar", fooV2); - CHECK_INVARIANT(fooV == fooV2, "bad getVal"); + TEST_ASSERT(fooV == fooV2); fooV4.resize(3); - CHECK_INVARIANT(!dc1.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(!dc1.getDict()->hasVal("baz")); dc1.getDict()->setVal("baz", fooV4); - CHECK_INVARIANT(dc1.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(dc1.getDict()->hasVal("baz")); dc1.getDict()->reset(); DictCon dc2 = dc1; - CHECK_INVARIANT(!dc2.getDict()->hasVal("foo"), "bad init"); + TEST_ASSERT(!dc2.getDict()->hasVal("foo")); int z = 1; dc2.getDict()->setVal("foo", z); - CHECK_INVARIANT(dc2.getDict()->hasVal("foo"), "should be there"); - CHECK_INVARIANT(!dc2.getDict()->hasVal("bar"), "bad other key"); + TEST_ASSERT(dc2.getDict()->hasVal("foo")); + TEST_ASSERT(!dc2.getDict()->hasVal("bar")); dc2.getDict()->setVal("bar", fooV); dc2.getDict()->getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); dc2.getDict()->getVal("bar", fooV2); - CHECK_INVARIANT(fooV == fooV2, "bad getVal"); + TEST_ASSERT(fooV == fooV2); fooV4.resize(3); - CHECK_INVARIANT(!dc2.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(!dc2.getDict()->hasVal("baz")); dc2.getDict()->setVal("baz", fooV4); - CHECK_INVARIANT(dc2.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(dc2.getDict()->hasVal("baz")); DictCon dc3(dc2); - CHECK_INVARIANT(dc3.getDict()->hasVal("foo"), "should be there"); + TEST_ASSERT(dc3.getDict()->hasVal("foo")); dc3.getDict()->getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); dc3.getDict()->getVal("bar", fooV2); - CHECK_INVARIANT(fooV == fooV2, "bad getVal"); + TEST_ASSERT(fooV == fooV2); fooV4.resize(3); - CHECK_INVARIANT(dc3.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(dc3.getDict()->hasVal("baz")); - CHECK_INVARIANT(dc3.getDict()->hasVal("foo"), "should be there"); + TEST_ASSERT(dc3.getDict()->hasVal("foo")); dc3.getDict()->getVal("foo", v); - CHECK_INVARIANT(v == 1, "bad val"); + TEST_ASSERT(v == 1); dc3.getDict()->getVal("bar", fooV2); - CHECK_INVARIANT(fooV == fooV2, "bad getVal"); + TEST_ASSERT(fooV == fooV2); fooV4.resize(3); - CHECK_INVARIANT(dc3.getDict()->hasVal("baz"), "bad get"); + TEST_ASSERT(dc3.getDict()->hasVal("baz")); testStringVals(); testVectToString(); #endif testConstReturns(); - + return 0; } diff --git a/Code/RDGeneral/testRDValue.cpp b/Code/RDGeneral/testRDValue.cpp new file mode 100644 index 000000000..bed611094 --- /dev/null +++ b/Code/RDGeneral/testRDValue.cpp @@ -0,0 +1,148 @@ +#include "RDValue.h" +#include "RDProps.h" +#include "Invariant.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace RDKit; + +template +void testLimits() { + // check numeric limits + { + RDValue v(std::numeric_limits::min()); + std::cerr << "min: " << std::numeric_limits::min() << " " << rdvalue_cast(v) << + std::endl; + CHECK_INVARIANT(rdvalue_cast(v) == std::numeric_limits::min(), "bad min"); + CHECK_INVARIANT(rdvalue_cast(RDValue(v)) == std::numeric_limits::min(), "bad min"); + v = std::numeric_limits::max(); + CHECK_INVARIANT(rdvalue_cast(v) == std::numeric_limits::max(), "bad max"); + CHECK_INVARIANT(rdvalue_cast(RDValue(v)) == std::numeric_limits::max(), "bad max"); + + } + { + RDValue v(std::numeric_limits::max()); + CHECK_INVARIANT(rdvalue_cast(v) == std::numeric_limits::max(), "bad max"); + RDValue vv(v); + CHECK_INVARIANT(rdvalue_cast(vv) == std::numeric_limits::max(), "bad max"); + + v = std::numeric_limits::min(); + RDValue vvv(v); + CHECK_INVARIANT(rdvalue_cast(v) == std::numeric_limits::min(), "bad min"); + CHECK_INVARIANT(rdvalue_cast(vvv) == std::numeric_limits::min(), "bad min"); + } +} + +void testPOD() { + testLimits(); + testLimits(); + testLimits(); + testLimits(); + testLimits(); +} + + + +template +void testVector() { + T minv = std::numeric_limits::min(); + T maxv = std::numeric_limits::max(); + std::vector data; + data.push_back(minv); + data.push_back(maxv); + data.push_back(T()); + + RDValue v(data); + CHECK_INVARIANT(rdvalue_cast >(v) == data, "bad vec"); + RDValue vv; copy_rdvalue(vv,v); + CHECK_INVARIANT(rdvalue_cast >(vv) == data, "bad copy constructor"); + RDValue::cleanup_rdvalue(v); // desctructor... + RDValue::cleanup_rdvalue(vv); +} + +void testPODVectors() { + testVector(); + testVector(); + testVector(); + testVector(); + testVector(); // stored in anys +} + +void testStringVect() { + std::vector vecs; + vecs.push_back("my"); + vecs.push_back("dog"); + vecs.push_back("has"); + vecs.push_back("fleas"); + RDValue v(vecs); + CHECK_INVARIANT(rdvalue_cast >(v) == vecs, "bad vect"); + RDValue vc; copy_rdvalue(vc, v); + CHECK_INVARIANT(rdvalue_cast >(vc) == vecs, "bad vect"); + RDValue vv = vecs; + RDValue vvc; copy_rdvalue(vvc, vv); + CHECK_INVARIANT(rdvalue_cast >(vv) == vecs, "bad vect"); + CHECK_INVARIANT(rdvalue_cast >(vvc) == vecs, "bad vect"); + + RDValue::cleanup_rdvalue(v); // desctructor... + RDValue::cleanup_rdvalue(vc); // desctructor... + RDValue::cleanup_rdvalue(vv); // desctructor... + RDValue::cleanup_rdvalue(vvc); // desctructor... +} + +void testMapsAndLists() { + { + typedef std::map listtype; + listtype m; + m["foo"] = 1; + m["bar"] = 2; + RDValue v(m); + CHECK_INVARIANT(rdvalue_cast(v) == m, "bad map cast"); + RDValue::cleanup_rdvalue(v); + } + { + std::list m; + m.push_back("foo"); + m.push_back("bar"); + RDValue v(m); + CHECK_INVARIANT(rdvalue_cast >(v) == m, "bad map cast"); + RDValue::cleanup_rdvalue(v); + } +} + +void testNaN() { + // make a NaN + double nan=sqrt(-1.0); + RDValue v(nan); + TEST_ASSERT(v.getTag() == RDTypeTag::DoubleTag); + + CHECK_INVARIANT(boost::math::isnan(rdvalue_cast(v)), "Oops, can't store NaNs!"); + + RDValue vv(2.0); + TEST_ASSERT(rdvalue_is(vv)); + TEST_ASSERT(vv.getTag() == RDTypeTag::DoubleTag); +} + +template +std::vector makeVec() { + std::vector vec; + vec.push_back((T)0); + vec.push_back((T)1); + vec.push_back((T)2); + vec.push_back((T)3); + vec.push_back((T)4); + vec.push_back((T)5); + return vec; +} + +int main() { + std::cerr << "-- running tests -- " << std::endl; + testPOD(); + testPODVectors(); + testStringVect(); + testNaN(); +} diff --git a/Code/RDGeneral/types.cpp b/Code/RDGeneral/types.cpp index 322c4ae4e..46f62c47c 100644 --- a/Code/RDGeneral/types.cpp +++ b/Code/RDGeneral/types.cpp @@ -48,6 +48,7 @@ const std::string _SmilesStart = "_SmilesStart"; const std::string _StereochemDone = "_StereochemDone"; const std::string _TraversalBondIndexOrder = "_TraversalBondIndexOrder"; const std::string _TraversalRingClosureBond = "_TraversalRingClosureBond"; +const std::string _TraversalStartPoint = "_TraversalStartPoint"; const std::string _TriposAtomType = "_TriposAtomType"; const std::string _Unfinished_SLN_ = "_Unfinished_SLN_"; const std::string _UnknownStereo = "_UnknownStereo"; diff --git a/Code/RDGeneral/types.h b/Code/RDGeneral/types.h index 70348bfe1..e36df715f 100644 --- a/Code/RDGeneral/types.h +++ b/Code/RDGeneral/types.h @@ -17,7 +17,7 @@ #include -#include +#include "Invariant.h" #include "Dict.h" namespace detail { @@ -42,84 +42,125 @@ const std::string computedPropName = "__computedProps"; namespace RDKit { namespace common_properties { -extern const std::string TWOD; -extern const std::string BalabanJ; -extern const std::string BalanbanJ; -extern const std::string Discrims; -extern const std::string DistanceMatrix_Paths; -extern const std::string MolFileComments; -extern const std::string MolFileInfo; -extern const std::string NullBond; -extern const std::string _2DConf; -extern const std::string _3DConf; -extern const std::string _AtomID; -extern const std::string _BondsPotentialStereo; -extern const std::string _CIPCode; -extern const std::string _CIPRank; -extern const std::string _ChiralityPossible; -extern const std::string _CrippenLogP; -extern const std::string _CrippenMR; -extern const std::string _MMFFSanitized; -extern const std::string _MolFileChiralFlag; -extern const std::string _MolFileRLabel; -extern const std::string _Name; -extern const std::string _NeedsQueryScan; -extern const std::string _QueryFormalCharge; -extern const std::string _QueryHCount; -extern const std::string _QueryIsotope; -extern const std::string _QueryMass; -extern const std::string _ReactionDegreeChanged; -extern const std::string _RingClosures; -extern const std::string _SLN_s; -extern const std::string _SmilesStart; -extern const std::string _StereochemDone; -extern const std::string _TraversalBondIndexOrder; -extern const std::string _TraversalRingClosureBond; -extern const std::string _TriposAtomType; -extern const std::string _Unfinished_SLN_; -extern const std::string _UnknownStereo; -extern const std::string _connectivityHKDeltas; -extern const std::string _connectivityNVals; -extern const std::string _crippenLogP; -extern const std::string _crippenLogPContribs; -extern const std::string _crippenMR; -extern const std::string _crippenMRContribs; -extern const std::string _doIsoSmiles; -extern const std::string _fragSMARTS; -extern const std::string _hasMassQuery; -extern const std::string _labuteASA; -extern const std::string _labuteAtomContribs; -extern const std::string _labuteAtomHContrib; -extern const std::string _protected; -extern const std::string _queryRootAtom; -extern const std::string _ringStereoAtoms; -extern const std::string _ringStereoWarning; -extern const std::string _ringStereochemCand; -extern const std::string _smilesAtomOutputOrder; -extern const std::string _starred; -extern const std::string _supplementalSmilesLabel; -extern const std::string _tpsa; -extern const std::string _tpsaAtomContribs; -extern const std::string _unspecifiedOrder; -extern const std::string _brokenChirality; +/////////////////////////////////////////////////////////////// +// Molecule Props +extern const std::string _Name; // string +extern const std::string MolFileInfo; // string +extern const std::string MolFileComments; // string +extern const std::string _2DConf; // int (combine into dimension?) +extern const std::string _3DConf; // int +extern const std::string _doIsoSmiles; // int (should probably be removed) +extern const std::string extraRings; // vec > +extern const std::string _smilesAtomOutputOrder; // vec computed +extern const std::string _StereochemDone; // int +extern const std::string _NeedsQueryScan; // int (bool) +extern const std::string _fragSMARTS; // std::string +extern const std::string maxAttachIdx; // int TemplEnumTools.cpp +extern const std::string origNoImplicit; // int (bool) +extern const std::string ringMembership; //? unused (molopstest.cpp) + +// Computed Values +// ConnectivityDescriptors +extern const std::string _connectivityHKDeltas;// std::vector computed +extern const std::string _connectivityNVals; // std::vector computed + +extern const std::string _crippenLogP; // double computed +extern const std::string _crippenLogPContribs; // std::vector computed + +extern const std::string _crippenMR; // double computed +extern const std::string _crippenMRContribs; // std::vector computed + +extern const std::string _labuteASA; // double computed +extern const std::string _labuteAtomContribs; // vec computed +extern const std::string _labuteAtomHContrib; // double computed + +extern const std::string _tpsa; // double computed +extern const std::string _tpsaAtomContribs; // vec computed + +extern const std::string numArom; // int computed (only uses in tests?) +extern const std::string _MMFFSanitized; // int (bool) computed + +extern const std::string _CrippenLogP; // Unused (in the basement) +extern const std::string _CrippenMR; // Unused (in the basement) + +/////////////////////////////////////////////////////////////// +// Atom Props + +// Chirality stuff +extern const std::string _BondsPotentialStereo; // int (or bool) COMPUTED +extern const std::string _CIPCode; // std::string COMPUTED +extern const std::string _CIPRank; // int COMPUTED +extern const std::string _ChiralityPossible; // int +extern const std::string _UnknownStereo; // int (bool) AddHs/Chirality +extern const std::string _ringStereoAtoms; // int vect Canon/Chiral/MolHash/MolOps//Renumber//RWmol +extern const std::string _ringStereochemCand; // chirality bool COMPUTED +extern const std::string _ringStereoWarning; // obsolete ? + +// Smiles parsing +extern const std::string _SmilesStart; // int +extern const std::string _TraversalBondIndexOrder; // ? unused +extern const std::string _TraversalRingClosureBond; // unsigned int +extern const std::string _TraversalStartPoint; // bool +extern const std::string _queryRootAtom; // int SLNParse/SubstructMatch +extern const std::string _hasMassQuery; // atom bool +extern const std::string _protected; // atom int (bool) +extern const std::string _supplementalSmilesLabel; // atom string (SmilesWrite) +extern const std::string _unspecifiedOrder;// atom int (bool) smarts/smiles +extern const std::string _RingClosures; // INT_VECT smarts/smiles/canon + +// MDL Style Properties (MolFileParser) +extern const std::string molAtomMapNumber; // int +extern const std::string molFileAlias; // string +extern const std::string molFileValue; // string +extern const std::string molInversionFlag; // int +extern const std::string molParity; // int +extern const std::string molRxnComponent; // int +extern const std::string molRxnRole; // int +extern const std::string molTotValence; // int +extern const std::string _MolFileRLabel; // int +extern const std::string _MolFileChiralFlag; // int + +extern const std::string dummyLabel; // atom string + +// Reaction Information (Reactions.cpp) +extern const std::string _QueryFormalCharge; // int +extern const std::string _QueryHCount; // int +extern const std::string _QueryIsotope; // int +extern const std::string _QueryMass; // int = round(float * 1000) +extern const std::string _ReactionDegreeChanged; // int (bool) +extern const std::string NullBond; // int (bool) extern const std::string _rgroupAtomMaps; extern const std::string _rgroupBonds; -extern const std::string dummyLabel; -extern const std::string extraRings; -extern const std::string isImplicit; -extern const std::string maxAttachIdx; -extern const std::string molAtomMapNumber; -extern const std::string molFileAlias; -extern const std::string molFileValue; -extern const std::string molInversionFlag; -extern const std::string molParity; -extern const std::string molRxnComponent; -extern const std::string molRxnRole; -extern const std::string molTotValence; -extern const std::string numArom; -extern const std::string origNoImplicit; -extern const std::string ringMembership; -extern const std::string smilesSymbol; + +// SLN +extern const std::string _AtomID; // unsigned int SLNParser +extern const std::string _starred; // atom int COMPUTED (SLN) +extern const std::string _SLN_s; // string SLNAttribs (chiral info) +extern const std::string _Unfinished_SLN_; // int (bool) + +// Smarts Smiles +extern const std::string _brokenChirality; // atom bool +extern const std::string isImplicit; // atom int (bool) +extern const std::string smilesSymbol; // atom string (only used in test?) + +// Tripos +extern const std::string _TriposAtomType; // string Mol2FileParser +// missing defs for _TriposAtomName//_TriposPartialCharge... + + +/////////////////////////////////////////////////////////////// +// misc props +extern const std::string TWOD; // need THREED -> confusing using in TDTMol supplier + // converge with _2DConf? +extern const std::string BalabanJ; // mol double +extern const std::string BalanbanJ; // typo!! fix... + +extern const std::string Discrims; // FragCatalog Entry + // Subgraphs::DiscrimTuple (uint32,uint32,uint32) +extern const std::string DistanceMatrix_Paths; // boost::shared_array + // - note, confusing creation of names in + // - getDistanceMat + } // end common_properties #ifndef WIN32 typedef long long int LONGINT;