www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Reflection in D

reply medhi558 <medhi558 gmail.com> writes:
Hello, I would like to know if it is possible to recover all 
classes in the project in D.

Example in c# :

Assembly asm = Assembly.GetAssembly(typeof(MyClass));

foreach (Type type in asm.GetTypes())
{
}
Jan 27
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 27 January 2017 at 21:02:13 UTC, medhi558 wrote:
 Hello, I would like to know if it is possible to recover all 
 classes in the project in D.
Yes, `foreach(mod; ModuleInfo) foreach(lc; mod.localClasses)`... but why do you want it? That facility is kinda limited in doing many things with.
Jan 27
parent reply medhi558 <medhi558 gmail.com> writes:
I develop a game server. Currently I use a switch :

import protocol.messages.connection.Message1;
import protocol.messages.connection.Message2;
import protocol.messages.queues.Message3;
import ......

import protocol.messages.NetworkMessage;

class ProtocolMessageManager
{
	public static NetworkMessage GetInstance(uint id)
	{
	      switch(id)
               {
                  case Message1.Id:
                      return new Message1();
                  case Message2.Id:
                      return new Message2();
                  .....

                  default:
	              return null;
               }
         }
}

what I want to do :

import protocol.messages.NetworkMessage;

class ProtocolMessageManager
{
     private static TypeInfo_Class[uint] m_types;

     public static void Init()
     {
         foreach(mod; ModuleInfo)
	{
	    foreach(TypeInfo_Class lc; mod.localClasses)
	    {
                 if(lc.name.indexOf("protocol.messages") != -1)
                 {
                     NetworkMessage c = 
cast(NetworkMessage)lc.create();
	            ProtocolMessageManager.m_types[c.MessageId] = lc;
                 }
	    }
					
	}
     }

     public static NetworkMessage GetInstance(string id)
     {
	auto v = (id in ProtocolMessageManager.m_types);
	if (v !is null)
	 return 
cast(NetworkMessage)ProtocolMessageManager.m_types[id].create();
	else
          return null;
     }
}
Jan 27
next sibling parent reply medhi558 <medhi558 gmail.com> writes:
I have a last question, currently i use :
if(lc.name.indexOf("protocol.messages") != -1)

To know if the class is a NetworkMessage, Would be possible to do 
this

if(lc is NetworkMessage)


Sorry for my English, i speak french.
Jan 27
next sibling parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 28 January 2017 at 07:10:27 UTC, medhi558 wrote:
 I have a last question, currently i use :
 if(lc.name.indexOf("protocol.messages") != -1)

 To know if the class is a NetworkMessage, Would be possible to 
 do this

 if(lc is NetworkMessage)


 Sorry for my English, i speak french.
if (auto nm = cast(NetworkMessage)lc) { //do something with nm }
Jan 27
parent reply medhi558 <medhi558 gmail.com> writes:
On Saturday, 28 January 2017 at 07:39:51 UTC, rumbu wrote:
 On Saturday, 28 January 2017 at 07:10:27 UTC, medhi558 wrote:
 I have a last question, currently i use :
 if(lc.name.indexOf("protocol.messages") != -1)

 To know if the class is a NetworkMessage, Would be possible to 
 do this

 if(lc is NetworkMessage)


 Sorry for my English, i speak french.
if (auto nm = cast(NetworkMessage)lc) { //do something with nm }
It doesn't work because lc is TypeInfo_Class
Jan 28
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 28 January 2017 at 08:18:15 UTC, medhi558 wrote:
 On Saturday, 28 January 2017 at 07:39:51 UTC, rumbu wrote:
 On Saturday, 28 January 2017 at 07:10:27 UTC, medhi558 wrote:
 I have a last question, currently i use :
 if(lc.name.indexOf("protocol.messages") != -1)

 To know if the class is a NetworkMessage, Would be possible 
 to do this

 if(lc is NetworkMessage)


 Sorry for my English, i speak french.
if (auto nm = cast(NetworkMessage)lc) { //do something with nm }
It doesn't work because lc is TypeInfo_Class
In this case: if (lc == typeid(NetworkMessage))
Jan 28
parent medhi558 <medhi558 gmail.com> writes:
abstract class NetworkMessage
{
     uint MessageId;
     //....
}

override class QueueStatusUpdateMessage : NetworkMessage
{
     uint MessageId = 1;
     //....
}

override class Message2 : NetworkMessage
{
     uint MessageId = 2;
     //....
}

override class Message3 : NetworkMessage
{
     uint MessageId = 3;
     //....
}

And I would like to retrieve all the classes that are based on 
NetworkMessage.

class ProtocolMessageManager
{
     private static TypeInfo_Class[uint] m_types;

     public static void Init()
     {
         foreach(mod; ModuleInfo)
	{
	    foreach(TypeInfo_Class lc; mod.localClasses)
	    {
                 if(....) //lc is NetworkMessage so
                 {
                     NetworkMessage c = 
cast(NetworkMessage)lc.create();
	            ProtocolMessageManager.m_types[c.MessageId] = lc;
                 }
	    }
					
	}
     }

     public static NetworkMessage GetInstance(string id)
     {
	auto v = (id in ProtocolMessageManager.m_types);
	if (v !is null)
	 return 
cast(NetworkMessage)ProtocolMessageManager.m_types[id].create();
	else
          return null;
     }
}
Jan 28
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 28 January 2017 at 07:10:27 UTC, medhi558 wrote:
 I have a last question, currently i use :
 if(lc.name.indexOf("protocol.messages") != -1)
If they're all in the same module, you can also use the compile time reflection to scan the module for classes. That's foreach(memberName; __traits(allMembers, mixin(__MODULE__))) static if(is(__traits(getMember, mixin(__MODULE__), memberName) : NetworkMessage) { if(memberName == runtime_string_of_class_name) { auto msg = new __traits(getMember, mixin(__MODULE__), memberName); // populate msg } } Or something like that. The compile time stuff works well when everything is in a shared module. If not, the runtime stuff has methods `base` and `interfaces` you can scan for your class. So like foreach(c; mod.localClasses if(c.base is typeid(NetworkMessage) { // work with it } Notice it is typeid for runtime, typeof at compile time, and this only gets direct children of NetworkMessage (though you could walk up the parent list to check more).
Jan 28
parent medhi558 <medhi558 gmail.com> writes:
thank you, the second method works perfectly.

I have one last problem, I start thread and in thread 
Protocol.GetInstance(id) return null while in main thread it 
works.

My class :

class ProtocolMessageManager
{
     private static TypeInfo_Class[uint] m_types;
	
     shared static this()
     {
         foreach(mod; ModuleInfo)
         {
	    foreach(TypeInfo_Class lc; mod.localClasses)
	    {
	        if(lc.base is typeid(NetworkMessage))
		{
		    NetworkMessage c = cast(NetworkMessage)lc.create();
		    ProtocolMessageManager.m_types[c.MessageId] = lc;
		}
	    }
	}
    }

    public static NetworkMessage GetInstance(uint id)
    {
        auto v = (id in ProtocolMessageManager.m_types);
        if (v !is null)
	return 
cast(NetworkMessage)ProtocolMessageManager.m_types[id].create();
        else
	return null;
     }
}
Jan 28
prev sibling parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 28 January 2017 at 07:03:51 UTC, medhi558 wrote:

     public static NetworkMessage GetInstance(string id)
     {
 	auto v = (id in ProtocolMessageManager.m_types);
 	if (v !is null)
 	 return 
 cast(NetworkMessage)ProtocolMessageManager.m_types[id].create();
 	else
          return null;
     }
 }
As long as your class has a default constructor, you can use directly Object.factory(id): public static NetworkMessage GetInstance(string id) { return cast(NetworkMessage)(Object.factory(id)); }
Jan 28
parent medhi558 <medhi558 gmail.com> writes:
On Saturday, 28 January 2017 at 09:05:13 UTC, rumbu wrote:
 As long as your class has a default constructor, you can use 
 directly Object.factory(id):

 public static NetworkMessage GetInstance(string id)
 {
   return cast(NetworkMessage)(Object.factory(id));
 }
It isn't possible. My function when I receive data : private void ReceivedData(char[] buffer) { BinaryReader reader = new BinaryReader(); uint id = reader.ReadUInt(); uint length = reader.ReadInt(); if(length > reader.Available.length) { //waiting for the rest return; } NetworkMessage message = ProtocolMessageManager.GetInstance(id); message.Deserialize(); //..... }
Jan 28