Considerar la siguiente plantilla de clase

class MyClassInterface {
public:
  virtual double foo(double) = 0;
}

class MyClass<int P1, int P2, int P3>
: public MyClassInterface {
public:
  double foo(double a) {
    //complex computation dependent on P1, P2, P3
  }
  //more methods and fields (dependent on P1, P2, P3)
}

Los parámetros de la plantilla P1, P2, P3 están en un rango restringido como de 0 a un cierto valor fijo n fija en tiempo de compilación.

Ahora me gustaría construir una «fábrica» método como

MyClassInterface* Factor(int p1, int p2, int p3) {
  return new MyClass<p1,p2,p3>(); //<- how to do this?
}

La pregunta sería cómo lograr la construcción de la plantilla de la clase cuando los parámetros de la plantilla sólo son conocidos en tiempo de ejecución. Y sería el mismo ser posible con los parámetros de la plantilla de tener un dominio muy grandes (como una doble)? Por favor, tenga en cuenta también, si la solución posible es extensible al uso de más parámetros de la plantilla.

  • Realmente me gustaría saber la razón más allá de que se trate. Podría usted explicarnos lo que usted está tratando de lograr mediante el uso de esta extraña construcción ?
  • Hay una enorme algoritmo que puede ser parametrizado con la plantilla entero parámetros. Dependen de los parámetros, la compilación genera un poco de código altamente optimizado. Ahora quiero ser capaz de utilizar los diferentes «versiones» de fuera sin preocuparse de su aplicación y por especificando los parámetros en tiempo de ejecución en un usuario supervisado manera. A pesar de esta aplicación, este fue también un teórico de la pregunta por pura curiosidad.
  • Tenga en cuenta que debido a la instanciación de una posible gran número de especializaciones, el ejecutable resultante gran tamaño puede técnicamente ir en contra de sus optimizaciones performancewise. Código grandes a menudo significa lento código, especialmente en la presencia de irregularidades en los patrones de ramificación. (como siempre, el perfil de saber lo que está pasando)
InformationsquelleAutor Danvil | 2010-05-20

7 Comentarios

  1. 19

    Aquí está lo que usted puede hacer:

    MyClassInterface* Factor(int p1, int p2, int p3) {
      if (p1 == 0 && p2 == 0 && p3 == 0)
        return new MyClass<0,0,0>();
      if (p1 == 0 && p2 == 0 && p3 == 1)
        return new MyClass<0,0,1>();
      etc;
    }

    Tenga en cuenta que esto no es ni remotamente la escala de valores de punto flotante. Las escalas sólo a un conocido de la lista de valores discretos.


    También he utilizado este trozo de código antes de hacer alguna plantilla de generación automática:

    #include <boost/preprocessor.hpp>
    
    #define RANGE ((0)(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12))
    #define MACRO(r, p) \
        if (BOOST_PP_SEQ_ELEM(0, p) == var1 && BOOST_PP_SEQ_ELEM(1, p) == var2 && BOOST_PP_SEQ_ELEM(2, p) == var3 && BOOST_PP_SEQ_ELEM(3, p) == var4) \
            actual_foo = foo<BOOST_PP_TUPLE_REM_CTOR(4, BOOST_PP_SEQ_TO_TUPLE(p))>;
    BOOST_PP_SEQ_FOR_EACH_PRODUCT(MACRO, RANGE RANGE RANGE RANGE)
    #undef MACRO
    #undef RANGE

    El compilador produce una salida que se parece a esto:

    if (0 == var1 && 0 == var2 && 0 == var3 && 0 == var4) actual_foo = foo<0, 0, 0, 0>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 1 == var4) actual_foo = foo<0, 0, 0, 1>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 2 == var4) actual_foo = foo<0, 0, 0, 2>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 3 == var4) actual_foo = foo<0, 0, 0, 3>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 4 == var4) actual_foo = foo<0, 0, 0, 4>; 
    if (0 == var1 && 0 == var2 && 0 == var3 && 5 == var4) actual_foo = foo<0, 0, 0, 5>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 6 == var4) actual_foo = foo<0, 0, 0, 6>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 7 == var4) actual_foo = foo<0, 0, 0, 7>;
    if (0 == var1 && 0 == var2 && 0 == var3 && 8 == var4) actual_foo = foo<0, 0, 0, 8>;
    etc...

    También, por favor, tenga en cuenta que con este método, con 4 variables, cada uno de los que van más de 13 valores, debería hacer que el compilador para crear instancias de 28561 copias de esta función. Si su n fue de 50, y aún tenía 4 opciones, usted tendría 6250000 funciones de instancias. Esto puede hacer que para un LENTO compilar.

    • ‘Esto puede hacer que para un LENTO compilar’ – por No hablar de un preprocesado tamaño se aproxima a la mitad de un gigabyte y una enorme ejecutable resultante tamaño si alguna vez has encontrado un compilador que podrían hacer frente a eso.
    • Absolutamente. Lo he utilizado para un conjunto de bools que yo quería tratar de plantillas a cabo. Creo que la mayoría de los que me hizo fue < 100 generado instancias.
    • ¿Por qué no utilizar BOOST_PP_SEQ_ENUM_PARAMS en lugar de convertir a la tupla y quitar el constructor ? Hay una eficiencia razón ?
    • La eficiencia de la macro generación no es realmente un problema. Nunca he usado este código para más de un día, por lo que fue hackeado. El único comentario sería que yo estaba usando Impulso 1.33.1 que pueden o no el apoyo adicional de la macro llamadas.
    • ¿Qué te hace pensar que los valores de punto flotante no son discretos? Incluso hay menos valores de coma flotante que hay valores int, porque NaN y co. tiene algunos duplicados de las representaciones.
    • Estoy más en referencia a el hecho de que si su rango es [0, 10], es mucho más fácil de hacer que si usted está utilizando enteros, en lugar de carrozas. No es que no hay un número finito de carrozas en ese rango, es que hay demasiados.
    • Un lento compilar, y posiblemente el lento tiempo de ejecución debido a que el tamaño del código y de la caché de instrucciones se pierde, derrotando al punto de hacer en tiempo de compilación micro optimización en el primer lugar. Esto parece lo suficientemente importante como para ser notado.

  2. 11

    Si las macros no son lo tuyo, también puede generar la if-then-else es el uso de plantillas:

    #include <stdexcept>
    #include <iostream>
    
    const unsigned int END_VAL = 10;
    
    class MyClassInterface
    {
    public:
        virtual double foo (double) = 0;
    };
    
    template<int P1, int P2, int P3>
    class MyClass : public MyClassInterface
    {
    public:
        double foo (double a)
        {
            return P1 * 100 + P2 * 10 + P3 + a;
        }
    };
    
    struct ThrowError
    {
        static inline MyClassInterface* create (int c1, int c2, int c3)
        {
            throw std::runtime_error ("Could not create MyClass");
        }
    };
    
    template<int DEPTH = 0, int N1 = 0, int N2 = 0, int N3 = 0>
    struct Factory : ThrowError {};
    
    template<int N2, int N3>
    struct Factory<0, END_VAL, N2, N3> : ThrowError {};
    
    template<int N1, int N3>
    struct Factory<1, N1, END_VAL, N3> : ThrowError {};
    
    template<int N1, int N2>
    struct Factory<2, N1, N2, END_VAL> : ThrowError {};
    
    template<int N1, int N2, int N3>
    struct Factory<0, N1, N2, N3>
    {
        static inline MyClassInterface* create (int c1, int c2, int c3)
        {
            if (c1 == N1)
            {
                return Factory<1, N1, 0, 0>::create (c1, c2, c3);
            }
            else
                return Factory<0, N1 + 1, N2, N3>::create (c1, c2, c3);
        }
    };
    
    template<int N1, int N2, int N3>
    struct Factory<1, N1, N2, N3>
    {
        static inline MyClassInterface* create (int c1, int c2, int c3)
        {
            if (c2 == N2)
            {
                return Factory<2, N1, N2, 0>::create (c1, c2, c3);
            }
            else
                return Factory<1, N1, N2 + 1, N3>::create (c1, c2, c3);
        }
    };
    
    template<int N1, int N2, int N3>
    struct Factory<2, N1, N2, N3>
    {
        static inline MyClassInterface* create (int c1, int c2, int c3)
        {
            if (c3 == N3)
            {
                return new MyClass<N1, N2, N3> ();
            }
            else
                return Factory<2, N1, N2, N3 + 1>::create (c1, c2, c3);
        }
    };
    
    MyClassInterface* factory (int c1, int c2, int c3)
    {
        return Factory<>::create (c1, c2, c3);
    }

    Ya que las pruebas están anidados se debe ser más eficiente que sharth macro de la solución.

    Se puede extender a un mayor número de parámetros mediante la adición de más profundidad los casos.

  3. 8

    Eso no es posible, las plantillas se crean instancias en tiempo de compilación.

    Por el tiempo que usted tiene un ejecutable que sólo tienen clases(en particular los casos de las plantillas), no hay plantillas más.

    Si usted no sabe los valores en tiempo de compilación no se puede tener las plantillas para los.

    • Esto no es cierto. Para el entero de los parámetros de un pequeño dominio, puede utilizar el modificador/si las declaraciones, como se indica en el post sharth.
    • No hay ninguna manera de achieve the construction of the template class when template parameters are only known at runtime sin la construcción de todos los casos posibles y haciendo tiempo de ejecución de conmutación.
    • Y la pregunta era cómo por ejemplo hacer esto recursiva de conmutación (sin escribir en el código a mano).
  4. 2

    Técnicamente es *posible** – pero no es práctico y es casi sin duda el camino equivocado para abordar el problema.

    ¿Hay alguna razón por la que P1, P2 y P3 no se pueden regular de variables de tipo integer?


    *Se puede incrustar un compilador de C++ y una copia de su código fuente, compilar una librería dinámica o compartido objeto que implementa su fábrica de función para un conjunto dado de P1,P2,P3 – pero, ¿realmente quieres hacer eso? De la OMI, que es una locura lo que hay que hacer.

  5. 2

    No sé si esto es aplicable a su problema actual, pero parece que en C++11
    constexpr puede ser lo que usted está buscando – constexpr funciones pueden ser llamadas durante el tiempo de ejecución y, al mismo tiempo, puede ser ejecutado en tiempo de compilación.

    El uso de constexpr también tiene la ventaja de ser mucho más «limpios» en busca de que el uso de TMP, se trabaja con valores de tiempo de ejecución (no sólo los valores integrales), mientras que la retención de la mayoría de TMP beneficios tales como memoization y compilación en tiempo de ejecución, aunque esto es algo dado para el compilador de la decisión. De hecho, constexpr es generalmente mucho más rápido que un TMP versión equivalente.

    Tenga en cuenta también que, en general, el uso de plantillas durante el tiempo de ejecución sería socavar uno de la plantilla de las mayores características – El hecho de que son manejados durante el tiempo de compilación y prácticamente desaparecen durante el tiempo de ejecución.

    • Ejemplo por favor?
  6. 1

    No se puede. plantilla de tiempo de compilación sólo.

    Se puede construir en tiempo de compilación de todas las posibles plantillas de valores que usted desea, y elegir uno de ellos en tiempo de ejecución.

  7. 0

    demasiado tarde, lo sé, pero lo que acerca de esto:

    //MSVC++ 2010 SP1 x86
    //boost 1.53
    
    #include <tuple>
    #include <memory>
    //test
    #include <iostream>
    
    #include <boost/assert.hpp>
    #include <boost/static_assert.hpp>
    #include <boost/mpl/size.hpp>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/push_back.hpp>
    #include <boost/mpl/pair.hpp>
    #include <boost/mpl/begin.hpp>
    #include <boost/mpl/deref.hpp>
    #include <boost/mpl/int.hpp>
    #include <boost/mpl/placeholders.hpp>
    #include <boost/mpl/unpack_args.hpp>
    #include <boost/mpl/apply.hpp>
    //test
    #include <boost/range/algorithm/for_each.hpp>
    
    /*! \internal
     */
    namespace detail
    {
    /*! \internal
     */
    namespace runtime_template
    {
    
    /*! \internal
        fwd
     */
    template <
        typename Template
        , typename Types
        , typename Map  //top level map iterator
        , typename LastMap  //top level map iterator
        , int Index
        , bool Done = std::is_same<Map, LastMap>::value
    >
    struct apply_recursive_t;
    
    /*! \internal
        fwd
     */
    template <
        typename Template
        , typename Types
        , typename Map  //top level map iterator
        , typename LastMap  //top level map iterator
        , typename First
        , typename Last
        , int Index
        , bool Enable = !std::is_same<First, Last>::value
    >
    struct apply_mapping_recursive_t;
    
    /*! \internal
        run time compare key values + compile time push_back on \a Types
     */
    template <
        typename Template
        , typename Types
        , typename Map  //top level map iterator
        , typename LastMap  //top level map iterator
        , typename First
        , typename Last
        , int Index //current argument
        , bool Enable /* = !std::is_same<First, Last>::value */
    >
    struct apply_mapping_recursive_t
    {
        typedef void result_type;
        template <typename TypeIds, typename T>
        inline static void apply(const TypeIds& typeIds, T&& t)
        {   namespace mpl = boost::mpl;
            typedef typename mpl::deref<First>::type key_value_pair;
            typedef typename mpl::first<key_value_pair>::type typeId;   //mpl::int
            if (typeId::value == std::get<Index>(typeIds))
            {
                apply_recursive_t<
                    Template
                    , typename mpl::push_back<
                        Types
                        , typename mpl::second<key_value_pair>::type
                    >::type
                    , typename mpl::next<Map>::type
                    , LastMap
                    , Index + 1
                >::apply(typeIds, std::forward<T>(t));
            }
            else
            {
                apply_mapping_recursive_t<
                    Template
                    , Types
                    , Map
                    , LastMap
                    , typename mpl::next<First>::type
                    , Last
                    , Index
                >::apply(typeIds, std::forward<T>(t));
            }
        }
    };
    
    /*! \internal
        mapping not found
        \note should never be invoked, but must compile
     */
    template <
        typename Template
        , typename Types
        , typename Map  //top level map iterator
        , typename LastMap  //top level map iterator
        , typename First
        , typename Last
        , int Index
    >
    struct apply_mapping_recursive_t<
        Template
        , Types
        , Map
        , LastMap
        , First
        , Last
        , Index
        , false
    >
    {
        typedef void result_type;
        template <typename TypeIds, typename T>
        inline static void apply(const TypeIds& /* typeIds */, T&& /* t */)
        {
            BOOST_ASSERT(false);
        }
    };
    
    /*! \internal
        push_back on \a Types template types recursively
     */
    template <
        typename Template
        , typename Types
        , typename Map  //top level map iterator
        , typename LastMap  //top level map iterator
        , int Index
        , bool Done /* = std::is_same<Map, LastMap>::value */
    >
    struct apply_recursive_t
    {
        typedef void result_type;
        template <typename TypeIds, typename T>
        inline static void apply(const TypeIds& typeIds, T&& t)
        {   namespace mpl = boost::mpl;
            typedef typename mpl::deref<Map>::type Mapping; //[key;type] pair vector
            apply_mapping_recursive_t<
                Template
                , Types
                , Map
                , LastMap
                , typename mpl::begin<Mapping>::type
                , typename mpl::end<Mapping>::type
                , Index
            >::apply(typeIds, std::forward<T>(t));
        }
    };
    
    /*! \internal
        done! replace mpl placeholders of \a Template with the now complete \a Types
        and invoke result
     */
    template <
        typename Template
        , typename Types
        , typename Map
        , typename LastMap
        , int Index
    >
    struct apply_recursive_t<
        Template
        , Types
        , Map
        , LastMap
        , Index
        , true
    >
    {
        typedef void result_type;
        template <typename TypeIds, typename T>
        inline static void apply(const TypeIds& /* typeIds */, T&& t)
        {   namespace mpl = boost::mpl;
            typename mpl::apply<
                mpl::unpack_args<Template>
                , Types
            >::type()(std::forward<T>(t));
        }
    };
    
    /*! \internal
        helper functor to be used with invoke_runtime_template()
        \note cool: mpl::apply works with nested placeholders types!
     */
    template <typename Template>
    struct make_runtime_template_t
    {
        typedef void result_type;
        template <typename Base>
        inline void operator()(std::unique_ptr<Base>* base) const
        {
            base->reset(new Template());
        }
    };
    
    }   //namespace runtime_template
    }   //namespace detail
    
    /*! \brief runtime template parameter selection
    
        \param Template functor<_, ...> placeholder expression
        \param Maps mpl::vector<mpl::vector<mpl::pair<int, type>, ...>, ...>
        \param Types std::tuple<int, ...> type ids
        \param T functor argument type
    
        \note all permutations must be compilable (they will be compiled of course)
        \note compile time: O(n!) run time: O(n)
    
        \sa invoke_runtime_template()
        \author slow
     */
    template <
        typename Template
        , typename Map
        , typename Types
        , typename T
    >
    inline void invoke_runtime_template(const Types& types, T&& t)
    {   namespace mpl = boost::mpl;
        BOOST_STATIC_ASSERT(mpl::size<Map>::value == std::tuple_size<Types>::value);
        detail::runtime_template::apply_recursive_t<
            Template
            , mpl::vector<>
            , typename mpl::begin<Map>::type
            , typename mpl::end<Map>::type
            , 0
        >::apply(types, std::forward<T>(t));
    }
    
    /*! \sa invoke_runtime_template()
     */
    template <
        typename Template
        , typename Map
        , typename Base
        , typename Types
    >
    inline void make_runtime_template(const Types& types, std::unique_ptr<Base>* base)
    {
        invoke_runtime_template<
            detail::runtime_template::make_runtime_template_t<Template>
            , Map
        >(types, base);
    }
    
    /*! \overload
     */
    template <
        typename Base
        , typename Template
        , typename Map
        , typename Types
    >
    inline std::unique_ptr<Base> make_runtime_template(const Types& types)
    {
        std::unique_ptr<Base> result;
    
        make_runtime_template<Template, Map>(types, &result);
        return result;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    
    namespace mpl = boost::mpl;
    using mpl::_;
    
    class MyClassInterface {
    public:
        virtual ~MyClassInterface() {}
        virtual double foo(double) = 0;
    };
    
    template <int P1, int P2, int P3>
    class MyClass
    : public MyClassInterface {
    public:
        double foo(double /*a*/) {
            //complex computation dependent on P1, P2, P3
            std::wcout << typeid(MyClass<P1, P2, P3>).name() << std::endl;
            return 42.0;
        }
        //more methods and fields (dependent on P1, P2, P3)
    };
    
    //wrapper for transforming types (mpl::int) to values
    template <typename P1, typename P2, typename P3>
    struct MyFactory
    {
        inline void operator()(std::unique_ptr<MyClassInterface>* result) const
        {
            result->reset(new MyClass<P1::value, P2::value, P3::value>());
        }
    };
    
    template <int I>
    struct MyConstant
        : boost::mpl::pair<
            boost::mpl::int_<I>
            , boost::mpl::int_<I>
        > {};
    
    std::unique_ptr<MyClassInterface> Factor(const std::tuple<int, int, int>& constants) {
        typedef mpl::vector<
            MyConstant<0>
            , MyConstant<1>
            , MyConstant<2>
            , MyConstant<3>
            //...
        > MyRange;
        std::unique_ptr<MyClassInterface> result;
        invoke_runtime_template<
            MyFactory<_, _, _>
            , mpl::vector<MyRange, MyRange, MyRange>
        >(constants, &result);
        return result;
    }
    
    int main(int /*argc*/, char* /*argv*/[])
    {
        typedef std::tuple<int, int, int> Tuple;
        const Tuple Permutations[] =
        {
            std::make_tuple(0,      0,  0)
            , std::make_tuple(0,    0,  1)
            , std::make_tuple(0,    1,  0)
            , std::make_tuple(0,    1,  1)
            , std::make_tuple(1,    0,  0)
            , std::make_tuple(1,    2,  3)
            , std::make_tuple(1,    1,  0)
            , std::make_tuple(1,    1,  1)
            //...
        };
    
        boost::for_each(Permutations, [](const Tuple& constants) { Factor(constants)->foo(42.0); });
        return 0;
    }

Dejar respuesta

Please enter your comment!
Please enter your name here