2012-01-11 37 views
14

在C++中,是否可以使用編譯時設施從字符串文字生成整數?例如,如果我們所有的都是文字「6​​」,是否有某種方法可以將它用作模板參數,如std::array<GET_INTEGER("6")> a;如何在編譯時從字符串文字生成整數?

我知道constexpr基於技術,如:

template <int N> constexpr char get_char(const char s[N], int n) { 
    return s[n]; 
} 

然而constexpr還沒有準備好對大多數編譯器,所以我在尋找可能使用宏和TMP的解決方案。

這只是爲了實驗,所以瘋狂的想法是值得歡迎的。

+0

「大多數編譯器」是什麼意思?如果「大多數編譯器」包含可能使用最廣泛的編譯器(gcc和Visual C++),那麼答案就是無法完成。 – 2012-01-11 18:29:07

+4

爲什麼不寫6,沒有引號? – littleadv 2012-01-11 18:30:00

+0

如果你使用字符文字'GET_INTEGER('6','7','8')',它可以工作,但我不認爲字符串文字會起作用。 – kennytm 2012-01-11 18:31:28

回答

4

顯然GCC允許"abcd"[3]被解釋爲'd',這使得這個工作(至少在G ++ - 4.6和4.7):

#include <boost/preprocessor/repetition/enum.hpp> 

template <const char... characters> 
struct GetIntegerTemplate; 

template <const char head, const char... rest> 
struct GetIntegerTemplate<head, rest...> 
{ 
    typedef GetIntegerTemplate<rest...> Prev; 
    enum 
    { 
     power = Prev::power * 10, 
     value = (head - '0') * Prev::power + Prev::value 
    }; 
}; 

template <> 
struct GetIntegerTemplate<> 
{ 
    enum 
    { 
     power = 1, 
     value = 0 
    }; 
}; 

#define GET_NTH_CHARACTER(z, n, data) data[n] 
#define GET_INTEGER(length, the_string) GetIntegerTemplate<BOOST_PP_ENUM(length, GET_NTH_CHARACTER, the_string)>::value 

int main() 
{ 
    static_assert(GET_INTEGER(7, "1234567") == 1234567, "oops"); 
} 

但它不會對鐺,它說「非編譯類型'const char'的類型模板參數不是一個整型常量表達式「。


什麼它確實是打破字符串文字"1234567"成字符文字'1', '2', '3', '4', '5', '6', '7'的列表。然後,將實例化

GetIntegerTemplate<'1', '2', '3', '4', '5', '6', '7'>::value 

被調用,以關閉該列表爲整數1234567字符串→炭字面步驟可以包括非標準的行爲可能不克外工作++(即差於constexpr☺),但是這GetIntegerTemplate<...>::value是便攜式。

+0

也許通過轉換爲'int'?我現在沒有LLVM,但這可能值得測試。 – slaphappy 2012-01-11 18:53:29

+0

@kbok:問題不在於類型,但是在clang中'1234567「[0]'不被視爲常量。這是一個海灣合作委員會擴展,但我找不到任何提及這一點。 – kennytm 2012-01-11 18:58:41

+0

我明白了,我認爲問題在於它不是「整體」。 MSVC不喜歡那樣(「無效的模板參數爲'富',預期的編譯時常量表達式」);這是一個遺憾,只有gcc支持它,而它是唯一支持constexpr的編譯器。 – slaphappy 2012-01-11 19:01:55

-1

也許?

template<int C> 
struct get_char 
{ 
    static const int value = C - 48; 
}; 

static_assert(get_char<'0'>::value == 0, ""); 
2

(從another answer of mine轉貼)

如果你不介意例如改變你的概念定義「字符串常量」您可以使用BoostMPLboost::mpl::string<>做到這一點:

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

使用看起來像:

int i = string_to_integral<int, '4258','97'>::value; 
// or 
typedef boost::mpl::string<'4258','97'> str_t; 
unsigned j = string_to_integral2<unsigned, str_t>::value; 

爲負數的支持實現,溢出檢測的支持不是(但你的編譯器可能會給出警告) 。

+0

這很有趣,但什麼是'4258'? – slaphappy 2012-01-11 18:37:40

+0

@kbok:''4258','97'是以'boost :: mpl :: string <>'可用的方式表示字符串字面值'「425897」'的方式。 – ildjarn 2012-01-11 18:39:16

+0

@ildjarn:這些示例代碼是否需要所有這些提升?我想我記得爲什麼我討厭使用提升... – 2012-01-11 19:00:54

-1

我不確定是否有可能,但那是你可以嘗試的。

您可以將字符數字的值減少'0'以獲取數值中的值。

像:

char a = '5'; 
int num = a - '0'; 

這將解決你的問題的一個數字。

爲了解決許多數字(如「12345」)的數字,您必須循環所有數字並將結果相加(每個數字乘以10^pos)。

這樣做在執行時很容易,但在編譯時並不那麼簡單。

「編譯時遞歸」可能是你的朋友在這裏。說實話,我無法想到使用它的任何解決方案,但你可能會找到一個。

祝你好運!