我想创建一个可以对它们施加限制的数字(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
答案 0 :(得分:0)
可能是具有虚拟toString
和fromString
功能的公共基类?然后你的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)
我首先要说的是,您可以在基类中提供一些toInt
和toDouble
以及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
并将自己限制为整数,双精度和字符串。