/** * @file * @author Caleb Fangmeier * @version 0.1 * * @section LICENSE * * * MIT License * * Copyright (c) 2017 Caleb Fangmeier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * @section DESCRIPTION * This header defines a set of generic classes that wrap up "values". In * essence, a Value object is just something that contains a value of type T * and can provide it when requested. The usefulness stems from composing * values together with calculations. This enables very clear dependency * mapping and a way to know clearly how every value was arrived at. This could * be used to, for example, automatically generate commentary for plots that * explain the exect calculation used to create it. Or easily making a series * of plots contrasting different values that have been composed slightly * differently. */ #ifndef value_hpp #define value_hpp #include #include #include #include #include #include #include #include #include #include #include #include #include "log.hpp" #include "config.hpp" /** * The namespace containing all filval classes and functions. */ namespace fv { template class Value; /** * A type-agnostic value. * It is necessary to create a type-agnostic parent class to Value so that * it is possible to handle collections of them. GenValue also provides the * rest of the type-independent interface to Value. */ class GenValue; typedef std::map ValueSet; class GenValue { private: /** * The name of the value. * This is used to allow for dynamic lookup of * values based on their name via GenValue::get_value. */ std::string name; protected: /** * Mark the internal value as invalid. This is needed for DerivedValue * to force a recalculation of the internal value when a new * observation is loaded into memory. It is called automatically for * all GenValue objects when reset is called. */ bool value_valid; void _reset() { this->value_valid = false; } /** * A static mapping containing all created Value objects. * Every value object must have a unique name, and this name is used as * a key in values to that object. This is used to enable more dynamic * creation of objects as well as avoiding the uneccesary passing of * pointers. */ inline static std::map, GenValue *> values; /** * Composite value names are typically nested. This makes complex * values have rather unwieldy names. Therefore, one can declare * aliases which allow for more human-usable names to be used. When a * value is requested by name, an alias with that value takes precidence * over a name with that value. */ inline static std::map, GenValue *> aliases; public: GenValue(const std::type_index &&ti, const std::string &name, const std::string &alias) : name(name), value_valid(false) { if (alias != "") INFO("Registered value: \"" << name << "\" with alias: \"" << alias << "\""); else INFO("Registered value: \"" << name); values[std::make_pair(ti, name)] = this; if (alias != "") GenValue::alias(ti, alias, this); } const std::string &get_name() { return name; } static void reset() { for (auto val : values) { if (val.second != nullptr) { val.second->_reset(); } } } template static Value *get_value(const std::string &name) { const std::type_index &ti = typeid(T); auto lookup_id = std::make_pair(ti, name); if (aliases[lookup_id] != nullptr) return (Value *) aliases[lookup_id]; else return (Value *) values[lookup_id]; } static void alias(const std::type_index &ti, const std::string &name, GenValue *value) { auto lookup_id = std::make_pair(ti, name); if (aliases[lookup_id] != nullptr) { WARNING("WARNING: alias \"" << name << "\" overrides previous entry."); } aliases[lookup_id] = value; } template static void alias(const std::string &name, Value *value) { alias(typeid(T), name, value); } static std::string summary() { std::stringstream ss; ss << "The following values have been created:" << std::endl; for (auto item : values) { auto &key = item.first; auto &value = item.second; if (value == nullptr) continue; ss << "\tVALUE::\"" << key.second << "\" at address " << value << std::endl; } ss << "And these aliases:" << std::endl; for (auto item : aliases) { auto &key = item.first; auto &value = item.second; std::string orig("VOID"); if (value == nullptr) continue; for (auto v_item : values) { auto &v_value = v_item.second; if (v_value == value) { orig = v_value->get_name(); break; } } ss << "\tALIAS::\"" << key.second << "\" referring to \"" << orig << "\"" << std::endl; } return ss.str(); } friend std::ostream &operator<<(std::ostream &os, const GenValue &gv); }; std::ostream &operator<<(std::ostream &os, GenValue &gv) { os << gv.get_name(); return os; } /** * A templated value. * In order to facilitate run-time creation of analysis routines, it is * necessary to have some ability to get and store *values*. Values can either * be directly taken from some original data source (i.e. ObservedValue), or * they can be a function of some other set of values (i.e. DerivedValue). They * template class T of Value is the type of thing that is returned upon * calling get_value(). */ template class Value : public GenValue { public: Value(const std::string &name, const std::string &alias = "") : GenValue(typeid(T), name, alias) {} /** Calculate, if necessary, and return the value held by this object. */ virtual T &operator() () = 0; }; /** * A value supplied by the dataset, not derived. * An ObservedValue is the interface to your dataset. Upon creation, an * ObservedValue is given a pointer to an object of type T. When an observation * is loaded into memory, the value at the location referenced by that pointer * must be updated with the associated data from that observation. This is the * responsibility of whatever DataSet implementation is being used. This object * then will read that data and return it when requested. */ template class ObservedValue : public Value { private: T *val_ref; public: ObservedValue(const std::string &name, T *val_ref, const std::string &alias = "") : Value(name, alias), val_ref(val_ref) {} static std::string fmt_name(const std::string &name) { return name; } T &operator() (){ if (!this->value_valid) { if (fv_util::debug_on) { std::cout << "Calculating Value: " << this->get_name() << std::endl; } this->value_valid = true; } return *val_ref; } }; /** * A Value derived from some other Values, not directly from the dataset. * A DerivedValue is generally defined as some function of other Value objects. * For example, a Pair is a function of two other Value objects that makes a * pair of them. Note that these other Value objects are free to be either * ObservedValues or other DerivedValues. * * It is desireable from a performance standpoint that each DerivedValue be * calculated no more than once per observation. Therefore, when a get_value is * called on a DerivedValue, it first checks whether the value that it holds is * **valid**, meaning it has already been calculated for this observation. If * so, it simply returns the value. If not, the update_value function is called * to calculate the value. and then the newly calculated value is marked as * valid and returned. */ template class DerivedValue : public Value { protected: T value; /** * Updates the internal value. * This function should be overridden by any child class to do the * actual work of updating value based on whatever rules the class * chooses. Normally, this consists of geting the values from some * associated Value objects, doing some calculation on them, and * storing the result in value. */ virtual void update_value() = 0; public: DerivedValue(const std::string &name, const std::string &alias = "") : Value(name, alias) {} T &operator() () { if (!this->value_valid) { if (fv_util::debug_on) { std::cout << "Calculating Value: " << this->get_name() << std::endl; } update_value(); this->value_valid = true; } return value; } }; /** * A Value of a pointer. The pointer is constant, however the data the pointer * points to is variable. */ template class PointerValue : public DerivedValue { protected: void update_value() {} public: PointerValue(const std::string &name, T *ptr, const std::string alias = "") : DerivedValue(name, alias) { this->value = ptr; } }; /** * Used for when the value is an object whose location in memory is changing, * but one has a pointer to a pointer that points to the always updated * location. (Mainly when reading objects such as stl containers from a root * TTree) */ template class ObjectValue : public DerivedValue { protected: T **obj_pointer; void update_value() { this->value = **obj_pointer; } public: ObjectValue(const std::string &name, T **ptr, const std::string alias = "") : DerivedValue(name, alias), obj_pointer(ptr) {} }; /** * A Value which always returns the same value, supplied in the constructor. */ template class ConstantValue : public DerivedValue { protected: void update_value() {} public: static std::string fmt_name(const std::string &name) { return "const::" + name; } ConstantValue(const std::string &name, T const_value, const std::string alias = "") : DerivedValue(fmt_name(name), alias) { this->value = const_value; } }; } #endif // value_hpp