C++ compile time function execution

后端 未结 6 1638
醉酒成梦
醉酒成梦 2021-01-02 02:41

I have string tags in my code that are converted to numbers and used to search values in a tag-value structure.

I have something like this:

void foo(         


        
相关标签:
6条回答
  • 2021-01-02 03:14

    Not sure that you can. Is the list of possible tags small? Even if not, is it small most of the time. If so, you can use template specialization on a subset of the tags

    template<char *tag> 
    int toNumber() {
        return toNumber(tag);
    }
    
    template<>
    int toNumber<"0">() {
         return 0;
    }
    
    template<>
    int toNumber<"1">() {
         return 1;
    }
    

    (caveats: my specialization syntax might be wrong, and I have no idea if this works for char*)

    0 讨论(0)
  • 2021-01-02 03:25

    If the string literal is known at compile time, then there is probably no reason to use it as a string literal. You may use enumeration or named integral constants.

    If the string is passed to the search function by a variable and it is not known at compile time, then there is no way to do the toNumber() resulution at compile time. Then a good solution is to use some kind of a dictionary (e.g. std::map<std::string, int>)

    0 讨论(0)
  • 2021-01-02 03:26

    Althrough not compile time, I think this is fast enough for you;

    void foo()
    {
        const static auto someTagN = toNumber("SomeTag");
        type value = internal_search(someTagN );
    }
    
    0 讨论(0)
  • 2021-01-02 03:31

    You cannot operate on string literals at compile-time, so what you want isn't feasible in the way you suggested. However, if you're contemplating to process these strings at compile-time, then this means you know all strings at compile-time, and from that you might arrive at acceptable approximations to what you want.

    The code you showed implies that the number generation (let's call it a hash) is invoked every time someone searches for a tag. Would reducing this to one invocation be acceptable? If so, you could define constants and use these instead of strings:

    const int SomeTag       = toNumber("SomeTag"      ); 
    const int SomeOtherTag  = toNumber("SomeOtherTag" ); 
    const int YetAnotherTag = toNumber("YetAnotherTag"); 
    // ... 
    

    Then, simply replace all occurances of search("SomeTag") by search(SomeTag).

    If there's a great number of tags, typing the above could be very tedious, in which case a macro might help:

    #define DEFINE_TAG(Tag_) const int Tag_ = toNumber(#Tag_); 
    
    DEFINE_TAG(SomeTag); 
    DEFINE_TAG(SomeOtherTag); 
    DEFINE_TAG(YetAnotherTag); 
    // ... 
    
    #undef DEFINE_TAG
    
    0 讨论(0)
  • 2021-01-02 03:36

    It sounds like what you want is Boost.MPL's boost::mpl::string. It would be more-or-less trivial to write a metafunction to convert an mpl::string to an integral type at compile time using mpl::fold (or fail to compile if the string literal does not represent a valid integral value).

    EDIT:

    I'm not entirely sure what you're looking for, so here is effectively two different answers depending on interpretation:


    IF what you're looking for is compile-time string-to-integral-value conversion (e.g. so "425897" could be recognized as the integral constant 425897 at compile time), then one can use Boost.MPL as I suggested:

    #include <cstddef>
    #include <boost/type_traits/is_integral.hpp>
    #include <boost/type_traits/is_same.hpp>
    #include <boost/type_traits/is_signed.hpp>
    #include <boost/mpl/and.hpp>
    #include <boost/mpl/assert.hpp>
    #include <boost/mpl/char.hpp>
    #include <boost/mpl/contains.hpp>
    #include <boost/mpl/end.hpp>
    #include <boost/mpl/eval_if.hpp>
    #include <boost/mpl/find_if.hpp>
    #include <boost/mpl/fold.hpp>
    #include <boost/mpl/front.hpp>
    #include <boost/mpl/identity.hpp>
    #include <boost/mpl/integral_c.hpp>
    #include <boost/mpl/minus.hpp>
    #include <boost/mpl/negate.hpp>
    #include <boost/mpl/next.hpp>
    #include <boost/mpl/not.hpp>
    #include <boost/mpl/pair.hpp>
    #include <boost/mpl/placeholders.hpp>
    #include <boost/mpl/plus.hpp>
    #include <boost/mpl/pop_front.hpp>
    #include <boost/mpl/push_back.hpp>
    #include <boost/mpl/reverse_fold.hpp>
    #include <boost/mpl/size_t.hpp>
    #include <boost/mpl/string.hpp>
    #include <boost/mpl/times.hpp>
    #include <boost/mpl/vector.hpp>
    
    namespace details
    {
        namespace mpl = boost::mpl;
    
        typedef mpl::vector10<
            mpl::char_<'0'>, mpl::char_<'1'>, mpl::char_<'2'>, mpl::char_<'3'>,
            mpl::char_<'4'>, mpl::char_<'5'>, mpl::char_<'6'>, mpl::char_<'7'>,
            mpl::char_<'8'>, mpl::char_<'9'>
        > valid_chars_t;
    
        template<typename IntegralT, typename PowerT>
        struct power_of_10;
    
        template<typename IntegralT, std::size_t Power>
        struct power_of_10<IntegralT, mpl::size_t<Power> > : mpl::times<
            power_of_10<IntegralT, mpl::size_t<Power - 1u> >,
            mpl::integral_c<IntegralT, 10>
        > { };
    
        template<typename IntegralT>
        struct power_of_10<IntegralT, mpl::size_t<1u> >
            : mpl::integral_c<IntegralT, 10>
        { };
    
        template<typename IntegralT>
        struct power_of_10<IntegralT, mpl::size_t<0u> >
            : mpl::integral_c<IntegralT, 1>
        { };
    
        template<typename IntegralT, typename StringT>
        struct is_negative : mpl::and_<
            boost::is_signed<IntegralT>,
            boost::is_same<
                typename mpl::front<StringT>::type,
                mpl::char_<'-'>
            >
        > { };
    
        template<typename IntegralT, typename StringT>
        struct extract_actual_string : mpl::eval_if<
            is_negative<IntegralT, StringT>,
            mpl::pop_front<StringT>,
            mpl::identity<StringT>
        > { };
    
        template<typename ExtractedStringT>
        struct check_valid_characters : boost::is_same<
            typename mpl::find_if<
                ExtractedStringT,
                mpl::not_<mpl::contains<valid_chars_t, mpl::_> >
            >::type,
            typename mpl::end<ExtractedStringT>::type
        > { };
    
        template<typename ExtractedStringT>
        struct pair_digit_with_power : mpl::first<
            typename mpl::reverse_fold<
                ExtractedStringT,
                mpl::pair<mpl::vector0<>, mpl::size_t<0> >,
                mpl::pair<
                    mpl::push_back<
                        mpl::first<mpl::_1>,
                        mpl::pair<mpl::_2, mpl::second<mpl::_1> >
                    >,
                    mpl::next<mpl::second<mpl::_1> >
                >
            >::type
        > { };
    
        template<typename IntegralT, typename ExtractedStringT>
        struct accumulate_digits : mpl::fold<
            typename pair_digit_with_power<ExtractedStringT>::type,
            mpl::integral_c<IntegralT, 0>,
            mpl::plus<
                mpl::_1,
                mpl::times<
                    mpl::minus<mpl::first<mpl::_2>, mpl::char_<'0'> >,
                    power_of_10<IntegralT, mpl::second<mpl::_2> >
                >
            >
        > { };
    
        template<typename IntegralT, typename StringT>
        class string_to_integral_impl
        {
            BOOST_MPL_ASSERT((boost::is_integral<IntegralT>));
    
            typedef typename extract_actual_string<
                IntegralT,
                StringT
            >::type ExtractedStringT;
            BOOST_MPL_ASSERT((check_valid_characters<ExtractedStringT>));
    
            typedef typename accumulate_digits<
                IntegralT,
                ExtractedStringT
            >::type ValueT;
    
        public:
            typedef typename mpl::eval_if<
                is_negative<IntegralT, StringT>,
                mpl::negate<ValueT>,
                mpl::identity<ValueT>
            >::type type;
        };
    }
    
    template<typename IntegralT, typename StringT>
    struct string_to_integral2
        : details::string_to_integral_impl<IntegralT, StringT>::type
    { };
    
    template<typename IntegralT, int C0, int C1 = 0, int C2 = 0,
        int C3 = 0, int C4 = 0, int C5 = 0, int C6 = 0, int C7 = 0>
    struct string_to_integral : string_to_integral2<
        IntegralT,
        boost::mpl::string<C0, C1, C2, C3, C4, C5, C6, C7>
    > { };
    

    Usage would look like:

    type search(int tag) { /*impl... */ }
    
    void foo()
    {
        type value = search(string_to_integral<int, '4258','97'>::value);
    }
    
    // OR, if you still want to maintain the separation
    // between `search` and `internal_search`
    
    type internal_search(int tag) { /*impl... */ }
    
    template<typename TagStringT>
    type search()
    {
        return internal_search(string_to_integral2<int, TagStringT>::value);
    }
    
    void foo()
    {
        typedef boost::mpl::string<'4258','97'> tag_t;
        type value = search<tag_t>();
    }
    

    Support for negative numbers is implemented, support for overflow detection is not (but your compiler will probably give a warning).


    IF what you're looking for is compile-time string-to-integral-value mapping (e.g. so "SomeTag" could be recognized as the integral constant 425897 at compile time), then Boost.MPL still solves the problem, but all string-to-integral-value mappings must be known at compile time and registered centrally:

    #include <boost/type_traits/is_same.hpp>
    #include <boost/mpl/assert.hpp>
    #include <boost/mpl/at.hpp>
    #include <boost/mpl/integral_c.hpp>
    #include <boost/mpl/map.hpp>
    #include <boost/mpl/pair.hpp>
    #include <boost/mpl/string.hpp>
    #include <boost/mpl/void.hpp>
    
    namespace details
    {
        namespace mpl = boost::mpl;
    
        typedef mpl::map<
            mpl::pair<
                mpl::string<'Some','Tag'>,
                mpl::integral_c<int, 425897>
            >,
            mpl::pair<
                mpl::string<'Some','Othe','rTag'>,
                mpl::integral_c<int, -87>
            >,
            mpl::pair<
                mpl::string<'AnUn','sign','edTa','g'>,
                mpl::integral_c<unsigned, 7u>
            >
        > mappings_t;
    
        template<typename StringT>
        struct map_string_impl
        {
            typedef typename mpl::at<
                mappings_t,
                StringT
            >::type type;
            BOOST_MPL_ASSERT_NOT((boost::is_same<type, mpl::void_>));
        };
    }
    
    template<typename StringT>
    struct map_string2 : details::map_string_impl<StringT>::type { };
    
    template<int C0, int C1 = 0, int C2 = 0, int C3 = 0,
        int C4 = 0, int C5 = 0, int C6 = 0, int C7 = 0>
    struct map_string : map_string2<
        boost::mpl::string<C0, C1, C2, C3, C4, C5, C6, C7>
    > { };
    

    Usage would look like:

    type search(int tag) { /*impl... */ }
    
    void foo()
    {
        type value = search(map_string<'Some','Tag'>::value);
    }
    
    // OR, if you still want to maintain the separation
    // between `search` and `internal_search`
    
    type internal_search(int tag) { /*impl... */ }
    
    template<typename TagStringT>
    type search()
    {
        return internal_search(map_string2<TagStringT>::value);
    }
    
    void foo()
    {
        typedef boost::mpl::string<'Some','Tag'> tag_t;
        type value = search<tag_t>();
    }
    

    mappings_t is what needs to be edited to maintain your string-to-integral-value mappings, and, as demonstrated, the mapped integral values need not all be of the same underlying type.


    In either case, because the mapping is done at compile time, search/internal_search (the one with the real implementation taking an int) could be made to take the integral value as a template parameter rather than as a function parameter if doing so makes sense for its implementation.

    Hopefully this answers your questions.

    0 讨论(0)
  • 2021-01-02 03:36

    I know it is probably not fashionable, but you can generate a hash-table ahead of time. I like to use gperf to generate the plumbing there.

    I know know. You wanted something to make compilation last longer... Just saying :)

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