2012-01-11 30 views
14

Trong C++, có thể tạo ra một số nguyên từ một chuỗi ký tự bằng cách sử dụng các cơ sở biên dịch không? Ví dụ:Làm cách nào để tạo số nguyên từ chuỗi ký tự ở thời gian biên dịch?

Ví dụ: nếu tất cả chúng ta có chữ "6", có cách nào để sử dụng nó làm đối số mẫu, chẳng hạn như std::array<GET_INTEGER("6")> a; không?

tôi biết về constexpr kỹ thuật dựa trên, chẳng hạn như:

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

Tuy nhiên constexpr chưa sẵn sàng trên hầu hết các trình biên dịch, vì vậy tôi đang tìm kiếm các giải pháp sử dụng có thể macro và TMP.

Chỉ để thử nghiệm, các ý tưởng điên rồ được hoan nghênh.

+0

"Hầu hết các trình biên dịch" nghĩa là gì? Nếu "hầu hết các trình biên dịch" bao gồm những gì có lẽ là trình biên dịch được sử dụng rộng rãi nhất (gcc và Visual C++) thì câu trả lời là nó không thể được thực hiện. –

+4

Tại sao không chỉ viết 6, không có dấu ngoặc kép? – littleadv

+0

Nó có thể hoạt động nếu bạn sử dụng các ký tự chữ 'GET_INTEGER ('6', '7', '8')', nhưng tôi không nghĩ chuỗi ký tự chuỗi sẽ hoạt động. – kennytm

Trả lời

4

Rõ ràng gcc cho phép "abcd"[3] được hiểu là 'd', cho phép này để làm việc (ít nhất là trên g ++ - 4.6 và 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"); 
} 

Nhưng nó sẽ không biên dịch trên kêu vang, mà nói "không loại đối số mẫu của loại 'const char' không phải là một biểu thức hằng số tích phân ".


gì nó thực sự là để phá vỡ chuỗi chữ "1234567" vào một danh sách các nhân vật literals '1', '2', '3', '4', '5', '6', '7'. Instantiation

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

sau đó được viện dẫn để chuyển danh sách vào một số nguyên 1234567. Chuỗi → char bước theo nghĩa đen có thể liên quan đến hành vi phi tiêu chuẩn mà có thể không làm việc bên ngoài g ++ (tức là tồi tệ hơn constexpr ☺), nhưng điều đó GetIntegerTemplate<...>::value là di động.

+0

Có thể bằng cách truyền tới 'int'? Tôi không có LLVM ngay bây giờ, nhưng đó có thể là giá trị thử nghiệm. – slaphappy

+0

@ kbok: Vấn đề không phải là loại, nhưng rằng '" 1234567 "[0]' trong tiếng kêu vang không được coi là một hằng số. Đây là một phần mở rộng gcc nhưng tôi không thể tìm thấy bất cứ nơi nào đề cập đến điều này. – kennytm

+0

Tôi hiểu, tôi nghĩ rằng vấn đề là nó không phải là 'không thể thiếu'. MSVC không thích điều đó ("đối số mẫu không hợp lệ cho 'foo', biểu thức hằng số biên dịch dự kiến"); đó là một sự xấu hổ mà chỉ gcc hỗ trợ nó, trong khi đó là trình biên dịch duy nhất hỗ trợ constexpr. – slaphappy

-1

Có thể?

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

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

(reposting từ another answer of mine)

Nếu bạn không nhớ thay đổi định nghĩa về khái niệm của bạn về 'chuỗi chữ' từ ví dụ
"425897" đến '4258','97', sau đó bạn có thể sử dụng Boost. MPL 's boost::mpl::string<> để thực hiện điều này:

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

Cách sử dụng sẽ như thế nào:

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; 

Hỗ trợ cho số âm được thực hiện, hỗ trợ để phát hiện tràn không phải là (nhưng trình biên dịch của bạn có thể sẽ đưa ra một cảnh báo) .

+0

Đó là thú vị, nhưng những gì là '' 4258''? – slaphappy

+0

@kbok: ''4258', '97'' là cách biểu diễn chuỗi chữ" "425897" 'theo cách có thể sử dụng với' boost :: mpl :: string <> '. – ildjarn

+0

@ildjarn: Tất cả những tăng cường đó có cần thiết cho mã mẫu đó không? Tôi nghĩ rằng tôi nhớ lý do tại sao tôi ghét sử dụng tăng ... –

-1

Tôi không chắc chắn nếu nó có thể, nhưng đó là một cái gì đó bạn có thể thử.

Bạn có thể giảm giá trị của một chữ số của ký tự bằng '0' để nhận giá trị đó bằng số.

Giống như:

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

Điều đó sẽ giải quyết vấn đề của bạn cho một chữ số.

Để bạn giải quyết một số có nhiều chữ số (như "12345"), bạn sẽ phải lặp lại tất cả các chữ số và tổng kết quả (mỗi số nhân với 10^pos).

Điều đó sẽ dễ thực hiện trong thời gian thực thi, nhưng trong thời gian biên dịch, điều đó không đơn giản.

"đệ quy thời gian đệ quy" có thể là bạn của bạn ở đây. Thành thật mà nói, tôi không thể suy nghĩ trong bất kỳ giải pháp sử dụng nó, nhưng bạn có thể tìm thấy một.

Chúc may mắn!

Các vấn đề liên quan