How can I convert a hash to and object? Suppose I have the following definition:
class A {
int i;
string s;
}
class B {
int bi;
A a;
}
hash h = (
"bi" : 100,
"A" : ("i" : 10, "s" : "str"));
is there any way how I could convert h to an object of class B?
Is this to be generic? If so, then you'd need a bit more verbose structure. Such that would make it clear that 'A' is to be an object, 'i' is to be an int, etc.
Anyway, there's a built-in function create_object that can be used to create an object as long as the object does not need constructor arguments or you know what they should be. Then you would set all members and that means they need to be public. You could also iterate object's public members and thus check if your hash provides all info.
I'm not aware of a way how to learn more about an object, such as member types, method signatures, etc. Still, you either know the object (so you can provide args to constructor or call a method after creation etc) or it has to be a simple object with no constructor args and public members (as private members cannot be set directly).
Related
I have code accepts a class as a parameter and prepares data to call either the constructor for that class of a companion object factory method if the factory method is present.
All works fine when calling the constructor, but I get the error
java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun nz.salect.objjson.JVMTest.StudentWithFactory.Companion.fromJson(kotlin.String, kotlin.Int): nz.salect.objjson.JVMTest.StudentWithFactory
when calling the factory method. The factory method in question:
data class StudentWithFactory(val name: String, val years: Int=0) {
companion object {
fun fromJson(name: String="", age: Int = 0):StudentWithFactory {
return StudentWithFactory(name, age)
}
}
}
has no required parameters, unless there is some hidden parameter. Any ideas?
In fact, I reverted removing the parameters completely from fromJson and directly calling the companion method using ::fromJson.callby(emptyMap()). Same error.
It is clear that companion methods need at least one additional parameter. Perhaps the class? Or the companion object?
How can I specify the needed parameter(s)?
The function building up the callBy() is supplied a class (or finds the class from a supplied class) and json names and values.
var funk:KFunction<*>?=null
val companionFuncs=cls.companionObject?.declaredMemberFunctions
if(companionFuncs?.size ?:0 >0){
companionFuncs?.forEach {
if(it.name == "fromJson") funk=it
}
}
val cons:KFunction<T> = if(funk != null)
funk as KFunction<T>
else
cls.primaryConstructor ?: throw IllegalArgumentException("no primary constructor ${cls.simpleName}")
val valuesMap = cons.parameters.filter{it.name in vals}
.associateBy(
{it},
{generateValue(it)}
)
val data = cons.callBy(valuesMap) //as T
return data
In addition to my short answer, a more technical explanation:
Yes, there actually is a hidden parameter and you can see it (for example), if you take a look at the decompiled (to Java) bytecode:
public final class StudentWithFactory {
// ...
public static final class Companion {
// ...
#NotNull
public static StudentWithFactory fromJson$default(StudentWithFactory.Companion var0, String var1, int var2, int var3, Object var4) {
// ...
return var0.fromJson(var1, var2);
}
// ...
}
}
The first parameter (var0) is actually an instance of the companion object. var1 (name) and var2 (age) are the parameters you declared. var3 is a bitmask for determining if explicit values have been passed or if the default ones should be used*. I honestly don't know what var4 is for. It is unused in the Java code. But the imported part is that you only need to worry about var0, var1 and var2 if you want to invoke the function.
So, in the end the non-static version of fromJson* is actually invoked on the instance of the companion object:
var0.fromJson(var1, var2)
*left code out for simplicity
You can use the parameters property to determine how much parameters you have to pass to the function/constructor.
If you call
val paramsConstr = StudentWithFactory::class.primaryConstructor?.parameters
paramsConstr will be of size two as expected, but if you call
val paramsFunc = ::fromJson.parameters
paramsFunc will be of size three. The first element corresponds to the instance of the companion object. So, thats the list of parameters you need to provide.
You can invoke the fromJson like this:
// not using any default parameters
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[1] to "Hello",
paramsFunc[2] to 30
))
// using only the default parameter for "name"
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[2] to 30
))
I have the following scenario (https://run.dlang.io/is/19OOW9):
import std.stdio;
void main(string[] args)
{
inter1 c1 = new foo();
foo c2 = new foo();
writeln("Origin=interface: ", typeof(c1).stringof);
writeln("Origin=class: ", typeof(c2).stringof);
}
interface inter1 {
}
class foo : inter1 {
}
I work with interfaces and have different implementations for them. Now I need to know which concrete implementation is currently being used. So in the example above, I would like to know from c1 that it is an instance of the class foo.
Is this possible in the language D?
I have already tried the possibilities of object (e.g. TypeInfo_Class) and std.traits. Unfortunately without success.
A workaround is, of course, to provide the interface with a suitable meta method (https://run.dlang.io/is/Xnt0TO):
import std.stdio;
void main(string[] args)
{
inter1 c1 = new foo();
foo c2 = new foo();
writeln("Origin=interface: ", c1.strategyName);
writeln("Origin=class: ", c2.strategyName);
}
interface inter1 {
#property string strategyName() const;
}
class foo : inter1 {
#property string strategyName() const {
return "foo";
}
}
However, this is cumbersome and unusual for D. I can well imagine that there is a better implementation of this.
Best regards
Thorsten
It is quite simple actually: first cast to Object, then fetch the typeid, after a null check:
Object o = cast(Object) your_object;
if(o is null) { /* i don't think this ever happens but you should check anyway */ }
writeln(typeid(o)); // will tell the class name
If you want to call a method on a specific class, you can just cast directly to your class, and again, null check it.
The intermediate cast to Object allows the typeid (aka classinfo) to succeed, whereas calling it directly on an interface always returns the typeid of the interface itself. This is because a D interface is defined to be very thin for maximum compatibility with other languages and doesn't automatically assume run time type information is actually present through it. But the cast to Object tells it you are assuming the RTTI is present, and then typeid will pull it.
Note that the typeid data doesn't provide a whole lot of information... it is mostly just what's needed for dynamic cast, comparison, and other features of the language runtime. But one convenience method it has is a class name and toString methods, which is why the writeln succeeds. But if you're looking for more detailed runtime reflection, you'll have to do it with a CT bridge function, or probably better yet, just write your own methods in the interface.
But if all you need is the class name, use that toString. It gives the fully-qualified name, including module name, so instead of foo, you will get like yourmodule.foo. You can just cut that off if you like by slicing at the dot.
I have following class structure -
class Student{
int age;
String name;
}
class Group{
Student allStudents[];
int avgAge;
}
class Handler{
public int myFunction(Group g){
...
do something with 'g'
...
return k;
}
}
I want to invoke the function, 'myFunction' from Javassist using a custom 'Group' object and test the behaviour of the function for this object.
In a different scenario, I'm able to work with functions with parameters of primitive data type as follows-
ctBehavior.insertBefore("{for (int i=0; i < $args.length; i++) {System.out.println($args[i]);}}");
I want to do something like this for parameters that are custom objects.
Javassist lets you code out anything that you could write in Java (v1.4). Additionally, it gives you a few makro commands. If you want to create a group object, depending on its constructor, it would look something like:
"myFunction(new Group((Student[]) $args[0], Integer.valueOf((Integer) $args[1])));"
Note that Javassist does not automatically box or unbox your values. Any value read by $args is boxed and of type Object. This is most likely the problem you are observing.
I'm trying to use websocket in my project.
to do so, I installed the package Microsoft Asp.Net SignalR, which consists of WebSocketHandler abstract class.
i defined a class inheriting WebSocketHandler, but then the compiler complains:
'Microsoft.AspNet.SignalR.WebSockets.WebSocketHandler' does not contain a constructor that takes 0 arguments'.
It seems wierd to me, because the definitioin of WebSocketHandler ctor gets a nullable value, which means the ctor could get no parameter,
the definition looks like this:
protected WebSocketHandler(int? maxIncomingMessageSize);
can anybody tell me what the problem is?
thanks.
It seems wierd to me, because the definitioin of WebSocketHandler ctor gets a nullable value, which means the ctor could get no parameter
No, it doesn't. There's a big difference between receiving a null value for a nullable type, and not receiving a value at all.
If the parameter were optional, that would be a different matter - but it's not. You have to supply an argument convertible to int? in the call. If you want to provide the null value for int?, do so:
var handler = new WebSocketHandler(null);
Or if you want to avoid accidentally using any other single-parameter constructor definitions which may be applicable with a null literal as the argument, you could use:
var handler = new WebSocketHandler((int?) null);
Or:
var handler = new WebSocketHandler(default(int?));
protected member is accessible by derived class instances and there's nothing special about it. Nothing special in the class itself, either # WebSocketHandler.cs.
It just mens you need to pass in a nullable type, it does not mean it can't get any arguments.
int? maxIncomingMessageSize = 0;
var socket = new WebSocketHandler(maxIncomingMessageSize);
In your derived class you could/should define a "constructor that takes 0 arguments".
public class MyHandler : WebSocketHandler
{
// not mandatory
public MyHandler()
:this(null)
{}
// mandatory
public MyHandler(int? maxIncomingMessageSize)
:base(maxIncomingMessageSize)
{}
}
I need to log all the function parameters in a dozen functions.
Is there a way to pro grammatically determine all the parameters and their values (or at least their .ToString() value)? Perhaps via reflection?
Here is an example of how to do this with PostSharp
http://consultingblogs.emc.com/merrickchaffer/archive/2009/08/04/using-postsharp-to-log-method-entry-and-exit-in-net-code.aspx
You can also roll your own
http://www.developerfusion.com/article/5307/aspect-oriented-programming-using-net/3/
To the best of my knowledge there's no way to use reflection to dynamically list and determine value of local variables. You can use reflection to get type information about the parameters of a method, but only the declared type - you can't automatically get information about the actual arguments, because the reflection metadata gives information about the method definition, not the specific values passed to it at runtime.
You can, however, do something like this:
static class Extensions
{
public static string GetTypeAndValue(this object obj)
{
return String.Format("{0}: {1}", obj.GetType().Name, obj.ToString());
}
}
Then, from within each method in which you want to perform logging, do something like
private void SomeMethodToBeLogged(string some_string, int some_int, bool some_bool)
{
Logger.Log(String.Format("SomeMethodToBeLogged({0}, {1}, {2})",
some_string.GetTypeAndValue(),
some_int.GetTypeAndValue(),
some_bool.GetTypeAndValue()));
}