2012-01-11 2 views
14

C++에서는 컴파일 타임 기능 만 사용하여 문자열 리터럴에서 정수를 생성 할 수 있습니까?컴파일 타임에 문자열 리터럴에서 정수를 생성하려면 어떻게합니까?

예를 들어 우리가 가지고있는 모든 문자가 "6"이라면, 템플릿 인자로 사용할 방법이 있습니까? ? 그러나 constexpr 대부분의 컴파일러에서 아직 준비가되지

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

, 그래서 아마 매크로와 TMP를 사용하여 솔루션을 찾고 있어요 :

나는 등 약 constexpr 기반 기술을 알고있다.

실험용이므로 미친 생각을 환영합니다.

+0

"대부분의 컴파일러"는 무엇을 의미합니까? "대부분의 컴파일러"가 아마도 가장 널리 사용되는 컴파일러 (gcc 및 Visual C++)를 포함한다면 대답은 할 수 없다는 것입니다. –

+4

따옴표없이 6을 쓰지 않는 이유는 무엇입니까? – littleadv

+0

문자 리터럴'GET_INTEGER ('6', '7', '8')'을 사용하면 작동하지만 문자열 리터럴이 작동하지 않는다고 생각합니다. – kennytm

답변

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'유형의 유형 템플릿 인수는 정수 상수 표현이 아닙니다. " 정말 무엇을


문자 리터럴 '1', '2', '3', '4', '5', '6', '7'의 목록에 "1234567" 리터럴 문자열을 파괴하는 것입니다. 인스턴스화

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

는 정수 1234567 ++ (즉, 더 이상 constexpr ☺)를 g의 외부에서 작동하지 않을 수 있습니다 비표준 동작을 포함 할 수 문자열 → 문자 그대로 단계로 목록을 설정하기 위해 호출하지만입니다 GetIntegerTemplate<...>::value은 휴대가 가능합니다.

+0

어쩌면'int'로 캐스팅 한 것일까 요? 지금 당장 LLVM이 없지만 테스트 할 가치가 있습니다. – slaphappy

+0

@kbok : 문제는 유형이 아니지만 clang의''1234567 '[0]'은 상수로 간주되지 않습니다. 이것은 gcc 확장자입니다 만 어디에서나 찾을 수 없습니다. – kennytm

+0

나는 문제가 '필수적이지 않다'고 생각했다. MSVC는 (" 'foo'에 대한 잘못된 템플릿 인수, 예상되는 컴파일 타임 상수 표현식); gcc 만 지원한다는 것은 수치 스럽지만, constexpr을 지원하는 유일한 컴파일러입니다. – slaphappy

-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에서 재개시)

당신은 예로부터 '리터럴 문자열'당신의 개념 정의를 변경 괜찮다면
"425897" ~ '4258','97'이면 Boost을 사용할 수 있습니다. 오버 플로우 탐지를위한 지원은 음수에 대한 지원이 구현

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; 

하지

(하지만 컴파일러는 아마 경고를 줄 것이다)처럼

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

사용이 보일 것이다 MPLboost::mpl::string<>이 작업을 수행하는 .

+0

재미있다. 그러나 '4258'은 무엇인가? – slaphappy

+0

@kbok :''4258 ','97''은 문자열 리터럴''425897 ''을 boost :: mpl :: string <>'에서 사용할 수있는 방법으로 표현하는 방법입니다. – ildjarn

+0

@ildjarn : 그 모든 부스트가 샘플 코드에 필요합니까? 내가 왜 내가 부스트를 사용하는 것을 싫어했는지 기억하고 있다고 생각한다. –

-1

가능한지 확실하지 않지만 시도 할 수있는 내용입니다.

문자 숫자의 값을 '0'만큼 감소시켜 그 값을 숫자로 얻을 수 있습니다.

처럼 : 하나의 숫자에 대한 문제를 해결할

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

.

숫자가 많은 숫자 (예 : "12345")를 해결하려면 모든 자릿수를 반복하고 결과를 합산해야합니다 (각 숫자에 10^pos를 곱한 값).

실행 시간에는 쉽게 할 수 있지만 컴파일 시간에는 그렇게 간단하지 않습니다.

"컴파일 시간 재귀"는 여기에 친구가 될 수 있습니다. 솔직하게 말해서, 나는 그것을 사용하는 어떤 해결책에서도 생각할 수 없었지만, 당신은 그것을 찾을 수있었습니다.

행운을 빈다.

관련 문제