Confusion about Maps and generics - list

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.

Related

Kotlin seemingly recursive template type that compiles

Doing code review at work and came across a use of template types I've not seen before. Upon first glance it looked like the code shouldn't compile as the definition seemed recursive. I've boiled it down to the most simple verifiable example:
interface Bar<T>
interface Foo<T: Bar<T>> // Surely this is recursive?
My understanding of how template types work is:
interface Foo<T> - a Foo of T, no constraints
interface Foo<T : Bar> - a Foo of T, where T is constrained to a Bar
Assuming what I said above is true, then this doesn't make sense to me:
interface Bar<T> - a Bar of T, no constraint on T
interface Foo<T: Bar<T>> - a Foo of T, where T is constrained to a Bar<T>
Uh oh, how can T be defined in terms of Bar<T>?.
We know T is a Bar<T>, so if we substitute the T in Bar<T>, well it's a Bar<Bar<T>>.
we still haven't resolved T for Bar... For sake of argument, let's substitute T again. Now we have T being a Bar<Bar<Bar<T>>>. Surely this goes into infinity no?
CRTP (recursively bounded quantification) is a well-known design idiom which is often used (among other things) to provide generic code with some sort of a "self" type.
Here's a practical example of recursive generics.
Say you have a function that operates on a set of comparable values.
fun <T> findMax(collection: Collection<T>): T?
Ideally, we would like to constrain this function to only operate on collections of Comparable values:
fun <T> findMax(collection: Collection<Comparable<T>>): Comparable<T>?
And that's all. Right?
While this will work, you'd need a cast on the return value for it to do anything useful, since it returns a Comparable<T> rather than a T.
Now let's say we try:
fun <T : Comparable<T>> findMax(collection: Collection<T>): T?
Much better. This ensures:
T is Comparable
and more importantly, T is comparable to itself
The same applies to classes and inheritance.
interface SelfReturner<T : SelfReturner<T>> {
fun getSelf(): T
}
class A : SelfReturner<A> {
override fun getSelf(): A // <--
}
This works fine thanks to return type covariance, because A is a SelfReturner<A>.
This is commonly used to allow a class to "know" its own type, though it's important to keep in mind that it is not foolproof:
class Impostor : SelfReturner<A> {
override fun getSelf(): A // <-- oops!
}
While you're right about the apparent recursiveness of these generics, because one could indeed instead write
fun <T : Comparable<Comparable<Comparable<...>>>> findMax(collection: Collection<T>): T?
this doesn't go on forever because the condition is generally satisfied after a single level of recursion (say we use String, for example. It is a Comparable<String>, and that's all the compiler needs to check.)
Note that unlike e.g. C++, Kotlin does not use templates. Generic type information is only used by the compiler to ensure code correctness, and is not preserved* (see type erasure) in the compiled code.
Whereas template instantiation will result in the creation of a new and completely separate type, generic types are all erased to the same (non-generic) class at runtime.
* This isn't completely true; some generic type information is available to reflection, which is why type tokens work, but it's only available in limited circumstances.
Fun fact: Wikipedia claims that this was discovered by accident,
by Jan Falkin, who accidentally derived a base class from a derived class
so it appears to have been just as confusing even to those who came up with the concept.
Yes, there's no citation, but let's not ruin the magic. :)
Recursive Type Bound
The pattern you are referring to is called a recursive type bound in the JVM world. In generics, when a reference type has a type parameter that is bounded by the reference type itself, then that type parameter is said to have a recursive type bound.
For example, in the generic type Fruit<T extends Fruit<T>>, Fruit is the reference type, its type parameter T is bounded by the Fruit itself, so, the type parameter T has a recursive type bound Fruit<T>.
Let's solve a simple problem to understand this concept step by step.
Problem
Assume that we have to sort the fruits by their sizes. And we are told that we can only compare fruits of the same types. For example, we can't compare apples with oranges (pun intended).
So, we create a simple type hierarchy like following,
Fruit.kt
interface Fruit {
val size: Int
}
Apple.kt
class Apple(override val size: Int) : Fruit, Comparable<Apple> {
override operator fun compareTo(other: Apple): Int {
return size.compareTo(other.size)
}
}
Orange.kt
class Orange(override val size: Int) : Fruit, Comparable<Orange> {
override operator fun compareTo(other: Orange): Int {
return size.compareTo(other.size)
}
}
Test
fun main() {
val apple1 = Apple(1)
val apple2 = Apple(2)
println(apple1 > apple2) // No error
val orange1 = Orange(1)
val orange2 = Orange(2)
println(orange1 < orange2) // No error
println(apple1 < orange1) // Error: different types
}
Solution
In this code, we are able to achieve our objective of being able to compare the same types, that is, apples with apples and oranges with oranges. When we compare an apple with an orange we get an error which is what we want.
Problem
The problem here is that the code for implementing the compareTo() method is duplicated for Apple and Orange class. And will be duplicated more in all the classes that we extend from the Fruit when we create new fruits in the future. The amount of repeated code in our example is less but in the real world, the repeated code can be of hundreds of lines in each class.
Moving the Repeated Code to Interface
Fruit.kt
interface Fruit : Comparable<Fruit> {
val size: Int
override operator fun compareTo(other: Fruit): Int {
return size.compareTo(other.size)
}
}
Apple.kt
class Apple(override val size: Int) : Fruit
Orange.kt
class Orange(override val size: Int) : Fruit
Solution
In this step, we get rid of the repeated code of compareTo() method by moving it to the interface. Our extended classes Apple and Orange are no longer polluted with common code.
Problem
Now the problem is that we are now able to compare different types, comparing apples to oranges no longer gives us an error:
println(apple1 < orange1) // No error
Introducing a Type Parameter
Fruit.kt
interface Fruit<T> : Comparable<T> {
val size: Int
override operator fun compareTo(other: T): Int {
return size.compareTo(other.size) // Error: size not available.
}
}
Apple.kt
class Apple(override val size: Int) : Fruit<Apple>
Orange.kt
class Orange(override val size: Int) : Fruit<Orange>
Solution
To restrict the comparison of different types, we introduce a type parameter T. So that the comparable Fruit<Apple> cannot be compared to comparable Fruit<Orange>. Note our Apple and Orange classes; they now inherit from the types Fruit<Apple> and Fruit<Orange> respectively. Now if we try to compare different types, the IDE shows an error, our desired behaviour:
println(apple1 < orange1) // Error: different types
Problem
But in this step, our Fruit class doesn't compile. The size property of T is unknown to the compiler. This is because the type parameter T of our
Fruit class doesn't have any bound. So, the T could be any class, it is not possible that every class in the world would have a size property. So the compiler is right in not recognizing the size property of T.
Introducing a Recursive Type Bound
Fruit.kt
interface Fruit<T : Fruit<T>> : Comparable<T> {
val size: Int
override operator fun compareTo(other: T): Int {
return size.compareTo(other.size)
}
}
Apple.kt
class Apple(override val size: Int) : Fruit<Apple>
Orange.kt
class Orange(override val size: Int) : Fruit<Orange>
Final Solution
So, we tell the compiler that our T is a subtype of Fruit. In other words, we specify the upper bound T extends Fruit<T>. This makes sure that only subtypes of Fruit are allowed as type arguments. Now the compiler knows that the size property can be found in the subtype of Fruit class (Apple, Orange etc.) because the Comparable<T> also receives our type(Fruit<T>) that contains the size property.
This allows us to get rid of the repeated code of compareTo() method and also allows us to compare the fruits of the same types, apples with apples and oranges with oranges.
More About Recursive Type Bounds
A recursive type is one that includes a function that uses that type itself as a type for some argument or its return value. In our example, compareTo(other: T) is the function of the recursive type that takes the same recursive type as an argument.
Caveat
The caveat in this pattern is that the compiler doesn’t prevent us from creating a class with a type argument of other subtype:
class Orange(override val size: Int) : Fruit<Orange>
class Apple(override val size: Int) : Fruit<Orange> // No error
Note in the Apple class above, by mistake we passed Orange instead of Apple itself as a type argument. This results in the compareTo(other: T) method to take Orange instead of Apple.
Now we no longer get error while comparing different types and suddenly can't compare apples with apples:
println(apple1 < orange1) // No error
println(apple1 > apple2) // Error
So, the developer needs to be careful while extending the classes.
Infinite Recursion Not Possible
The declaration Fruit<T extends Fruit<T>> makes sure that only the subtypes of type Fruit<T> are allowed by the compiler. Fruit<Fruit<T>> or Fruit<Fruit<Fruit<T>>> and so on are not the subtypes of Fruit<T>, in other words, they are not within bound.
For example, if we use the declaration in the following manner:
class Orange(override val size: Int) : Fruit<Fruit<Orange>>
The compiler will give error: Type argument is not within its bound
There is no imaginable use case for Fruit<Fruit>, so the compiler doesn't allow that either. Only the first level is allowed, that is, Fruit<Apple>, Fruit<Orange> etc.
These two things together prevent the infinite recursion.
That's it! Hope that helps.

Proper design for C++ class wrapping multiple possible types

I am trying to implement a C++ class which will wrap a value (among other things). This value may be one of a number of types (string, memory buffer, number, vector).
The easy way to implement this would be to do something like this
class A {
Type type;
// Only one of these will be valid data; which one will be indicated by `type` (an enum)
std::wstring wData{};
long dwData{};
MemoryBuffer lpData{};
std::vector<std::wstring> vData{};
};
This feels inelegant and like it wastes memory.
I also tried implementing this as a union, but it came with significant development overhead (defining custom destructors/move constructors/copy constructors), and even with all of those, there were still some errors I encountered.
I've also considered making A a base class and making a derived class for each possible value it can hold. This also feels like it isn't a great way to solve the problem.
My last approach would be to make each member an std::optional, but this still adds some overhead.
Which approach would be the best? Or is there another design that works better than any of these?
Use std::variant. It is typesafe, tested and exactly the right thing for a finite number of possible types.
It also gets rid of the type enum.
class A {
std::variant<std::wstring, long, MemoryBuffer, std::vector<std::wstring>> m_data{}; // default initializes the wstring.
public
template<class T>
void set_data(T&& data) {
m_data = std::forward<T>(data);
}
int get_index() { // returns index of type.
m_data.index();
}
long& get_ldata() {
return std::get<long>(m_data); // throws if long is not the active type
}
// and the others, or
template<class T>
T& get_data() { // by type
return std::get<T>(m_data);
}
template<int N>
auto get_data() { // by index
return std::get<N>(m_data);
}
};
// using:
A a;
a.index() == 0; // true
a.set_data(42);
a.index() == 1; // true
auto l = a.get<long>(); // l is now of type long, has value 42
a.get<long>() = 1;
l = a.get<1>();
PS: This example does not even include the coolest (in my opinion) feature of std::variant: std::visit I am not sure what you want to do with your class, so I cannot create a meaningful example. If you let me know, I will think about it.
You basically want QVariant without the rest of Qt, then :)?
As others have mentioned, you could use std::variant and put using MyVariant = std::variant<t1, t2, ...> in some common header, and then use it everywhere it's called for. This isn't as inelegant as you may think - the specific types to be passed around are only provided in one place. It is the only way to do it without building a metatype machinery that can encapsulate operations on any type of an object.
That's where boost::any comes in: it does precisely that. It wraps concepts, and thus supports any object that implements these concepts. What concepts are required depends on you, but in general you'd want to choose enough of them to make the type usable and useful, yet not too many so as to exclude some types prematurely. It's probably the way to go, you'd have: using MyVariant = any<construct, _a>; then (where construct is a contract list, an example of which is as an example in the documentation, and _a is a type placeholder from boost::type_erasure.
The fundamental difference between std::variant and boost::any is that variant is parametrized on concrete types, whereas any is parametrized on contracts that the types are bound to. Then, any will happily store an arbitrary type that fulfills all of those contracts. The "central location" where you define an alias for the variant type will constantly grow with variant, as you need to encapsulate more type. With any, the central location will be mostly static, and would change rarely, since changing the contract requirements is likely to require fixes/adaptations to the carried types as well as points of use.

c++ dynamic return type

I'm not sure if this is a thing (to be honest I want to say that it is not), but I was wondering if there is a way to write a c++ function so that it can choose which type of object to return.
For example, I have a base class (A) that has 3 child classes (Aa, Ab, Ac). In a factory(F) class I have a std::map<UINT, A*> that holds a number of the child classes based on a UINT id. My goal is to write a function that can build and return the correct object when I pass in an id value.
I'll probably end up returning pointers and cloning the data that they point to, but I was just curious as to whether or not the aforementioned was actually possible.
Thanks!
C++ being statically typed, the return type of a function must be known at compile time. From here arises the question:
do I know the expected return type statically on each call site of F (== it only depends on constant expression values)
or does it depend on some runtime variable.
For case #1, a function template for F would be a good approach.
But in your case, it seems you are facing #2 (because you want to return a type depending on ID that we can assume is not a constant expression).
Because of the static typing, if you are to write a function (assuming you do not overload it, because it seems your input parameters are always the same), it will have a single and well-defined return type. Basically, you do not have a syntax to say that your factory F will return either an Aa Ab or Ac (and that is a very good thing, with regard to static typing and all the compiler verifications it enables ; )
C++ solution: Type erasure
With that being said, you have a few approaches to type erasure, that will allow you to return an instance of a variant type hidden behind a common single type.
The obvious one is the pointer-to-derived to pointer-to-base conversion. It is particularly usefull if you plan to use the returned object mainly through its A interface (i.e., you will call the virtual functions defined on A).
A* F(ID aId)
This A* could point to any type deriving from A. From here, you could call every function defined on A public interface on the returned pointer. Of course, if you wanted to call an operation that is only available on a subclass, you would need to know what is the exact type on call site,and then cast the pointer to a pointer-to-derived before being able to call the operation.
A possible alternative, if you'd rather avoid dynamic memory, could be boost::variant. At the cost of having to explicitly list all the possible types the function could return.
boost::variant<Aa, Ab, Ac> F(ID aId);
You can take a look at the tutorial for a quick introduction to the syntax and features.
Sure, something like this:
class MyMapClass
{
public:
template< class ExactType > ExactType * getValue(UINT key)
{
return dynamic_cast<ExactType*>(_myMap.at(key));
}
BaseType * at(UINT key)
{
return _myMap.at(key);
}
private:
std::map<UINT, BaseType*> _myMap;
}
However, since you are storing the pointers to base types, you can as well return them as is, and rely on the caller to make a specific cast, if that goes well with your application's architecture.
Unfortunately, you can not do it fully automatically. Sooner or later you will have to determine the exact class that hides behind the base class pointer, and make a cast. With the template solution it is done "sooner":
MyDerivedType * value = myMapClassInstance.getValue<MyDerivedType>(1);
If you prefer to return the base pointer, it is done "later":
BaseType * value = myMapClassInstance.at(1);
MyDerivedType * exactValue = dynamic_cast<MyDerivedType*>(value);

Passing maps with different value types to a function in 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)

C++ storing derived objects within a map

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.