2010-08-10 5 views
2

일반적인 "서명되지 않은"클래스를 개발했거나 uint8_t을 매개 변수로 사용하여 C (C++) 기본 제공 서명이없는 클래스 템플릿 Unsigned<size_t N>을 개발했습니다. 예를 들어, Unsigned<4>uint32_t과 동일하고 Unsigned<32>은 존재하는 경우 uint256_t과 동일합니다.템플릿 : 런타임시 유형화되지 않은 매개 변수에서 인스턴스화 (및 참조)?

지금까지 나는 빌트인 부호없는 (특히 sizeof(Natural<N>)==N, (Natural<N>(-1) == "max_value_all_bits_1" == ~Natural<N>(0))), abs(), sign(), div (사용자 정의 div_t 구조체 사용)와의 호환성에 대한 대부분의 설명을 따르지 않았다.), ilogb() (GCC 전용) 및 numeric_limits <>를 사용합니다.

그러나 클래스 템플릿은 템플릿이기 때문에 템플리트 형식은 관련이 없으므로 문제가 있습니다. 템플릿에 형식이 지정되지 않은 매개 변수에는 "컴파일시 상수"가 필요합니다. 방법이 "const"보다 엄격합니다. 나는 본질적으로 을 알 수 없으므로 서명되지 않은 N을 만들 수 없습니다. 나는이 같은 코드를 가질 수 없습니다 즉

:

... 
(... assuming all adequate headers are included ...) 
using namespace std; 
using lpp::Unsigned; 
std::string str; 
cout<< "Enter an arbitrarily long integer (end it with <ENTER>) :>"; 
getline(cin, str, '\n'); 
const int digits10 = log10(str.length()) + 1; 
const int digits256 = (digits10 + 1) * ceil(log(10)/log(256)); // from "10×10^D = 256^T" 
// at this point, I "should" be able to, semantically, do this: 
Unsigned<digits256> num; // <-- THIS I CAN'T -- num would be guaranteed 
         // big enough to hold str's binary expression, 
         // no more space is needed 
Unsigned::from_str(num, str); // somehow converts (essentially a base change algo) 
// now I could do whatever I wanted with num "as if" a builtin. 
std::string str_b3 = change_base(num, 3); // a generic implemented somehow 
cout<< "The number above, in base 3, is: "<< str_b3<< endl; 
... 

(A/N - 이것은 "약간 많은 수의"(내가 가진 읽어 부호의있는 TestSuite의 일부입니다 그에 따라 N을 설정 한 후 최대 120 자까지 시도 함) 다른 모든 기지에서 표현하는 것과 같은 작업을 수행합니다.

이 제한을 무시하거나 완화 할 수있는 방법을 모색합니다 , 나는 시도하고 탐구하고 싶은 몇 가지 개념에 뛰어 들고 있었지만, 나는 너무 많은 노력을 대안으로 사용하고 싶지 않다. 더 복잡한 것을 만들거나 클래스의 행동을 너무 많이 벗어나게 만들뿐입니다.

내가 처음 생각한 것은 내가 선택한 것의 Unsigned<N>을 선택할 수 없다면 적절한 생성자로 이어질 N 개의 사전 선택된 값 집합에서 선택할 수있었습니다. 런타임에 호출되지만 컴파일 시간 값에 따라 달라집니다.

???? GetMeAnUnsigned (size_t S) { 
    switch (S) { 
    case 0: { throw something(); } // we can't have a zero-size number, right? 
    case 1, 2, 3, 4: { return Unsigned<4>(); break; } 
    case 5, 6, 7, 8: { return Unsigned<8>(); break; } 
    case 9, 10, 11, 12, 13, 14, 15, 16: { return Unsigned<16>(); break; } 
    .... 
    default: { return Unsigned<128>(); break; } // wow, a 1Kib number! 
    } // end switch 
    exit(1); // this point *shouldn't* be reachable! 
    } // end function 

저는 개인적으로 접근 방식을 좋아합니다. 그러나 반환 형식을 지정하는 데 사용할 수있는 것은 무엇인지 알 수 없습니다. 실제로 문제를 "해결"하지는 않으며 심각성을 어느 정도 저하시킵니다. 인스턴스화가 일 때 컴파일 타임 상수에서 일 때 전환 스위치를 사용하여 트릭을 수행 할 것이라고 확신하는 경우이 발생하는 만 변경됩니다.

반환 형식을 선언하는 유일한 실행 가능한 도움말은이 새로운 C++ 0 (1?) X "decltype"구조 인 것 같습니다.이 구조체를 사용하면 적절한 형식을 얻을 수 있습니다. 올바르게 기능 :

decltype (Unsigned<N>) GetMeAnUnsigned (size_t S) { 
    .. do some choices that originate an N 
    return Unsigned<N>(); 
    } 

... 또는 이와 비슷한 것. 나는 C++에 입력하지 않았습니까? X는 auto (iterators의 경우)을 넘지 만, 첫 번째 질문은 이고 decltype 또는과 같은 기능을 사용하면 원하는 것을 얻을 수 있습니까? (런타임 제한하는 경우에도 인스턴스의 선택) 대안에 대한

, 나는 문제가 내 수업 사이의 관계 있다면 나는 그들에게 모든는 "종류-의"자료를 만들 수 있다고 생각했다 템플릿 자체 유도로 : 음, 하나 하지 않습니다, 때문에하지만 ...

template <size_t N> 
class Unsigned : private UnsignedCommon { ... 

을 나는 내장 - 인 (모두 "-의 종류를"만드는)하는 백 버너에 그 접근 방식을 왼쪽 , 어떤 경우에는 일 때 더하기 일반 클래스로 그것을 정적 초기화, 포인터를 반환하고 제대로 호출 할 경우 클라이언트가 파괴해야합니다. 두 번째 질문 : 이 대안을 너무 일찍 폐기하는 것이 잘못 되었습니까?

+0

'sizeof'가 N을 얼마나 중요하게 생각합니까? 당신이 그것을 희생한다면, 템플릿이 아닌 대안들이 있습니다. – Troubadour

+0

"Unsigned Unknown Unknown N"을 목표로한다면 필요에 따라 동적으로 번호에 대한 저장소를 할당 할 수 있습니다. 궁극적으로 사용자가 결과에 필요한 인스턴스화를 파악하지 않고 'big_a + big_b'를 사용하기를 원하지 않습니까? – UncleBens

+0

@Troubadour : "sizeof() == N"불변량은 내가 "[unsigned] ints와 같아지기를 원했을 때 그 타입을 디자인 한 것입니다. 나는 그것을 떨어 뜨릴지도 모른다고 생각하지만, 나는 대부분의 정상적인 대안을 다 써 버린 후에야 마침내 버릴지도 모른다. –

답변

2

간단히 말해서 문제는 기본 제공되는 통합 유형의 문제와 다르지 않습니다. short이 주어지면 큰 정수를 저장할 수 없습니다. 그리고 당신은 런타임에 여러 개의 미리 정의 된 옵션 (short, int, long, long long, 예를 들어. 또는 귀하의 경우, Unsigned<4>, Unsigned<8>, Unsigned<256> 사이에서 선택할 수있는 switch 또는 유사한을 사용하지 않는 한 사용하는 정수의 유형을 결정할 수 없습니다 . 크기는 에서, 런타임에 동적으로 방법으로 계산 될 수 없다. 당신은 크기가 하지 템플릿 매개 변수입니다 (std::vector 유사) 동적 크기의 유형을 정의하는 단일 있도록하기 위해 중 하나가

을 타입은 어떤 타입의 정수를 저장할 수 있습니다. 또는 임의의 정수를 처리하는 유일한 옵션은 미리 정의 된 크기의 세트를 하드 코드하고 런타임 중에 선택하는 것입니다.

decltype 중 하나라도 문제가 해결되지 않습니다. 그것은 auto과 상당히 유사하며, 컴파일 타임에 완전히 작동하며, 표현식의 유형을 반환합니다. (2+2의 타입은 int이고 컴파일러는 컴파일 타임에 이것을 알고 있습니다. 4은 런타임에만 계산됩니다)

+0

하! 이제 내가 누락 된 점은 런타임 선택이 int로는 할 수없는 것입니다. 나는 "uintX_t"표기법 (예를 들어,'Unsigned256_t'이'Unsigned <32>'이 될 것입니다.)을 따르는 하드 코딩 된 유형을 배열 할 수 있고 클라이언트가 그와 함께 살 수 있습니다. 나는 많은 사람들을 모르는 것을 의미합니다. 3 또는 7 바이트 정수를 사용하십시오 ... 'decltype'에 대한 힌트를 주셔서 감사합니다. –

0

직접 할 수는 없습니다. 분리 된 숫자를 가진 각각의 unsigned는 별도의 타입을 가지며 컴파일러는 컴파일 타임에 메소드의 리턴 타입을 알아야한다.

Unsigned_base 기본 클래스는 Unsigned<t> 항목에서 파생됩니다. 그런 다음 GetMeAnUnsigned 메서드에서 Unsigned_base에 대한 포인터를 반환 할 수 있습니다. 그런 다음 dynamic_cast<Unsigned<8> >() 같은 것을 사용하여 캐스팅 할 수 있습니다.

함수가 가능한 unsigned<n> 유형의 합집합을 반환하는 것이 더 나을지 모르지만 유형이 조합원이되는 요구 사항을 충족하는 경우에만 작동합니다.

편집 : 여기에 예입니다 :

struct UnsignedBase 
{ 
    virtual ~UnsignedBase() {} 
}; 

template<std::size_t c> 
class Unsigned : public UnsignedBase 
{ 
    //Implementation goes here. 
}; 

std::auto_ptr<UnsignedBase> GiveMeAnUnsigned(std::size_t i) 
{ 
    std::auto_ptr<UnsignedBase> result; 
    switch(i) 
    { 
    case 42: 
     result.reset(new Unsigned<23>()); 
    default: 
     result.reset(new Unsigned<2>()); 
    }; 
    return result; 
} 
+0

아하, 가능한 해결책의이 쌍이 흥미로운 것 같습니다. 나는'UnsignedBase' 접근 방식을 고려해 보았습니다. 그러나 내가 말했듯이, 나는 그것이 일찍이 "버거울"것이기 때문에 그것을 일찍 버렸습니다. 문제를 해결할 수있는 포인터를 반환하면됩니까? 또한 ... 조합원의 요구 사항은 무엇입니까? 이런 식으로 내가 처음 들었습니다. 연속되는 스토리지, 구성 가능한 유형 등과 관련이 있습니까? –

+0

@ 루이스 : 포인터를 반환하지 않으면 슬라이싱 문제가 발생합니다. 나는 조합원이되는 요구 조건이 POD 유형과 동일하다고 생각하지만 나는 긍정적이지 않습니다. 'Unsigned '에 Vector와 같은 것을 넣을 필요가있는 경우, 당신은 노조에서 사소한 소멸자가 아닌 객체를 가질 수 없다는 것을 알고 있습니다. –

+0

빌리 : 설명 해줘서 고마워. Unsigned 의 소멸자는 사소한 AFAIK입니다. Unsigned 은 본질적으로 uint8_t [c]를 구현하기 때문에 중요하지 않은 생성자가 있지만 거기에는 배열의 의미에 대해 잘 모르겠습니다. OO 접근법이 갈 수있는 방법 일 수 있습니다. 나는 새로운 C++ 0X 기능이이 답변을 (필자의 특별한 문제에 대한) 결정적인 것으로 생각하기 전에 (위의 스위치 케이스를 고려하여) 유형을 반환하는 것을 도울 수 없다는 확인을 기다릴 것이다. 감사! –

1

당신이 직면하고있는 문제는 매우 일반적이다. 템플릿은 컴파일 타임에 해결되지만 런타임에 동작을 변경해야합니다. 신화 적으로 간접 접근법을 사용하여 추가 작업을 수행하려는 경우가 많으므로 문제는 해결되지 않습니다. 함수의 반환 유형을 선택할 수 없습니다.

런타임 정보를 기반으로 작업을 수행해야하므로 동적 다형성 (템플릿이 제공하는 정적 다형성 대신)을 사용해야합니다. 이는 GetMeAnUnsigned 메서드 내에서 동적 할당을 사용하고 아마도 포인터를 반환한다는 것을 의미합니다.

공용 인터페이스를 제공하고 내부 할당 된 객체에 위임하는 클래스 내부에 포인터를 숨기는 것 (예 : boost::any)과 동일한 스타일로 사용자가 단일 유형을 볼 수 있도록하는 몇 가지 트릭이 있습니다. 실제 객체는 런타임에 선택됩니다. 그렇게하면 설계가 더욱 어려워지고, 코드가 얼마나 복잡해 질지 확신 할 수 없지만, 외부 인터페이스의 요구 사항을 충족시키기 위해 내부 클래스 계층 구조에서 제공해야하는 최소한의 인터페이스가 무엇인지 생각해야합니다. - 이건 정말 재미있는 문제처럼 보입니다.

+0

고마워, 데이빗. 나는 또한 "간접비의 한 여분의 층"이 어느 시점에 날려 버릴 것이라고 생각했지만, * 일찍이 아닙니다. "boost :: any"는 흥미로운 것으로 들리지만, 아직 탐험해야 할 것들이 많이 있습니다. –

0

마지막으로 그것이 행렬 (템플릿 매개 변수로 차원 및 런타임 제공 값을 처리하는 방법)과 함께있는 것을 본 마지막 시간입니다.

불행히도 다루기 힘든 문제입니다.

이 문제는 C++ 그 자체와 관련이 없으며 컴파일 타임 검사와 결합 된 강력한 입력에만 적용됩니다. 예를 들어 하스켈도 비슷한 행동을 보일 수 있습니다.

이 처리하는 2 가지 방법이 있습니다 :

  • 당신은 switch이 유형을 생성하지 않는 사용하지만 실제로 전체 계산을 시작하는, 즉 main 거의 비어 만 입력 값을 읽어 역할을
  • 복싱을 사용합니다. 실제 유형을 일반 컨테이너 (직접 작성한 클래스 또는 boost::any 또는 boost::variant)에 넣은 다음 필요한 경우 특정 처리를 위해 값을 해제합니다.

저는 개인적으로 두 번째 방법을 선호합니다.

struct UnsignedBase: boost::noncopyable 
{ 
    virtual ~UnsignedBase() {} 
    virtual UnsignedBase* clone() const = 0; 

    virtual size_t bytes() const = 0; 

    virtual void add(UnsignedBase const& rhs) = 0; 
    virtual void substract(UnsignedBase const& rhs) = 0; 
}; 

그런 다음 당신은 당신이 의존하고 있다는 사실을 숨기기 (고객을위한 메모리 관리를 쉽게하기 위해 단순한 관리자에서이 클래스를 포장 :

이 작업을 수행하는 쉬운 방법은 기본 클래스 (인터페이스)를 사용하는 것입니다 힙 할당 + unique_ptr)에 : 여기

class UnsignedBox 
{ 
public: 
    explicit UnsignedBox(std::string const& integer); 

    template <size_t N> 
    explicit UnsignedBox(Unsigned<N> const& integer); 

    size_t bytes() const { return mData->bytes(); } 

    void add(UnsignedBox const& rhs) { mData->add(rhs.mData); } 
    void substract(UnsignedBox const& rhs) { mData->substract(rhs.mData); } 

private: 
    std::unique_ptr<UnsignedBase> mData; 
}; 

, 가상 파견 언 박싱 (다소)이 자리의 수를 알고 있다면, 당신은 또한 언 박스 수동)를 dynamic_cast (또는 static_cast를 사용하여 수 처리한다 :

void func(UnsignedBase* i) 
{ 
    if (Unsigned<2>* ptr = dynamic_cast< Unsigned<2> >(i)) 
    { 
    } 
    else if (Unsigned<4>* ptr = dynamic_cast< Unsigned<4> >(i)) 
    { 
    } 
    // ... 
    else 
    { 
    throw UnableToProceed(i); 
    } 
} 
관련 문제