2011-04-25 5 views
2

제한을 가할 수있는 숫자 (int 및 double) 목록 (std :: container 또는 list * 괜찮습니다)을 만들고 싶습니다.C++ : 한정된 숫자를위한 이질적인 템플릿

template<typename T> 
class setting { 
    public: 
    std::string name, units; 
    T value, min, max, step; 

    setting(std::string n, T val) : name(n), value(val) { } 

    setting operator++(int) { 
     setting tmp = *this; 
     value += step; if(value > max) value = max; 
     return tmp; 
    } 
}; 
... 
list.add(new setting<int>("channel", 4)); 
list.add(new setting<double>("amplitude", 5.6)); 
... 
for(int i = 0; i < list.size(); i++) 
    std::cout << list[i].name << ": " << list[i].value << std::endl; 

저는 여러 가지 방법을 시도했지만 그 중 하나에 만족하지 않습니다. 기본 유형은 유형을 알지 못하거나 사전 정의 된 유형을 가져야하기 때문에 기본 유형이`value '에 대해 알지 못하기 때문에 공통 기본에서 파생 될 수 없습니다. 매크로 템플릿으로 시도했지만 다운 캐스트는 엉성함을 느낍니다. 형식 식별자가있는 모든 유형의 조합을 사용하지 않고 올바른 구성원을 선택하는 대신이 작업을 수행 할 수있는 방법이 있습니까?

부스트 :: 오버로드 된 생성자와 변형 내가 컴파일 타임에 모든 유형을 열거하는 것을 제외하고, 지금까지 원하는 것을 할 것 같다 :

class setting { 
    public: 
     boost::variant< 
     bool, 
     int8_t, 
     uint8_t, 
     int16_t, 
     uint16_t, 
     int32_t, 
     uint32_t, 
     int64_t, 
     uint64_t, 
     float, 
     double, 
     std::string 
      > value; 

     std::string name; 

     setting(std::string n, int v) : name(n), value(v) { } 
     setting(std::string n, double v) : name(n), value(v) { } 
     setting(std::string n, std::string v) : name(n), value(v) { } 
}; 

typedef std::map<std::string, setting*> MapType; 
typedef MapType::const_iterator MapItr; 

int main() { 
    MapType settinglist; 

    settinglist["height"] = new setting("height", 1.3); 
    settinglist["width"] = new setting("width", 5); 
    settinglist["name"] = new setting("name", "the name"); 

    for(MapItr i = settinglist.begin(); i != settinglist.end(); ++i) { 
     std::cout << i->second->name 
     << " : " << i->second->value 
     << std::endl; 
    } 

    return 0; 
}; 

제공 : 아마

height : 1.3 
name : the name 
width : 5 
+0

이것이 라이브러리에서 이미 해결되었는지 궁금합니다. 이름 - 값 쌍을 사용하는 API는 숫자 속성에 대한 최소 및 최대 유효한 값과 마찬가지로 매우 일반적입니다. – paperjam

+0

명확하게 말하자면, 값이 기본형 또는 제한된 유형 중 하나를 얻을 수 있도록 하시겠습니까? 또한, 어떤 원시 또는 int 및 double 수 있습니까? –

+0

"value"는 int64_t가 필요함을 알 수 있지만 근본적인 형식 일 필요가 있습니다. – lister

답변

0

가상 toStringfromString이있는 일반적인 기본 클래스는 무엇입니까? 그러면 for 루프는

list<setting_base> lst; 
for(list<setting_base>::iterator it = lst.begin(); it != lst.end(); ++it) 
    std::cout << it->name << ": " << it->toString() << std::endl; 
+0

글쎄, 나는 일반적으로 "가치"를 조작 할 수 있기를 바란다. 방금 예를 들어 stdout을 사용했습니다. 문자열로 변환하면 값을 얻을 수 있지만 적절한 값 유형으로 변환해야 사용할 수 있습니다. 이것은 아마도 유용 할 것이지만, 내가 바라는 해결책은 아닙니다. 예를 들어, "value"를 "double"또는 무엇이든 다른 함수에 전달하고자 할 수 있습니다. 나는 기본 클래스에서 T가 무엇인지를 검색 할 수있는 함수가 있다고 생각했다. – lister

0

Boost.Operators에 일반형을 래핑하는 것은 어떻습니까?

template <class T, T max = numeric_limits<T>::max(), T min = 0> 
class Saturate 
    : boost::operators<Saturate<T, max, min>, T > 
{ 
private: 
    T _value; 
    void normalize() { 
     if(_value < min) _value = min; 
     if(_value > max) _value = max; 
    } 
    void toNormal(T t) { 
     if(t < min) return min; 
     if(t > max) return max; 
     return t; 
    } 
public: 
    Saturate(T t = T()) : _value(toNormal(t)) {} 
    T value() { return _value; } 
    Saturate& operator+=(const Saturate& x) 
     { _value += x._value; normalize(); return *this; } 
    Saturate& operator-=(const Saturate& x) 
     { _value -= x._value; normalize(); return *this; } 
    ... 
}; 
... 
std::vector<Saturate<int, 1023, -1023> > volume; 
... 
volume[3] = 50000; // really loud 
std::cout << volume[3].value(); // Not so loud 
+0

나는 벡터가 이질적이되기를 바라고, 내 목록에서 내가 원하는 유형을 미리 알지 못한다. 대부분 정수와 실제 목록으로 요약 할 수 있습니다.이 목록은 최대 2 개가 필요하지만 일반적으로 최소 2 개가 필요함을 의미합니다. 나는 그것이 정말 가까워지기 때문에 충분한 마술을 모를 것이라고 기대 했었습니다. . – lister

+0

@ 리스터 - 좋아, 나는 "이질적 유형"부분이 아닌 "한정된 회원"부분에 집중했다. [Boost.Any] (http://www.boost.org/doc/libs/1_46_1/doc/html/any/s02.html)를 보았습니까? –

+0

그건 제가 시도한 방법 중 하나였습니다. 당신은 any_cast에 어떤 타입을 넣었는지 알고 있어야합니다. 할당이 깨끗하고 검색시 추가 정보가 필요합니다. 지금 boost :: variant를보고 있습니다. 나는 또한 너무 나쁘지 않을 수도있는 유니온과 오버로드 된 접근자를 사용할 것을 고려했다. – lister

0

나는 모든 것을 하나의 유형 (int에 대해 잊어 버리거나, double을 사용하는 것을 잊지 말아야 함)에 맞추거나 더 일반적인 기본 유형을 정의해야한다고 생각합니다.

제네릭 기본 유형에 대해 지적한 문제는 일반적인 문제입니다. 컨테이너에있는 요소에 액세스 할 때 나중에 되돌릴 수없는 유형 정보가 손실됩니다.

나는 당신의 디자인 목표가 일관성이 없다고 생각합니다. 당신은 당신의 요구에 충분히 잘 맞는 (유형 불가지론적인) 제네릭 인터페이스를 만들거나 하나의 유형을 고수하고 그것에 충실해야합니다.

0

기본 클래스에 toInttoDoubletoInt64 메쏘드를 몇 개만 제공하면됩니다. 그런 다음 이중 값이 필요할 경우 요청하십시오. 그것은 모든 사람의 노력에 최소한의 노력이 필요합니다.

그렇지 않으면 일반 virtual int value() const 메서드를 virtual void value(Operator&) const으로 바꿀 수 있습니다. Operator은 각자가 행동하고자하는 유형 중 하나를 허용하는 가상 기능을 제공합니다. 기본적으로 : 당신이 당신의 기본 유형에 value를 호출 할 때

struct Operator { 
    virtual act(int) = 0; 
    virtual act(double) = 0; 
    //repeat for other types 
}; 

struct Base { 
    virtual value(Operator&) const = 0; 
}; 

는 가상 파견 올바른 구현이 호출됩니다 보장합니다. 이 구현 내에서 적절한 정적 유형을 act에 제공하고 오버로드 해결 방법을 시도합니다. 실제로 Operator 인스턴스 내에서 계산을 수행하거나 결과를 저장하고 접근자를 제공 할 수 있습니다. 전자의 경우에는 정수 유형이 더 빠르지 만 정밀도가없는 알고리즘을 사용하는 등 각 유형마다 고유 한 작업을 수행 할 수 있습니다. 후자의 경우에, 당신이 한 단계 더 갈 수 있으며, 물론 Base

struct Base { 
    //other stuff 
    template<typename Operator> 
    typename Operator::ResultType value() const { 
     Operator op; 
     value(op); 
     return op.result(); 
    } 
} 
// later 
cout << basePtr->get<IntGetter>(); 

내에서 템플릿 접근을 제공, 그 최종 결과는 내가 처음에 제안하는 일을하는 매우 복잡한 방법입니다.

/////// 원래 질문에 대한 편집을 방금 보았습니다. 가능한 많은 기본 유형을 사용하면이 방법이 훨씬 어려워집니다. Operator 클래스 내에 각 기본 유형의 오버로드를 제공해야합니다. 기본 구현을 기본 Operator 클래스에 제공 할 수 있습니다. 모든 정수형의 경우 act(int)을 호출하고 모든 부동 소수점 유형의 경우 act(double)을 호출하는 경우와 같이 구현 당 두 개의 필수 오버로드와 특정 사용 사례에 필요한 추가 오버로드가 모두 포함됩니다.

하지만 지금 나는 YAGNI를 바라보고 있습니다. 복잡한 기본 클래스가있어서 int 전체를 저장하지 않아도 몇 바이트를 절약 할 수있는 설정을 제공 할 수 있습니까? 정말로 모든 것을 double으로 저장할 수 있습니까? 정수 값을 정확하게 저장합니다. 또는 boost::variant을 사용하고 int, double 및 문자열로 제한하십시오.

+0

필자는 많은 형식을 허용할지 여부를 확인하기 위해 노력했지만, 이는 계측기 제어를위한 것이고 실제로 일부 기능은 실제로 구체적으로 수행하지 않습니다. 따라서 사용 가능하도록 설정하면 또 다른 전송을 절약 할 수 있습니다. 나는 그 모든 것을 IAGN 알기 때문에, 나는 정말로 필요한 경우 나중에 채워질 수있는 더 구체적인 것들을 생략 할 것이다. 또한 변형 스터프를 가지고 노는 동안 전체 클래스 대신 멤버 함수를 템플릿화할 수 있으며 변형 선언에 열거해야합니다. – lister