enum on AA's and delegates debug info

Bert Bert at gmail.com
Wed Jul 3 19:32:23 UTC 2019


On Wednesday, 3 July 2019 at 06:25:48 UTC, Rainer Schuetze wrote:
>
>
> On 01/07/2019 22:39, Bert wrote:
>> On Monday, 1 July 2019 at 06:54:17 UTC, Rainer Schuetze wrote:
>>>
>>>
>>> On 01/07/2019 01:27, Bert wrote:
>>>> an AA using an enum uses int as the base type rather than 
>>>> the enum member name.
>>>>
>>>> enum X { A, B, C};
>>>> AA[X] x;
>>>>
>>>> will show
>>>>
>>>> [0]
>>>> [1]
>>>>
>>>> instead of
>>>>
>>>> [A]
>>>> [B]
>>>>
>>>
>>> That's a long standing compiler bug: 
>>> https://issues.dlang.org/show_bug.cgi?id=982, 
>>> https://issues.dlang.org/show_bug.cgi?id=4372
>>>
>> 
>> That is unfortunate. Is there not a way to get the proper type?
>> 
>> In the json file I have
>> 
>>       "deco" : "HE14test13AADFCQBj6SZv",
>>       "originalType" : "X[][A]",
>> 
>> Here A is the enum, it clearly knows it in the json... I 
>> wonder if that could be used to realize that the int can be 
>> converted to an A?
>> 
>> Visual D could simply try to convert any values to A and use 
>> the name if it passes, else fallback to the int.
>
> Probably simpler to fix in the compiler.
>
>> 
>>>> It shouldn't be hard to get it to display the names through 
>>>> conversion.
>>>>
>>>> Also,
>>>>
>>>> delegates in an array do not show any value. This makes it 
>>>> difficult to know if they actually any value. Maybe you 
>>>> could just show the address of the delegate?
>>>
>>> It also doesn't show a delegate outside of an array. 
>>> Implemented now for the next release.
>> 
>> 
>> Any ETA on it?
>
> You can try a preliminary build from 
> https://ci.appveyor.com/project/rainers/mago/build/artifacts by 
> replacing MagoNatCC.dll in 
> "<VS-Installation-Path>\Common7\Packages\Debugger".

Thanks, it seems to be working. The only thing I really wish it 
could do not is display functions values.

I have tried to resolve the function values but I always get a 
symbol not found in the watch value.


It would be nice if the symbol could be searched for 
automatically or if they could be displayed in the autos/locals 
window as fields are. Obviously only parameterless methods and 
functions should be displayed(although maybe a dialog could pop 
up asking to input the arguments?).


The following code could be used as an example.

I'd like to get the value of Type. Only name shows up in the 
expansion list. Since I use property like functions heavily due 
to interfaces it ends up being problematic. This is usually not 
too bad since Visual D is now displaying fields properly and I 
usually back the properties with fields(although sometimes the 
method will modify the output).

It would be really cool to even call setters, I guess ideally a 
command window where one could execute parts of the program like 
repl would work well. C# and .net now have these things. I'm not 
sure how hard they are but it should be relatively straight 
forward? Just matching up symbols and then executing the code 
somehow(a parallel thread?)

Thanks for the update!


import std.stdio, std.traits;

struct ModelA
{
     // D only allows single inheritance, must use interfaces
     interface iAnimal
     {
         string Type();
		string Name();
         void Attack(iAnimal who);
         iFood LikesWhichFood();
     }

     interface iCat : iAnimal
     {
		void Meow();
     }

     interface iDog : iAnimal
     {
		void Bark();
     }
     interface iFood
     {

     }

     class Animal : iAnimal
     {
         void Attack(iAnimal who) { writeln(Name, " is attacking 
", who.Name, "!"); }
         string Type() { return "Unknown Animal Type"; }
		override string Name() { return "Unknown Animal"; }
		iFood LikesWhichFood() { writeln("Food D Type: ", 
fullyQualifiedName!iFood); return null; }					
     }

     class Cat : Animal, iCat
     {
		string name = "Unknown Cat";
         override string Type() { return "Cat"; }		
		override string Name() { return name; }
         void Meow() { writeln("Meow!"); }
		this() { }
		this(string n) { name = n; }
     }

	class Dog : Animal, iDog
     {
		string name = "Unknown Dog";
         override string Type() { return "Dog"; }		
		override string Name() { return name; }
         void Bark() { writeln("Bark!"); }
		this() { }
		this(string n) { name = n; }
     }


     class Food : iFood
     {

     }
}


// Model B, It is "derived" from A, meaning Model B could, in 
theory, substitute for Model A as long as everything is designed 
correctly
// In this case we will create a ViewModel, a gui framework for 
ModelA. We actually cannot do this naturally in D since it does 
not support multiple inheritance.
struct ModelB
{	
     interface iAnimal : ModelA.iAnimal
     {
		// Provides a covariant return, which keeps the return values 
of ModelB in modelB.
		override iFood LikesWhichFood();
     }

     interface iCat : iAnimal, ModelA.iAnimal
     {

     }

     interface iDog : iAnimal, ModelA.iAnimal
     {

     }
     interface iFood : ModelA.iFood
     {
		void IsItTasty();
     }

     class Animal : ModelA.Animal, iAnimal
     {
		// This effectively isolates the cast that we would otherwise 
have to do everywhere in ModelB. We know ModelA.LikesWhichFood 
will return a ModelA.iFood type, but consistency will always 
provide us with an actually ModelB.iFood object, so the cast is 
always safe. [In effect, no instance of ModelA(or it's objects) 
diretly exists. ModelA is used only as a structural template. It 
is a sort of model instance.
		override iFood LikesWhichFood() { return 
cast(iFood)super.LikesWhichFood; }
     }

     class Cat : ModelA.Cat, iAnimal, iCat // We need to derive 
from Animal, not iAnimal, to provide proper ModelB implementation 
of Animal
     {
		//alias Attack = Animal.Attack;	// Required by D
		
		// In D, ModelA.Cat's implement is not provided as default, we 
have to reimplement everything. Or is Animal providing any 
implementation
         override string Type() { return super.Type; }		
		override string Name() { return super.Name; }
         override void Meow() { super.Meow; }
		void Attack(iAnimal who) { super.Attack(who); }
		override void Attack(ModelA.iAnimal who) { super.Attack(who); }
		override iFood LikesWhichFood() { writeln("Food D Type: ", 
fullyQualifiedName!iFood); return new Cabbage; }					
		this() { }
		this(string n) { name = n; }
		
     }

	class Dog : ModelA.Dog, iAnimal, iDog
     {
		alias Attack = Animal.Attack;	
         override string Type() { return super.Type; }		
		override string Name() { return super.Name; }
         override void Bark() { super.Bark; }
		override void Attack(ModelA.iAnimal who) { super.Attack(who); 
}						// Ideally we could just use covariance, ModelB.Dog.Attack 
will only take a ModelB.iAnimal, we will never insert a more 
generic ModelA.iAnimal, but the compiler forces us to be most 
general, even when we already are(since what happens in ModelB 
stays in ModelB).
		void Attack(iAnimal who) { Attack(cast(ModelA.iAnimal)who); 
}						// This is required use ModelB.iAnimal in 
ModelB(consistency)
		override iFood LikesWhichFood() { writeln("Food D Type: ", 
fullyQualifiedName!iFood); return new Donuts; }					
		this() { }
		this(string n) { name = n; }
     }


     class Food : iFood
     {
		void IsItTasty() { writeln("Unknown Food"); }
     }

	class Donuts : Food
	{
		override void IsItTasty() { writeln("YUK!"); }
	}

	class Cabbage : Food
	{
		override void IsItTasty() { writeln("YUM!"); }
	}
}
void main()
{

	{
		ModelA.iAnimal animal1 = new ModelA.Cat("Mittens");
		ModelA.iAnimal animal2 = new ModelA.Dog("Sparky");

		writeln(animal1.Name);
		writeln(animal2.Name);
		animal1.Attack(animal2);
		animal1.LikesWhichFood;
	}

	writeln("\n----------\n");

	{
		ModelB.iAnimal animal1 = new ModelB.Cat("Super Mittens");
		ModelB.iAnimal animal2 = new ModelB.Dog("Super Sparky");

		writeln(animal1.Name);
		writeln(animal2.Name);
		animal1.Attack(animal2);
		auto f = animal1.LikesWhichFood;
		f.IsItTasty; 		// Error: no property `IsItTasty` for type 
`Models.ModelA.iFood`. It should return a ModelB.iFood, we are 
inside ModelB, never any risk
		(cast(ModelB.iFood)f).IsItTasty;		// We can, of course, force 
it, but that is the rub, we don't have to, that is why we want to 
have a concept of a model, it tells the compiler that there is 
something more going on and it can reduce all this overhead. We 
can't even override this because of the contravariance rule.

	}

	writeln("\n----------\n");

	// This is the magic, ModelB is now substituted in Model A. It's 
basically still oop but our entire derived model is(or should be) 
used.
	// We can substitute the new model in all places where the old 
was used. This is the easy way to do ModelViewModel, we simply 
extend the model and add the view, no complex bridging, adapting, 
maintance, dependencies, etc.
	{
		ModelA.iAnimal animal1 = new ModelB.Cat("Super Mittens");
		ModelA.iAnimal animal2 = new ModelB.Dog("Super Sparky");

		writeln(animal1.Name);
		writeln(animal2.Name);
		animal1.Attack(animal2);
		animal1.LikesWhichFood;
		auto f = animal2.LikesWhichFood;
		//f.IsItTasty;	// This Error is ok, we are inside ModelA, 
ModelA would never use IsItTasty and it would be wrong to do 
so(it's only wrong because it should be impossible for ModelA to 
know about ModelB, else we create a dependency between models and 
really end up with one combined model rather than two separate 
models). But note that we could cast		
		(cast(ModelB.iFood)f).IsItTasty;		// We can, of course, force 
it though(only because we know for a fact we are actually dealing 
with a ModelB disugised as a ModelA, this is generally not the 
case), but this then shows a dependency. Note that it is exactly 
like the above model though... but there is a huge difference. In 
the first case it is afe, in this case it is not.. and the only 
difference is the model we are working in.
	}


     getchar();
}


More information about the Digitalmars-d-ide mailing list