-
-
Notifications
You must be signed in to change notification settings - Fork 233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parser attribute #97
Parser attribute #97
Conversation
Thank you for the PR! I find ConsoleParserAttribute useful (though I find ConsoleCustomTypeParserAttribute easier to understand) but I believe the current implementation is too complex for my taste. Here are my concerns:
If you don't feel like changing the current implementation, I could still implement this the way I want in a future update. |
I'd be happy to make changes, I don't especially like the implementation either. I have a rough idea on how to support more attributes I'd like to try as well and see your thoughts on. I will start with making the changes you're requesting. |
Added IConsoleMethodInfo to wrap all types of method info attributes. |
Thank you for the changes and sorry for my late responses!
|
I personally prefer very clear error messages, but I suppose since the message contains the method definition example, it should remain clear anyways.
There should not be any linq used, I've removed it all. I noticed I missed a
I wanted your feedback on the new enumerator first, but my intentions where to use the attributes to hold
I will be pushing the commits shortly. Some unrelated errors in my project have to be fixed before I can perform tests. |
…dles their load order and load method.
I believe we can store all attributes in a List and then sort that list by Order. No need for TypeSearch and its Dictionary+enumerator approach anymore. PS. A base class with Order property is good thinking 👍 |
foreach( Type type in assembly.GetExportedTypes() ) | ||
IEnumerable<ConsoleAttribute> consoleMethods = TypeSearch.GetConsoleMethods(assembly.GetExportedTypes()); | ||
foreach (ConsoleAttribute consoleMethod in consoleMethods) | ||
{ | ||
foreach( MethodInfo method in type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly ) ) | ||
{ | ||
foreach( object attribute in method.GetCustomAttributes( typeof( ConsoleMethodAttribute ), false ) ) | ||
{ | ||
ConsoleMethodAttribute consoleMethod = attribute as ConsoleMethodAttribute; | ||
if( consoleMethod != null ) | ||
AddCommand( consoleMethod.Command, consoleMethod.Description, method, null, consoleMethod.ParameterNames ); | ||
} | ||
} | ||
consoleMethod.Load(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, can you restore this old code and instead of calling AddCommand, just fill the List? Then we can Sort that list and process its elements.
public static void AddCustomParameterType(MethodInfo method, Type type, string readableName) | ||
{ | ||
if (TryBuildCustomParameterFunction(method, out ParseFunction func, false)) | ||
AddCustomParameterType(type, func, readableName); | ||
} | ||
|
||
private static bool TryBuildCustomParameterFunction(MethodInfo method, out ParseFunction function, bool validate) | ||
{ | ||
try | ||
{ | ||
function = (ParseFunction)Delegate.CreateDelegate(typeof(ParseFunction), method); | ||
return true; | ||
} | ||
catch (Exception e) | ||
{ | ||
const string format = "Parser Method {0}.{1} is Invalid.\n{2}\nex: public static bool {1}(string input, out object result)"; | ||
string error = string.Format(format, method.DeclaringType.FullName, method.Name, e.Message); | ||
Debug.LogError(error); | ||
function = null; | ||
return false; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TryBuildCustomParameterFunction isn't called elsewhere so could you combine these two functions?
PS. The function can be internal
so that it can't be called from user code.
PPS. If we remove catch altogether, I believe we won't lose any important information since Delegate.CreateDelegate's exception message is informative by itself. In the worst case, if user can't understand the message, they can double click the exception and clearly see that a ParseFunction signature is expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to break when an exception is throw or just to skip this one method? If we don't catch here, all the next methods will be ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good with an unhandled exception in this case. There is an error that must be resolved and in both approaches, that error will be logged as soon as the developer enters Play mode. An exception will force the developer to resolve the issue which is a win-win in my book: it's simpler and more effective.
@@ -439,7 +455,7 @@ private static void AddCommand( string command, string description, string metho | |||
AddCommand( command, description, method, instance, parameterNames ); | |||
} | |||
|
|||
private static void AddCommand( string command, string description, MethodInfo method, object instance, string[] parameterNames ) | |||
public static void AddCommand( string command, string description, MethodInfo method, object instance, string[] parameterNames ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about internal
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this method and the other mentioned before, making them internal so they can't be called from user code means they won't be able to use these methods if the users make some additional custom Console Attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That shouldn't be a problem since there are already Console Attributes for these functions. If a user creates an alternative to ConsoleMethodAttribute, then I'd very much like to hear their reasoning.
When a user types DebugLogConsole.AddCommand to add a command via Scripting API, I want them to use the documented public functions and not these particular functions.
public readonly Type type; | ||
public readonly string readableName; | ||
|
||
public override int Order => 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I love this syntax but it isn't supported on very old Unity versions and I plan to support them for as long as possible. Could you use old syntax here and at other similar location(s): public override int Order { get { return 0; } }
…AssemblyForConsoleMethods;
foreach( Type type in assembly.GetExportedTypes() ) | ||
Type[] types = assembly.GetExportedTypes(); | ||
List<ConsoleAttribute> methods = new(); | ||
const BindingFlags BindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; | ||
for (int t = 0; t < types.Length; t++) | ||
{ | ||
foreach( MethodInfo method in type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly ) ) | ||
MethodInfo[] typeMethods = types[t].GetMethods(BindingAttr); | ||
for (int m = 0; m < typeMethods.Length; m++) | ||
{ | ||
foreach( object attribute in method.GetCustomAttributes( typeof( ConsoleMethodAttribute ), false ) ) | ||
MethodInfo method = typeMethods[m]; | ||
IEnumerable<ConsoleAttribute> attributes = method.GetCustomAttributes<ConsoleAttribute>(true); | ||
foreach (ConsoleAttribute attribute in attributes) | ||
{ | ||
ConsoleMethodAttribute consoleMethod = attribute as ConsoleMethodAttribute; | ||
if( consoleMethod != null ) | ||
AddCommand( consoleMethod.Command, consoleMethod.Description, method, null, consoleMethod.ParameterNames ); | ||
attribute.SetMethod(method); | ||
methods.Add(attribute); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the changes.
- Could we make the diff minimalistic by changing only the necessary lines? This will help with Git's Blame/Annotate functionality. Ideally, only the code inside the innermost foreach should change.
- I'd advise against casting arrays or lists to IEnumerable because
foreach(list)
andforeach(array)
(these use duck typing) are more optimized thanforeach(ienumerable)
(you can check out their GC allocations in Profiler). - new() isn't supported on older Unity versions. I also prefer this approach in my personal projects but atm not in my plugins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Could we make the diff minimalistic by changing only the necessary lines? This will help with Git's Blame/Annotate functionality. Ideally, only the code inside the innermost foreach should change.
Sure, I personally try to always for loops over foreach loops.
* I'd advise against casting arrays or lists to IEnumerable because `foreach(list)` and `foreach(array)` (these use duck typing) are more optimized than `foreach(ienumerable)` (you can check out their GC allocations in Profiler).
GetCustomAttributes returns a IEnumerable and not a list or array. I'll switch to the non generic one like the original code did.
* new() isn't supported on older Unity versions. I also prefer this approach in my personal projects but atm not in my plugins.
Force of habit, will fix!
…d removed try catch;
Thank you for the changes. |
Added ConsoleParserAttribute for Parser Methods to be marked and added to the dictionary before adding commands from the ConsoleMethodAttribute
Added TypeSearch helper struct to organize all parser and command methods found in a specified type.
Modified DebugLogConsole.SearchAssemblyForConsoleMethods to use TypeSearch's results and add all parser methods first, followed by command methods.
Usage Example: