#ifndef _utl_ConsecutiveEnumFactory_h_ #define _utl_ConsecutiveEnumFactory_h_ #include #include #include namespace utl { /** * \class ConsecutiveEnumFactory ConsecutiveEnumFactory.h "utl/ConsecutiveEnumFactory.h" * \brief Simple factory to create to some enumerator for a given * enumeration. * * Provides two alternatives for creation, via a int or * via an std::string, both under the form of an overloaded method * calle Create. * * As implied by the name, it's required (but no actually checked by * some language construct) that the enumeration is defined with * consecutive values. * Also, the fact that \p EnumType is actually an enum isn't checked. * The behavior under error (i.e. no enumerator with the given value) * is defined by \p NoConversionPolicy. * The implied condition First <= Last isn't checked either. * This method is mainly useful in terms of I/0 of enumerators. * * The \p Tags is expected to have a length equal to the number of * enumerators (as implied by \p First and \p Last) and to be * in correspondence with the order defined in the enumertion. That * is \p Tags[i] contains the label for the enumerator with First + i * integral value. * * It may be convinent for clients to define a typedef as an alias * for the particular needed instantiation of this class. * * For a given enumerator type there should be a single instantiation * of the template class. * * \param EnumType The actual enumeration type. * \param Last The greatest enumerator. * \param Tags Labels for each of the enumerators. * \param NoConversionPolicy Policy type for no conversion. It's supposed * to define a method compatible with the following signature * \p EnumType Handle(const std::string&). * This argument defaults to an exception throwing policy (utl::ThrowPolicy) * , with exception type utl::IOFailureException. * Via the allowed return value of Handle the user can provide an * error-condition value. * \param First The smallest enumerator. Defaults to zero (expicitly * converted to the \p EnumType). * * This class is related, is some way, to utl::ObjectFactory but this last * class is much more complex (and useful) than the one here defined. * * \todo Better diagnosis based on the actual enumeration type name. * * \todo Maybe is useful to default the tags to null, and so handle properly * the situation. * * \todo Telescope::CachePixelCalibrations() handles a situation like * this. It seems better to avoid that style. * - It seems better to have the convertion be defined "next to" the list of * enumerators,and avoid client code (Telescope in this case) to explicitly * "know" the full list. Anyway in this case there's a pretty tight relation * between both classes. * - Declares a double to be filled by GetPixelData which is the usual * templated proxy function for managers. I don't see why not an int? * - Then C-style casts the double value to int. By the way, it does so * in the ugly way "(int)value" instead of "int(value)". See Offline's * style guide for C++ code. * - Then does an in-place exhaustive switch-case over the enumeration. * - Has a default to unkown that allows "trash" in the configuration * data (when there's also a precise value for unknown). * * \todo For compile-time checking consider the use of Boost library, in * particular boost/type_traits/is_enum.hpp and boost/concept_check.hpp. By now * it doesn't seem worth of it (mainly due that as of Boost v1.34.1 is_enum is * broken under some compilers). * * \todo Can the case of non-consecutive values be handled (without passing * as a template argument an array with the values)?. In this case of passing * the values via an another arg, it seems that it cannot * be used to determine the case: one can think (at least I did) that this * new argument can defaulted to null, and then if the user doesn't supply * it an special implementation (assuming consecutive values) can be * instantiated thanks to a class/struct specialization for null pointers. * The problems seems to be item 5 paragraph 2 of section 14.3.2 (Template * non-type arguments) of the ISO/IEC 14882 C++ Standard where it's said: * "Although 0 is a valid template-argument for a non-type template-parameter * of integral type, it is not a valid template-argument for a non-type * template-parameter of pointer type". * In any case, it seems little risky to allow a default parameter, given * thet its omission would imply the asumption of consecutiveness. So it'd * be better to code a separate class with the following template signature: * template< * typename EnumType, * unsigned int NumEnum, * const EnumType Values[], * const char * const Tags[], * class NoConversionPolicy = ThrowPolicy, * > * * \author Rodolfo Federico Gamarra * \date 10 Jan 2009 * \version $Id$ * \ingroup stl */ template< typename EnumType, EnumType Last, const char* const Tags[], class NoConversionPolicy = ThrowPolicy, EnumType First = static_cast(0) > class ConsecutiveEnumFactory { /// Error handling template static EnumType Handle(const T& t) { std::ostringstream os; os << "Cannot convert " << t << " to any of the valid enumerators:"; for (unsigned int i = 0; i < kNumEnum; ++i) { os << Tags[i] << ' '; } // there's the usual last separator traling. os << "(with values from " << First << " to " << Last << ")." << std::endl; return NoConversionPolicy::Handle(os.str()); } public: /** * \brief Number of enumerators. * Calculated based on the fact that are consecutive. */ const static unsigned int kNumEnum = Last - First + 1; /// int version of the overloaded creation method. static EnumType Create(const int k) { if (First <= k && k <= Last) return static_cast(k); return Handle(k); } /// std::string version of the overloaded creation method. static EnumType Create(const std::string& tag) { // This linear search may be replaced by a more sophisticated // look-up strategy, but for this case it doesn't seem worth of // it. In fact, given a (supposed) typical quantity of (at most) // 10 enumerators, this simple loop couldn't be outperformed // (even discarding some initialization procedures that may // be needed). // Even more, who would be able to quantify the difference given // that number of values! for (unsigned int i = 0; i < kNumEnum; ++i) { if (Tags[i] == tag) return static_cast(i); } return Handle(tag); } }; } #endif