Specify template parameters at runtime

后端 未结 7 1054
遇见更好的自我
遇见更好的自我 2020-11-29 06:32

Consider the following template class

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

class MyClass
: pub         


        
相关标签:
7条回答
  • 2020-11-29 06:53

    You can't. template are compile time only.

    You can build at compile time all the possible templates values you want, and choose one of them in run time.

    0 讨论(0)
  • 2020-11-29 06:55

    If macros aren't your thing then you can also generate the if-then-else's using templates:

    #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);
    }
    

    Since the tests are nested it should be more efficient than sharth's macro solution.

    You can extend it to more parameters by adding more depth cases.

    0 讨论(0)
  • 2020-11-29 06:59

    Thats not posible, templates are instantiated at compile time.
    By the time you have an executable you only have classes(particular instantiations of those templates), no templates any more.

    If you don't know values at compile time you can't have templates for those.

    0 讨论(0)
  • 2020-11-29 06:59

    way too late, i know, but what about this:

    // 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;
    }
    
    0 讨论(0)
  • 2020-11-29 07:02

    I don't know if this is applicable to your current problem, but it would appear that C++11 constexpr may be what you are looking for - constexpr functions may be called during runtime and at the same time may be executed at compile time.

    The use of constexpr also has the added benefits of being far "cleaner" looking than using TMP, working with any runtime values (not just integral values) whilst retaining most of TMP's benefits such as memoization and compile time execution, although this is somewhat given to the compiler's decision. In fact, constexpr is usually much faster than a TMP equivalent version.

    Note also that in general the use of templates during runtime would undermine one of template's greatest features - The fact that they are handled during compile time and pretty much disappear during runtime.

    0 讨论(0)
  • 2020-11-29 07:09

    Here's what you can do:

    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;
    }
    

    Note that this does not even remotely scale to floating point values. It scales only to a known list of discrete values.


    I've also used this bit of code before to do some template automatic generation:

    #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
    

    The compiler produces output that looks like this:

    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...
    

    Also, please note that with this method, with 4 variables, each ranging over 13 values, You would cause the compiler to instantiate 28561 copies of this function. If your n was 50, and you still had 4 options, you would have 6250000 functions instantiated. This can make for a SLOW compile.

    0 讨论(0)
提交回复
热议问题