У меня есть строковые теги в моем коде, которые преобразуются в числа и используются для поиска значений в структуре значений тега.
Поскольку все временные теги являются постоянными во время компиляции, я хочу удалить вызов, который преобразует тег в число из функции поиска. Я знаю, что во время компиляции можно выполнить некоторые простые функции с помощью шаблонов (http://en.wikipedia.org/wiki/Compile_time_function_execution), но я точно не знаю, как итерации по нулевой завершаемой строке и сохранить промежуточные значения в шаблоне. Можете ли вы дать простой пример, который выполняет итерацию строки с завершающим нулем и добавляет символы в общедоступной переменной?
Ответ 1
Вы не можете работать с строковыми литералами во время компиляции, поэтому то, что вы хотите, невозможно в том, как вы предложили. Однако, если вы планируете обрабатывать эти строки во время компиляции, это означает, что вы знаете все строки во время компиляции, и из этого вы можете прийти к приемлемым приближениям к тому, что вы хотите.
Показанный код подразумевает, что генерация числа (пусть называется хешем) вызывается каждый раз, когда кто-то ищет тег. Будет ли сокращение этого до одного вызова приемлемым? Если это так, вы можете определить константы и использовать их вместо строк:
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
Похоже, что вы хотите Boost. MPL boost::mpl::string
. Было бы более или менее тривиально написать метафункцию для преобразования mpl::string
в интегральный тип во время компиляции с использованием mpl::fold
( или не скомпилировать, если строковый литерал не представляет действительное целочисленное значение).
EDIT:
Я не совсем уверен, что вы ищете, так что в действительности это два разных ответа в зависимости от интерпретации:
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 то, что вы ищете, - это сопоставление строк между целыми значениями во время компиляции (например, "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
), можно было бы сделать интегральное значение в качестве параметра шаблона, а не как параметр функции, если это имеет смысл для его реализации.
Надеюсь, это ответит на ваши вопросы.