C ++编译时函数执行

时间:2011-05-03 12:28:36

标签: c++ template-meta-programming

我的代码中有字符串标记,可以转换为数字并用于搜索标记值结构中的值。

我有这样的事情:

void foo()
{
    type value = search("SomeTag");
}

搜索定义如下:

type search(const char* tag)
{
    return internal_search(toNumber(tag));
}

因为所有时间标记在编译时都是常量,所以我想删除将标记转换为搜索函数中的数字的调用。我知道可以在编译时使用模板(http://en.wikipedia.org/wiki/Compile_time_function_execution)执行一些简单的函数,但我不知道如何迭代空终止字符串并在模板中保留中间值。你能给出一个简单的样本来迭代一个空终止的字符串,并将这些字符添加到公共变量中吗?

6 个答案:

答案 0 :(得分:12)

听起来你想要的是BoostMPLboost::mpl::string。使用mpl::fold编写元函数以在编译时将mpl::string转换为整数类型(或者如果字符串文字不表示有效的整数值则无法编译)或多或少是微不足道的)。

修改

我不完全确定你在寻找什么,所以根据解释,这里有两个不同的答案:


IF 您正在寻找的是编译时字符串到整数值转换(例如{{1} }可以在编译时被识别为整数常量"425897",然后可以按照我的建议使用Boost.MPL:

425897

用法如下:

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

支持负数,不支持溢出检测(但您的编译器可能会发出警告)。


IF 您正在寻找的是编译时字符串到整数值映射(例如{{1}在编译时可以将其识别为整数常量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>(); } ,然后Boost.MPL仍然可以解决问题,但所有字符串到整数值的映射必须在编译时知道并集中注册:

"SomeTag"

用法如下:

425897
需要编辑

#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> > { }; 来维护字符串到整数值的映射,并且如图所示,映射的整数值不必都是相同的基础类型。


在任何一种情况下,由于映射是在编译时完成的,因此可以使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(真实实现采用search的那个)取整数值作为模板参数而不是作为函数参数,如果这样做对其实现有意义。

希望这能回答你的问题。

答案 1 :(得分:5)

您无法在编译时对字符串文字进行操作,因此您所希望的方式不可行。但是,如果您正在考虑在编译时处理这些字符串,那么这意味着您在编译时知道所有字符串,并且从中可以获得您想要的可接受的近似值。

您展示的代码暗示每次有人搜索标记时都会调用数字生成(让我们称之为哈希)。将此减少到一次调用是否可以接受?如果是这样,您可以定义常量并使用它们而不是字符串:

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

然后,只需将所有出现的search("SomeTag")替换为search(SomeTag)

如果有大量标签,输入上述内容可能非常繁琐,在这种情况下,宏可能会有所帮助:

#define DEFINE_TAG(Tag_) const int Tag_ = toNumber(#Tag_); 

DEFINE_TAG(SomeTag); 
DEFINE_TAG(SomeOtherTag); 
DEFINE_TAG(YetAnotherTag); 
// ... 

#undef DEFINE_TAG

答案 2 :(得分:2)

如果在编译时知道字符串文字,那么可能没有理由将它用作字符串文字。您可以使用枚举或命名整数常量。

如果字符串由变量传递给搜索函数并且在编译时不知道,则在编译时无法进行toNumber()结果。然后一个好的解决方案是使用某种字典(例如std::map<std::string, int>

答案 3 :(得分:0)

通过不编译时间,我认为这对你来说足够快;

void foo()
{
    const static auto someTagN = toNumber("SomeTag");
    type value = internal_search(someTagN );
}

答案 4 :(得分:0)

我知道它可能不太时髦,但您可以提前生成哈希表。我喜欢使用gperf在那里生成管道。

我知道。你想要一些东西让编译持续更长时间......只是说:)

答案 5 :(得分:0)

不确定是否可以。可能的标签列表是否很小?即使不是,大部分时间都是小的。如果是这样,您可以对标记的子集使用模板专门化

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

template<>
int toNumber<"0">() {
     return 0;
}

template<>
int toNumber<"1">() {
     return 1;
}

(警告:我的专业化语法可能有误,我不知道这是否适用于char *)