Right, I am fairly new to c++ so I am still learning here. If I am going about this in the wrong way then tell me, but try to point me in the right direction if possible (perhpaps with a link to a tutorial).
I have being playing around with std::map and have used it to store an object (item). This works fine. The problem is trying to store derived items within the map. I have got it working but it seems to be slicing up the derived object.
So say item has the attributes a,b and c. and food is derived from item with the extra attributes d and e. I cannot access d and e when it is stored in a map of items. The compiler says:
"error: ‘class item’ has no member named ‘d’"
Is it possible to use std::map polymorphicaly or do I need to use another library like boost? Boost seems rather complex and I was hoping that there was a way to do it with map while I am still learning. Here is some of the code that I am playing with to make it clearer what I mean.
Map of items is declared as so:
typedef std::map <int, tItem*> itemMap;
Things are added to it like this:
Item * item = new Item (a, b, c);
itemmap [vnum] = item;
DerivedItem * deriveditem = new DerivedItem (a, b, c, d, e);
itemmap [vnum] = deriveditem;
This works, but I cannot access d and e of the derived item.
Thanks for the help guys
You can use dynamic_cast to cast back to the derived class, if you knwo what class it is.
dynamic_cast<DerivedItem*>(itemmap[vnum])->derivedFunction();
http://en.wikipedia.org/wiki/Dynamic_cast
If you want this to be done automatically, you can derive a new template class from std::map, where the [] operator has a template argument. But in this case you have to pass the type when you get the item:
itemmap<DerivedItem>[vnum]->derivedFunction()
You won't be able to access members specific to the DerivedItem class with an Item pointer. You could cast it:
val = static_cast<DerivedItem*>(itemmap[vnum])->d;
....but that depends on knowing which items are which type in the map.
For polymorphic behaviour, usually you would have a method in the parent class overridden in the derived classes that behaves differently.
Related
I just made an odd discovery and was wondering why it works this way. The following code throws a compiler error:
interface A
class B: A
val mapOfA: Map<A,A>
val mapOfB = mapOf<B,B>()
mapOfA = mapOfB
You get
Type mismatch.
Required: Map<A, A>
Found: Map<B, B>
But this code works.
val mapOfA: Map<A,A>
val mapOfB = mapOf<B,B>()
mapOfA = mapOfB.toMap()
The only difference is now I'm calling mapOfB.toMap(). mapOfB is already a Map so why does that change anything? I'm using Kotlin version 1.5.10. What's going on here?
Consider mapOfB.get. This accepts a B and only a B.
It is quite possible to have an implementation of mapOfB that cannot support get(A), that has no implementation for it. For example, imagine B is Int, and A is Number. Imagine mapOfB is actually implemented in terms of an array. mapOfA.get(3.14159) certainly can't look up the non-Int key in an array, since arrays are indexed by Ints.
(Kotlin chose this design in contrast to Java's design, which I'm not convinced was the right move -- but it's what they chose. Java's choice was for get, containsKey, and the like to take an Object argument, which results in questions like this.)
This is specifically specified in the definition of Map<K, out V>: upcasting V is allowed, but not K.
This is related to types variance. Consider this example:
val mapOfA: Map<A,A>
val mapOfB = mapOf<B,B>()
mapOfA = mapOfB // assume this is allowed
val item = mapOfA.get(A())
We did something weird here. Both variables point at the same map, so we just asked mapOfB for its A item. But mapOfB does not really know anything about A keys. It was supposed to work with B keys. It requires B in its get(), but we provided A. Therefore, we just broke the type-safety. This is why this is not allowed.
But why toMap() works properly? Because it creates a copy of the map. Now, asking mapOfA for A key asks only this copy, not map of B's. So this is allowed.
The type of a Map's key is invariant. That means a Map<B, B> is not a Map<A, B> or Map<A, A> because you cannot upcast an invariant type. Theoretically, the implementation of the Map interface being used could crash when passing it the wrong type of key, like if you passed it some subtype of A that is not a B.
When you call toMap, it creates a new Map, for which it is known to be safe to use the supertype A as a Key, so it can upcast the type safely. Under the hood, it is transferring each entry to a new map, so it's basically up-casting each of the keys to type A.
Here's an example of what the type safety protects you from:
interface A
class B(val name: String): A
class C: A
class MyMap: HashMap<B, B>() {
override fun get(key: B): B? {
println("I'm returning ${key.name}")
return super.get(key)
}
}
If you now did this and the compiler let you:
val a = Map<A, A>
val b: Map<B, B> = MyMap()
a = b // imagine this is allowed.
val x = a[C()] // Crash. C cannot be cast to B inside the MyMap.get() function
If you use toMap(), a new Map is being created from scratch and it will not have this problem so it is safe for the compiler to upcast the key type.
Java doesn't have this problem because get and contains, etc. do not take argument types of the key type, but accept anything. There are pros and cons to the two approaches. They each protect you from different types of bugs.
I created a type, which is a list of priority queues of strings (not my idea, I have to do it):
typedef list<priority_queue<string>> L;
L myList;
Now I need to create a class M, which will inherit from this type. From what I acknowledged, I need to use templates to do so, but I still have no idea how to do it and I haven't found anything online.
How do I make a class inherit from a type?
It's as simple as:
class foo : L { // Note this is private inheritance
};
The type alias is just a name for another type, not a type in its own right. Since std::list is a class template and L names a complete specialization, you can just do it.
Just be sure not to use it in any polymorphic way that involves deleteing a pointer to a std::list. It's not designed for it, since it lacks a virtual destructor.
This is my (maybe a little bit weird) thought, suppose I want to define a std::set object to contain some stuff for later use, but unfortunately I'm not sure which type will be passed to std::set<not-sure> as template arg, and this not-sure type will be determined through a string, like this:
class X {
public:
foo()
{
char not_sure_type[20];
scanf("%s", not_sure_type);
if (strcmp(not_sure_type, "int"))
// then std::set<int>
else if (// "char")
// then std::set<char>
}
private:
void * _set;
};
This way, I can determine that std::set<int> will be instantiated or not, right? But how can I tell _set that you should point to a std::set<int>? Without knowing that, either I cannot use static_cast to cast _set from void * to std::set<int>*, back and forth.
So can I save the std::set<int> just like an data member for later use?
Any idea is appreciated.
If you will know the the type of the set element at run-time (based on a say string), you could maybe store a pointer to an abstract type into the set (set), and then use an Abstract Factory in the constructor of the class that holds the std::set to instantiate the actual Concrete Types for the elements during run-time based on the provided string.
The problem is in using raw pointers here, since you will need to do the cleanup within the class that has std::set. Since you want to use std::set, make sure that your Concrete Type for the element is Comparable. Not sure if this is the right way to go though.. you said to throw in ideas...
sounds to me like you are considering using c++ as a weak type language, such as python. sure there could be workarounds like using some abstract base class etc. but the bottom line I think is that defining the type at run time is against the paradigm of c++..
I am having to add new functionality to some old, poorly written code. There are 50 different lists which need to be displayed and processed on screens, and the current code for them is all cut-and-paste with minor modifications from 2 different types, one a list in a DAO database and the other in a map.
I wrote a Search class which provides the functionality, and a helper class for each of the list types to provide the basic functions needed by Search. The map helper class only requires access to the tstring Key, it does not need the differentObject Values. But, I can't get this to compile.
Since the various differentObjects have no relationship, I defined a class SearchValue which is basically empty, and added it in the .h as a superclass for all the differentObject classes.
As an example, here is one of the map definitions:
typedef map<tstring, MyPatListEntry *, tstringcomp> patlist;
MyPatListEntry is defined:
class MyPatListEntry : public SearchValue {
and I have the function defined as:
ListHelper(map<tstring, SearchValue *> *map, CString fieldName)
The compiler (VC++) gives the error that none of the definitions for ListHelper() handles all the arguments. If I replace SearchValue with MyPatListEntry in the definition the compilation works, so the basic format is correct.
I've looked around on the site and found people suggesting this type of thing be done with function templates, and I suppose that would work, but I am curious whether there is some reason doing it this way does not work.
Thanks for any thoughts.
What you are asking for is called covariant generic type parameters in C# world (not possible in C++) and even there it would not work in your situation. The reason is actually quite straightforward.
Imagine the following code:
class B {};
class D1 : public B {};
class D2 : public B {};
map<string, D1 *> myMap;
D2 someObject;
void myFunc(map<string, B *> & someMap)
{
someMap["foo"] = &someObject;
}
You are not allowed to call myFunc with myMap as a parameter because of this problem. You would be allowed to assign someObject of type D2 into a map that is supposed to contain D1.
If you really need ListHelper to have different behavior over different SearchValue types, it may be appropriate to template the function over the value type of your map:
template <class AnySearchValue> ListHelper(map<tstring, AnySearchValue *> *map, CString fieldName)
It's hard to say if that's workable without seeing more of the implementation though. (oops sorry, missed in your OP where you said you'd considered this)
I am considering a factory function to create different classes in the same hierarchy. I understand that normally a factory is normally implemented as follows:
Person* Person::Create(string type, ...)
{
// Student, Secretary and Professor are all derived classes of Person
if ( type == "student" ) return new Student(...);
if ( type == "secretary" ) return new Secretary(...);
if ( type == "professor" ) return new Professor(...);
return NULL;
}
I am trying to think of a way so that the process can be automated so that the various conditions do not need to be hard-coded.
So far the only way I can think of is using a map and the prototype pattern:
The map will hold the type string in the first element and a class instance (prototype) in the second:
std::map<string, Person> PersonClassMap;
// This may be do-able from a configuration file, I am not sure
PersonClassMap.insert(make_pair("student", Student(...)));
PersonClassMap.insert(make_pair("secondary", Secretary(...)));
PersonClassMap.insert(make_pair("professor", Professor(...)));
The function may look something like this:
Person* Person::Create(string type)
{
map<string, Person>::iterator it = PersonClassMap.find(type) ;
if( it != PersonClassMap.end() )
{
return new Person(it->second); // Use copy constructor to create a new class instance from the prototype.
}
}
Unfortunately, the prototype method only works if you only want the class created by the factory to be identical every time, since it does not support arguments.
Does anybody know if it is possible to do it in a nice way, or am I stuck with the factory function?
I usually build a factory method (or a factory object) when the clients will be providing some information about the object to be created, but they don't know what concrete class the result will be. The determination about how to express the interface to the factory depends completely on what information the clients have. It could be that they provide a string (program text to be parsed, for example), or a set of parameter values (number of dimensions and sizes if we're creating geometric objects in n-space). The factory method then examines the information and decides what kind of object to create or which more specific factory to call.
So the decision about what to build shouldn't be made by the caller; if she knows, then there's no reason for a factory. If the list of things to be built is open-ended, you might even have a registration protocol that allows specific implementations to provide both their construction method and a discriminator function that would allow the factory method to decide which method to call.
It very much depends on what information is necessary and sufficient to decide which kind of object to build.
You can register a factory method (instead of the prebuilt element to copy). This will allow you to call the abstract factory with parameters that are passed to the concrete factory. The limitation here is that the set of parameters of all concrete factories must be the same.
typedef std::string discriminator;
typedef Base* (*creator)( type1, type2, type3 ); // concrete factory, in this case a free function
typedef std::map< discriminator, creator > concrete_map;
class Factory // abstract
{
public:
void register_factory( discriminator d, creator c ) {
factories_[ d ] = c;
}
Base* create( discriminator d, type1 a1, type2 a2, type3 a3 )
{
return (*(factories_[ d ]))( a1, a2, a3 );
}
private:
concrete_map factories_;
};
I have used free function creators to reduce the sample code, but you can define a concrete_factory type and use it instead of the 'creator' element above. Again, as you can see, you are limited to a fixed set of arguments in the factory 'create' method.
Each concrete factory can pass the arguments to the constructor of the given type:
Base* createDerived1( type1 a1, type2 a2, type3 a3 )
{
return new Derived1( a1, a2, a3 );
}
This is more flexible than your approach as you can create instances that hold references to external objects (those can only be initialized during construction) or constant members, or objects that cannot be reset to a different state after construction in a more general wording.
I would add a pure abstract clone method to class Person (which definitely looks like it should be an abstract class, existing mainly for the sake of being subclassed -- if you need a concrete "none of the above" kind of Person it's best done via a separate concrete subclass OtherKindOfPerson, rather than as the base class itself):
virtual Person* clone() const = 0;
and override it in every concrete subclass, e.g. in Student, with a new that invokes the specific concrete subclass's copy ctor:
Person* clone() const { return new Student(*this); }
You also need to change the registry map to:
std::map<string, Person*> PersonClassMap;
[[You could use some smarter pointer than a plain old Person *, but as the map and all of its entries probably needs to survive as long as the process does, this is definitely not a big deal -- the main added value you might get from smarter pointers being smarter behavior upon destruction of the "pointer"!-)]]
Now, your factory function can simply end with:
return it->second->clone();
The changes are needed to avoid the "slicing" effect of using the base class's copy ctor on a subclass that has extra attributes, as well as preserve any virtual method's resolution.
Subclassing a concrete class to yield other concrete classes is a bad idea exactly because these effects can be tricky and a source of bugs (see Haahr's recommendation against it: he writes about Java, but the advice is also good for C++ and other languages [indeed I find his recommendation even more crucial in C++!].
I am not familar with c++ but in many langugaes there concepts of delegates or closures. Means that instead of mapping to instance, you map to the function(delegate, closure) that is responsible to create object.
You could make an enum of each type of person:
enum PersonType { student, secretary, professor };
Well if you want a faster way to do it then using an enum and a switch statement will be many time faster than processing sequential if/else if statements ...
If you look at your two implementations, logically they are identical.
The first implementation is the same as your second if the recursive loop was unrolled. So really there is no advantage of your second implementation.
Regardless what you do you will need to list some where your types mapped to your constructors.
One way of doing it that can be useful is to have this map in a seperate xml file
<person>
<type> student </type>
<constructor> Student </type>
</person>
....
You can then read this xml file in to memory and use reflection to get back your constructor. For the given type. Given that you are using C++ however this will not be that simple as C++ does not have reflection as standard. You will have to look for an extension to provide you with reflection in C++.
But regardless, all these alternatives can not escape what you did in your original implementioan, ie: list the mapping from type to constructor and search the map.