Выполнение функции времени компиляции C ++ - PullRequest
12 голосов
/ 03 мая 2011

В моем коде есть строковые теги, которые преобразуются в числа и используются для поиска значений в структуре значений тегов.

У меня есть что-то вроде этого:

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 ]

12 голосов
/ 03 мая 2011

Звучит так, как вы хотите: Повышение . MPL boost::mpl::string.Было бы более или менее тривиально написать метафункцию для преобразования mpl::string в целочисленный тип во время компиляции, используя mpl::fold (или не выполнить компиляцию, если строковый литерал не представляет действительныйинтегральное значение).

РЕДАКТИРОВАТЬ:

Я не совсем уверен в том, что вы ищете, так что вот фактически два разных ответа в зависимости от интерпретации:


IF то, что вам нужно, это строка строки во время компиляции преобразование (например, "425897"может быть признана интегральной константой 425897 во время компиляции), тогда можно использовать Boost.MPL, как я предложил:

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

Использование будет выглядеть так:

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

Поддержкадля отрицательных чисел реализована поддержка обнаружения переполнения (но ваш компилятор, вероятно, выдаст предупреждение).


IF то, что вы ищетеfor это строка времени компиляции для значения компиляции отображение (например, так что "SomeTag" может быть записаново время компиляции он обозначается как целочисленная константа 425897), затем Boost.MPL все еще решает проблему, но все сопоставления строк и целых значений должны быть известны во время компиляции и зарегистрированы централизованно:

#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 / internal_search (тот, который в реальной реализации принимает int)принимать интегральное значение как параметр шаблона, а не как параметр функции, если это имеет смысл для его реализации.

Надеюсь, это ответит на ваши вопросы.

5 голосов
/ 03 мая 2011

Вы не можете оперировать строковыми литералами во время компиляции, поэтому то, что вы хотите, не осуществимо, как вы предлагали.Однако, если вы планируете обрабатывать эти строки во время компиляции, то это означает, что вы знаете все строки во время компиляции, и из этого вы можете получить приемлемые приближения к тому, что вы хотите.

Код, который вы показали, подразумевает, что генерация чисел (давайте назовем это хешем) вызывается каждый раз, когда кто-то ищет тег.Было бы приемлемо уменьшить это до одного вызова?Если это так, вы можете определить константы и использовать их вместо строк:

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 голосов
/ 03 мая 2011

Если строковый литерал известен во время компиляции, то, вероятно, нет причин использовать его как строковый литерал.Вы можете использовать перечисление или именованные целочисленные константы.

Если строка передается в функцию поиска переменной, и она не известна во время компиляции, то нет способа сделать результат toNumber() при компиляции.время.Тогда хорошим решением будет использование какого-то словаря (например, std::map<std::string, int>)

0 голосов
/ 03 мая 2011

Не уверен, что можешь. Список возможных тегов небольшой? Даже если это не так, он маленький в большинстве случаев Если это так, вы можете использовать специализацию шаблонов для подмножества тегов

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

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

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

(предостережения: мой синтаксис специализации может быть неправильным, и я понятия не имею, работает ли это для символа *)

0 голосов
/ 03 мая 2011

Я знаю, что это, вероятно, не модно, но вы можете создать хэш-таблицу заранее.Мне нравится использовать gperf для создания сантехники там.

Я знаю, знаю.Вы хотели, чтобы компиляция длилась дольше ... Просто говорю:)

0 голосов
/ 03 мая 2011

Хотя не время компиляции, я думаю, что это достаточно быстро для вас;

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