c ++:有界数字的异构模板

时间:2011-04-25 19:20:03

标签: c++ templates

我想创建一个可以对它们施加限制的数字(int和double)的列表(std :: container甚至list *就可以了)。

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;

我尝试了几种不同的方法,但我对其中任何一种都不满意。无法从公共基础派生,因为基类型不知道“值”,因为它不知道类型或必须提前定义类型。尝试使用宏模板,但垂头丧气感觉马虎。有没有办法做到这一点,而不诉诸所有类型的联合与类型标识符,以选择正确的成员?

带有重载构造函数的boost :: variant似乎做到了我想要的,除了必须在编译时枚举所有类型:

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

4 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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

答案 2 :(得分:0)

我认为你必须要么把所有东西都装到一个类型中(意思是忘记int,只使用double),或者定义一个更通用的基类型。

您在通用基本类型中注意到的问题是它非常通用。当您访问容器中的元素时,您将丢失以后无法获取的类型信息。

我认为您的设计目标不一致。你应该创建一个足以满足你需求的通用接口(那种类型不可知)或选择一种类型并坚持下去。

答案 3 :(得分:0)

我首先要说的是,您可以在基类中提供一些toInttoDouble以及toInt64方法。然后,如果您需要双倍值,您只需要它。每个人都需要花费最少的精力。

如果失败,您可以使用virtual int value() const替换常规virtual void value(Operator&) const方法。 Operator将提供自己的虚函数,每个函数都接受您想要操作的类型之一。基本上:

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

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

当您在基本类型上调用value时,虚拟调度将确保调用正确的实现。在该实现中,您为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并将自己限制为整数,双精度和字符串。