Field alone is not a type, but a template which can generate a family of types, such as Field<int> and Field<double>. All these fields are not related such that the one is somehow derived from the other or such. So you have to establish some relation between all these generated types. One way is to use a common non-template base class:
class FieldBase { };
template <typename T>
class Field : public FieldBase {
private:
T value;
DataType<T> type;
};
class Row {
private:
std::map<unsigned long,FieldBase*> column;
};
And consider using smart pointer instead of that raw pointer in the code. Anyway, now the problem is that the type-information is lost - whether you point to a Field<double> or to a Field<int> is not known anymore and can only be detected by keeping some sort of type-flag in the base which is set by the templated derived class - or by asking RTTI using
dynamic_cast<Field<int>*>(field) != 0
But that's ugly. Especially because what you want there is a value semantic. I.e you would want to be able to copy your row, and it would copy all the fields in it. And you would want to get a double when a double is stored - without first using RTTI to hack your way to the derived type.
One way of doing it is to use a discriminated union. That is basically an union for some arbitrary types and in addition a type-flag, which stores what value is currently stored in that field (e.g whether a double, int, ...). For example:
template <typename T>
class Field {
private:
T value;
DataType<T> type;
};
class Row {
private:
std::map<unsigned long,
boost::variant< Field<int>, Field<double> > >
column;
};
boost::variant does all the work for you. You can use visitation to make it call a functor using the right overload. Have a look at its manual
Answer from Johannes Schaub - litb on Stack OverflowVideos
Field alone is not a type, but a template which can generate a family of types, such as Field<int> and Field<double>. All these fields are not related such that the one is somehow derived from the other or such. So you have to establish some relation between all these generated types. One way is to use a common non-template base class:
class FieldBase { };
template <typename T>
class Field : public FieldBase {
private:
T value;
DataType<T> type;
};
class Row {
private:
std::map<unsigned long,FieldBase*> column;
};
And consider using smart pointer instead of that raw pointer in the code. Anyway, now the problem is that the type-information is lost - whether you point to a Field<double> or to a Field<int> is not known anymore and can only be detected by keeping some sort of type-flag in the base which is set by the templated derived class - or by asking RTTI using
dynamic_cast<Field<int>*>(field) != 0
But that's ugly. Especially because what you want there is a value semantic. I.e you would want to be able to copy your row, and it would copy all the fields in it. And you would want to get a double when a double is stored - without first using RTTI to hack your way to the derived type.
One way of doing it is to use a discriminated union. That is basically an union for some arbitrary types and in addition a type-flag, which stores what value is currently stored in that field (e.g whether a double, int, ...). For example:
template <typename T>
class Field {
private:
T value;
DataType<T> type;
};
class Row {
private:
std::map<unsigned long,
boost::variant< Field<int>, Field<double> > >
column;
};
boost::variant does all the work for you. You can use visitation to make it call a functor using the right overload. Have a look at its manual
- You got an error there: you have to "value" member in Field (one should probably be "type").
- Please don't keep raw pointers in the map's value. Use boost::shared_ptr.
- Also, you should have a good reason for writing such classes where there are plenty of DB/table handling code out there already which you can probably use. So, if it's applicable, consider using something existing and not writing your own table handling code.
Now, to answer your question :), the Field<> classes can inherit from a common base class that's shared by all data types. This way a container such as your column map can keep pointers (make that shared pointers) to derived objects that are instanced of a template class.
What do you reckon this constructor does? Adding one value at the beginning of map?
It initialises the map so that map[x] == v for any x. The map associates intervals with values, internally storing a normal map keyed by the start of each interval; it's initialised so that the entire range of the key type maps to the initial value.
I see though in the respective key only an address as value after initializing in main. What is wrong? The operator [] is supposed to get the values for a specific key. However I cannot use it so as to get the elements of the map in the output. Any hint?
I've no idea what you're asking there. If you try, for example, cout << Map1[42] << '\n';, then your program should output 10, since that is the initial value assigned to the entire range of integers.
Consider also that it should be a function that inserts values to the map.
Since the internal map is publicly exposed, you can add a new interval to the map with
Map1.my_map.insert(std::make_pair(interval_start, value));
It might be more polite to make my_map private, and provide an insert() function to do that. You could also add a non-const overload of operator[] that inserts a new range and returns a reference to its value, something like
V & operator[](K const & key) {
V const & old_value = (--my_map.upper_bound(key))->second;
return *my_map.insert(std::make_pair(key, old_value)).first;
}
although this might not be a great idea, as you'd have to be careful that you don't accidentally insert many ranges when you only want to read the values.
My problem is how to iterate through the map to get all its elements and print them in main. It shows me an address with a value of the object initialization.
Remembering that an iterator over a map refers to a key/value pair (of type std::pair<K,V>), you should be able to iterator over the map like this:
for (auto it = Map1.begin(); it != Map1.end(); ++it) {
std::cout << it->first << " maps to " << it->second << '\n';
}
(in C++03, you'll need to write template_map<int,int>::const_iterator rather than auto).
What do you reckon this constructor does? Adding one value at the beginning of map? I see though in the respective key only an address as value after initializing in main. What is wrong?
It adds this one value in to the map. The iterator argument is only a hint: if the new item is to be inserted right after this position, the operation can be completed faster. Otherwise the map will need to find the right place to insert the new value as usual.
The operator [] is supposed to get the values for a specific key. However I cannot use it so as to get the elements of the map in the output. Any hint?
upper_bound returns iterator to the first key-value pair, where key is greater than the argument. --upper_bound therefore returns an iterator to the item, whose key is either equal or less than the queried key. If upper_bound returned map.begin(), because all keys are greater than the query, decrementing it is undefined behavior.
What you need here is the find member function. You also need to deal with the case the key is not found (map.end() is returned), e.g by throwing an exception.
Alternatively you may implement your operator[] in terms of map::operator[]. This means that the function can't be const, because map inserts a new default value if the key is not found.