2011-05-03 132 views
12

我的代碼中有字符串標籤,它們被轉換爲數字並用於在標籤值結構中搜索值。C++編譯時間函數執行

我有這樣的事情:

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),但我不知道如何遍歷空終止的字符串並保留模板中的中間值。你可以給一個簡單的例子迭代一個空終止的字符串,並在公共變量中添加字符嗎?

+1

作爲替代,也許'toNumber'能保持地圖的所有標籤它已經看到了,以及它們的數值,這樣你只需要爲每個標籤支付一次轉換成本?它不像使用模板語言在編譯時進行評估那樣高效,但我敢打賭,這樣的代碼將更易於閱讀和維護。 – aroth 2011-05-03 12:33:06

+1

TTBOMK你不能在編譯時迭代字符串。 – sbi 2011-05-03 12:34:41

+0

@aroth我認爲toNumber函數比搜索更快,即使我會使用log2搜索。 – Felics 2011-05-03 12:34:58

回答

4

你不能在編譯時對字符串文字進行操作,所以你想要的東西在你建議的方式下是不可行的。但是,如果您打算在編譯時處理這些字符串,那麼這意味着您在編譯時知道所有字符串,並且可能會達到您想要的可接受的近似值。

您顯示的代碼意味着每次有人搜索標籤時都會調用數字生成(我們稱之爲哈希)。將這個減少到一個調用是可以接受的嗎?如果是這樣,你可以定義常量和使用這些替代字符串:

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 
+0

這可能是一個好主意,因爲它解決了枚舉的大問題(我不需要在所有項目文件中包含頭文件並在每次添加新標籤時重新編譯它)以及本地定義(標記衝突)。這裏唯一的問題是在不同的文件和鏈接器錯誤中重新定義相同的標籤,但是..這可以用靜態const int而不是const int來解決。謝謝! – Felics 2011-05-03 14:19:23

+1

那麼你是否在使用字符串,因爲你不想將枚舉包含在任何地方?我想你知道你實際上正在爲運行時安全性進行編譯時性能交易嗎?這真的值得嗎?過去我在這種情況下做的是將值空間(在你的情況下:enum值)分解爲獨立的塊(你必須使用const int),這些塊在不同的頭文件中定義。然後,您只包含您感興趣的一個標題,並且不受其他標題中的更改影響。 – sbi 2011-05-03 15:53:23

12

這聽起來像你想要的是BoostMPLboost::mpl::string。在編譯時使用mpl::fold編寫一個元函數來將mpl::string轉換爲整數類型(或者如果字符串文字不代表有效的整數值,則編譯失敗),這或多或少是微不足道的。

編輯:

我不完全知道你在找什麼,所以這裏是依賴於有效地詮釋兩種不同的答案:


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)可以進行取整數值作爲模板參數,而不是作爲一個函數參數如果這樣做對其實現有意義。

希望這可以回答你的問題。

+0

我不會說,它是微不足道的(至少不是我),但它可以做到。 – 2011-05-03 12:47:22

+0

請給我一個實現。這是最受投票的答案,但我認爲這是不可能的,正如其他人所說的那樣。當我有完整的答案時,我會投票。 – Felics 2011-05-06 09:46:42

+1

@Felics:用完整的實現進行編輯。我給了兩種方法,因爲我不清楚你真的在尋找什麼...... – ildjarn 2011-05-06 14:49:22

0

通過不編譯的時間,我認爲這對你來說足夠快;

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

這不會在編譯時運行,而是在全局構建時代,所以它會延長程序啓動時間。 – 2011-05-03 12:37:00

+0

這是我想避免的,在運行時進行字符串轉換。我不會使用很多次使用的標籤,我有不同的標籤只能使用一次或幾次。 – Felics 2011-05-03 12:38:33

+0

好吧,忽略這個然後=) – 2011-05-03 12:39:00

0

我知道這可能並不時髦,但您可以提前生成散列表。我喜歡用gperf在那裏生成管道。

我知道知道。你想要的東西,使編譯持續更長的時間......只是說:)

+0

這不是問題,對於調試,它可能是#define X(tag)toNumber(tag)並釋放#define X(tag)someTemplate :: number。這是爲了運行時優化,編譯時間是不相關的。 – Felics 2011-05-03 12:46:15

+0

我半開玩笑,我不擔心編譯時間:) – sehe 2011-05-03 12:54:59

+0

編譯時間是個大問題。在我目前的項目中,完全重建可能需要幾個小時,這是一個非常大的問題:) – Felics 2011-05-03 13:08:13

0

不知道你可以。可能的標籤列表很小嗎?即使不是,大部分時間都很小。如果是這樣,你可以在標籤

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

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

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

的子集,使用模板特(警告:我的專業化語法可能是錯的,我不知道,如果這個工程對於char *)

+2

模板不接受字符文字,只接受常量的地址。所以你必須爲字符串創建'const char * const'變量並將這些常量傳遞給模板。 – 2011-05-03 12:43:34

+1

字符串文字是左值,因此不能是編譯時常量。但是,*字符*文字確實可以是編譯時常量。 – ildjarn 2011-05-03 12:50:19

2

如果字符串字面量在編譯時是已知的,那麼可能沒有理由將它用作字符串文字。您可以使用枚舉或命名的整型常量。

如果字符串通過變量傳遞給搜索函數並且在編譯時不知道,那麼編譯時就沒有辦法做到這一點。然後一個好的解決方案是使用某種字典(例如std::map<std::string, int>

+0

正如我之前所說的,編號函數比搜索更快。 – Felics 2011-05-03 12:51:03

+0

我認爲這裏的第一段是關鍵。要在編譯時執行此操作,您需要在編譯時瞭解所有可能的字符串。而當你有這些時,沒有理由不爲他們使用符號常量。 – sbi 2011-05-03 14:01:46