12

コードに文字列タグがあり、数値に変換され、タグ値構造の値を検索するために使用されます。

私はこのようなものを持っています:

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

検索が次のように定義されている場合:

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

すべてのtimeタグはコンパイル時に一定であるため、タグを数値に変換する呼び出しを検索関数から削除したいと思います。テンプレート( http://en.wikipedia.org/wiki/Compile_time_function_execution )を使用してコンパイル時にいくつかの単純な関数を実行できることは知っていますが、nullで終了する文字列を繰り返し処理して中間値を保持する方法が正確にはわかりません。テンプレート内。nullで終了する文字列を繰り返し、パブリック変数に文字を追加する簡単なサンプルを教えてください。

4

6 に答える 6

12

あなたが欲しいのはブーストのように聞こえます。MPLboost::mpl::stringmpl::stringを使用してコンパイル時にを整数型に変換するメタ関数を作成することは、多かれ少なかれ簡単mpl::foldです(または、文字列リテラルが有効な整数値を表していない場合は、コンパイルに失敗します)。

編集:

私はあなたが何を探しているのか完全にはわかりません、それでここに解釈に応じて事実上2つの異なる答えがあります:


探しているのがコンパイル時の文字列から整数値への変換である場合(たとえば、コンパイル時"425897"に整数定数として認識される可能性がある場合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>
> { };

使用法は次のようになります。

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

負の数のサポートは実装されていますが、オーバーフロー検出のサポートは実装されていません(ただし、コンパイラーはおそらく警告を出します)。


探しているのがコンパイル時の文字列から整数値へのマッピングである場合(たとえば、コンパイル時"SomeTag"に整数定数として認識される可能性がある場合)、 Boost.MPL425897は問題を解決しますが、すべての文字列から整数へのマッピングです。 -値のマッピングは、コンパイル時に認識され、一元的に登録される必要があります。

#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もの)は、実装に意味がある場合は、関数パラメーターとしてではなく、テンプレートパラメーターとして整数値を取得するように作成できます。 。

うまくいけば、これはあなたの質問に答えます。

于 2011-05-03T12:34:46.780 に答える
5

コンパイル時に文字列リテラルを操作することはできないため、提案した方法では目的を達成できません。ただし、コンパイル時にこれらの文字列を処理することを検討している場合、これはコンパイル時にすべての文字列を知っていることを意味し、そこから、必要なものに許容できる近似値に到達する可能性があります。

あなたが示したコードは、誰かがタグを検索するたびに番号生成(ハッシュと呼びましょう)が呼び出されることを意味します。これを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
于 2011-05-03T14:09:19.517 に答える
2

文字列リテラルがコンパイル時にわかっている場合、それを文字列リテラルとして使用する理由はおそらくありません。列挙型または名前付き整数定数を使用できます。

文字列が変数によって検索関数に渡され、コンパイル時に不明な場合、コンパイル時に結果を取得する方法はありませんtoNumber()。次に、良い解決策は、ある種の辞書を使用することです(例std::map<std::string, int>

于 2011-05-03T12:44:30.717 に答える
0

コンパイル時ではありませんが、これは十分に高速だと思います。

void foo()
{
    const static auto someTagN = toNumber("SomeTag");
    type value = internal_search(someTagN );
}
于 2011-05-03T12:36:04.253 に答える
0

おそらくファッショナブルではないことは知っていますが、事前にハッシュテーブルを生成することができます。私はgperfを使用してそこに配管を生成するのが好きです。

私は知っています。あなたはコンパイルを長持ちさせる何かを望んでいました...ただ言ってください:)

于 2011-05-03T12:38:57.857 に答える
0

できるかどうかわからない。可能なタグのリストは小さいですか?そうでない場合でも、ほとんどの場合、それは小さいですか。その場合、タグのサブセットでテンプレートの特殊化を使用できます

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

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

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

(警告:私の特殊化構文が間違っている可能性があり、これがchar *で機能するかどうかはわかりません)

于 2011-05-03T12:39:15.750 に答える