Does D have reflection?
Does D have reflection or anything close to it, to be able to access objects at runtime ?
if not :
How exactly would I access or edit objects at runtime ?
for example :
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
This is all I can think of at the moment, please let me know what I misunderstand about D in regards to this topic.
I've looked through the documentation on dlang.org and couldn't really find something to do with reflection, or at least not in the way that Java has.
ps, the above code is pseudo-code which I just made up on the spot, I'm sure for whatever reasons it would not actually compile, I'm just hoping to get through that I want access to objects in a specific way just so it convenient for me.
reflection d
add a comment |
Does D have reflection or anything close to it, to be able to access objects at runtime ?
if not :
How exactly would I access or edit objects at runtime ?
for example :
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
This is all I can think of at the moment, please let me know what I misunderstand about D in regards to this topic.
I've looked through the documentation on dlang.org and couldn't really find something to do with reflection, or at least not in the way that Java has.
ps, the above code is pseudo-code which I just made up on the spot, I'm sure for whatever reasons it would not actually compile, I'm just hoping to get through that I want access to objects in a specific way just so it convenient for me.
reflection d
add a comment |
Does D have reflection or anything close to it, to be able to access objects at runtime ?
if not :
How exactly would I access or edit objects at runtime ?
for example :
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
This is all I can think of at the moment, please let me know what I misunderstand about D in regards to this topic.
I've looked through the documentation on dlang.org and couldn't really find something to do with reflection, or at least not in the way that Java has.
ps, the above code is pseudo-code which I just made up on the spot, I'm sure for whatever reasons it would not actually compile, I'm just hoping to get through that I want access to objects in a specific way just so it convenient for me.
reflection d
Does D have reflection or anything close to it, to be able to access objects at runtime ?
if not :
How exactly would I access or edit objects at runtime ?
for example :
bool myFunc(string status){
switch(status){
case "create":
Object my_obj = new createObject();
write("Object has been created or whatever");
break;
case "mutate":
//Don't want to have to make a global declaration
my_obj.attribute = "A meme";
write("Object has been mutated or whatever");
break;
case "delete":
//Don't want to have to make a global declaration
delete();
write("Object has been deleted or whatever");
break;
default:
//blah
break;
}
return true;
}
void main(){
while(true)
{String status = readln();myFunc(status);}
}
This is all I can think of at the moment, please let me know what I misunderstand about D in regards to this topic.
I've looked through the documentation on dlang.org and couldn't really find something to do with reflection, or at least not in the way that Java has.
ps, the above code is pseudo-code which I just made up on the spot, I'm sure for whatever reasons it would not actually compile, I'm just hoping to get through that I want access to objects in a specific way just so it convenient for me.
reflection d
reflection d
edited Nov 25 '18 at 9:49
timi95
asked Nov 24 '18 at 20:33
timi95timi95
9911
9911
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
Yes, D has reflection, but no, it is not like Java.
D's reflection comes in the form of compile time building blocks, rather than runtime methods. Of course, you can create the runtime methods yourself, but it won't just work out of the box with everything.
I actually just wrote a thing today that reflection loops over a method to show its properties and let you edit it: https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
Anyway, the way I do it is to create methods out of simple loops using the reflection info. The language provides two facilities, __traits, and the is expression to do this:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
And the standard library wraps and extends with the std.traits module
http://dpldocs.info/experimental-docs/std.traits.html
(or if you prefer the official website of basically the same docs, just imo harder to read/navigate: https://dlang.org/phobos/std_traits.html )
You can combine this with other code generation techniques like template mixins and traditional things like interfaces and constructors to create the runtime stuff.
But for a simple case, try something like this:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", ));
writeln(obj.call("anotherMethod", ["5"]));
}
It might help to copy/paste that code out of the website and read the comments in your regular editor, since Stack Overflow will often make you scroll and that is hard. I show the basic ideas in the comments.
Once you write that reflection bridge function once though and make it work somehow that you are happy with it... you can add as many methods as you want and it will work!
In fact, you can even make the call
method there part of an interface, and the definition of the body part of a mixin template
(see https://dlang.org/spec/template-mixin.html ) and pop it into any of your classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
The interface lets you refer to it from base classes (same as Java, for the most part), and the mixin template lets you copy that reflection provider into each child class, so it gives all methods even on child classes. Java would do that for you automatically, but in D you do need to add that mixin line to each one. Not too much of a hassle, but something to consider. (It actually is possible for D to do it automatically too.. but it requires hacking the core runtime library, so it is a fairly advanced topic and only useful in special situations (since you must use that hacked library project-wide). So probably not useful to you, just hinting it is there.)
With the interface btw, you can also add a static constructor to your classes to register them in some runtime associative array or switch or whatever of class names to factory functions, and create them from strings too. No particularly special code to realize that, it is the same kind of pattern you have probably seen before, but if you need new class objects from strings instead of just editing existing objects, that's how I'd get started.
I'll leave those details for you to play with though, lemme know if anything here makes no sense.
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
add a comment |
The D programming language has support for compile-time reflection, but it does not support run-time reflection (like Java for an example). For more details on the compile-time reflection see Adam's answer.
10+ years ago Thomas Kühne wrote a brilliant package called FlectioneD ( http://dsource.org/projects/flectioned ) that is still a good reference on this topic...
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53462128%2fdoes-d-have-reflection%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Yes, D has reflection, but no, it is not like Java.
D's reflection comes in the form of compile time building blocks, rather than runtime methods. Of course, you can create the runtime methods yourself, but it won't just work out of the box with everything.
I actually just wrote a thing today that reflection loops over a method to show its properties and let you edit it: https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
Anyway, the way I do it is to create methods out of simple loops using the reflection info. The language provides two facilities, __traits, and the is expression to do this:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
And the standard library wraps and extends with the std.traits module
http://dpldocs.info/experimental-docs/std.traits.html
(or if you prefer the official website of basically the same docs, just imo harder to read/navigate: https://dlang.org/phobos/std_traits.html )
You can combine this with other code generation techniques like template mixins and traditional things like interfaces and constructors to create the runtime stuff.
But for a simple case, try something like this:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", ));
writeln(obj.call("anotherMethod", ["5"]));
}
It might help to copy/paste that code out of the website and read the comments in your regular editor, since Stack Overflow will often make you scroll and that is hard. I show the basic ideas in the comments.
Once you write that reflection bridge function once though and make it work somehow that you are happy with it... you can add as many methods as you want and it will work!
In fact, you can even make the call
method there part of an interface, and the definition of the body part of a mixin template
(see https://dlang.org/spec/template-mixin.html ) and pop it into any of your classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
The interface lets you refer to it from base classes (same as Java, for the most part), and the mixin template lets you copy that reflection provider into each child class, so it gives all methods even on child classes. Java would do that for you automatically, but in D you do need to add that mixin line to each one. Not too much of a hassle, but something to consider. (It actually is possible for D to do it automatically too.. but it requires hacking the core runtime library, so it is a fairly advanced topic and only useful in special situations (since you must use that hacked library project-wide). So probably not useful to you, just hinting it is there.)
With the interface btw, you can also add a static constructor to your classes to register them in some runtime associative array or switch or whatever of class names to factory functions, and create them from strings too. No particularly special code to realize that, it is the same kind of pattern you have probably seen before, but if you need new class objects from strings instead of just editing existing objects, that's how I'd get started.
I'll leave those details for you to play with though, lemme know if anything here makes no sense.
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
add a comment |
Yes, D has reflection, but no, it is not like Java.
D's reflection comes in the form of compile time building blocks, rather than runtime methods. Of course, you can create the runtime methods yourself, but it won't just work out of the box with everything.
I actually just wrote a thing today that reflection loops over a method to show its properties and let you edit it: https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
Anyway, the way I do it is to create methods out of simple loops using the reflection info. The language provides two facilities, __traits, and the is expression to do this:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
And the standard library wraps and extends with the std.traits module
http://dpldocs.info/experimental-docs/std.traits.html
(or if you prefer the official website of basically the same docs, just imo harder to read/navigate: https://dlang.org/phobos/std_traits.html )
You can combine this with other code generation techniques like template mixins and traditional things like interfaces and constructors to create the runtime stuff.
But for a simple case, try something like this:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", ));
writeln(obj.call("anotherMethod", ["5"]));
}
It might help to copy/paste that code out of the website and read the comments in your regular editor, since Stack Overflow will often make you scroll and that is hard. I show the basic ideas in the comments.
Once you write that reflection bridge function once though and make it work somehow that you are happy with it... you can add as many methods as you want and it will work!
In fact, you can even make the call
method there part of an interface, and the definition of the body part of a mixin template
(see https://dlang.org/spec/template-mixin.html ) and pop it into any of your classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
The interface lets you refer to it from base classes (same as Java, for the most part), and the mixin template lets you copy that reflection provider into each child class, so it gives all methods even on child classes. Java would do that for you automatically, but in D you do need to add that mixin line to each one. Not too much of a hassle, but something to consider. (It actually is possible for D to do it automatically too.. but it requires hacking the core runtime library, so it is a fairly advanced topic and only useful in special situations (since you must use that hacked library project-wide). So probably not useful to you, just hinting it is there.)
With the interface btw, you can also add a static constructor to your classes to register them in some runtime associative array or switch or whatever of class names to factory functions, and create them from strings too. No particularly special code to realize that, it is the same kind of pattern you have probably seen before, but if you need new class objects from strings instead of just editing existing objects, that's how I'd get started.
I'll leave those details for you to play with though, lemme know if anything here makes no sense.
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
add a comment |
Yes, D has reflection, but no, it is not like Java.
D's reflection comes in the form of compile time building blocks, rather than runtime methods. Of course, you can create the runtime methods yourself, but it won't just work out of the box with everything.
I actually just wrote a thing today that reflection loops over a method to show its properties and let you edit it: https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
Anyway, the way I do it is to create methods out of simple loops using the reflection info. The language provides two facilities, __traits, and the is expression to do this:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
And the standard library wraps and extends with the std.traits module
http://dpldocs.info/experimental-docs/std.traits.html
(or if you prefer the official website of basically the same docs, just imo harder to read/navigate: https://dlang.org/phobos/std_traits.html )
You can combine this with other code generation techniques like template mixins and traditional things like interfaces and constructors to create the runtime stuff.
But for a simple case, try something like this:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", ));
writeln(obj.call("anotherMethod", ["5"]));
}
It might help to copy/paste that code out of the website and read the comments in your regular editor, since Stack Overflow will often make you scroll and that is hard. I show the basic ideas in the comments.
Once you write that reflection bridge function once though and make it work somehow that you are happy with it... you can add as many methods as you want and it will work!
In fact, you can even make the call
method there part of an interface, and the definition of the body part of a mixin template
(see https://dlang.org/spec/template-mixin.html ) and pop it into any of your classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
The interface lets you refer to it from base classes (same as Java, for the most part), and the mixin template lets you copy that reflection provider into each child class, so it gives all methods even on child classes. Java would do that for you automatically, but in D you do need to add that mixin line to each one. Not too much of a hassle, but something to consider. (It actually is possible for D to do it automatically too.. but it requires hacking the core runtime library, so it is a fairly advanced topic and only useful in special situations (since you must use that hacked library project-wide). So probably not useful to you, just hinting it is there.)
With the interface btw, you can also add a static constructor to your classes to register them in some runtime associative array or switch or whatever of class names to factory functions, and create them from strings too. No particularly special code to realize that, it is the same kind of pattern you have probably seen before, but if you need new class objects from strings instead of just editing existing objects, that's how I'd get started.
I'll leave those details for you to play with though, lemme know if anything here makes no sense.
Yes, D has reflection, but no, it is not like Java.
D's reflection comes in the form of compile time building blocks, rather than runtime methods. Of course, you can create the runtime methods yourself, but it won't just work out of the box with everything.
I actually just wrote a thing today that reflection loops over a method to show its properties and let you edit it: https://twitter.com/adamdruppe/status/1066390612516179968 It is not done yet but I'll link so you can see some of it in action: https://github.com/adamdruppe/arsd/commit/5411dd9844869909466f2091391abe091b529bf8#diff-7a88942576365ca3d7f389b766344477R6587
Anyway, the way I do it is to create methods out of simple loops using the reflection info. The language provides two facilities, __traits, and the is expression to do this:
https://dlang.org/spec/traits.html
https://dlang.org/spec/expression.html#IsExpression
And the standard library wraps and extends with the std.traits module
http://dpldocs.info/experimental-docs/std.traits.html
(or if you prefer the official website of basically the same docs, just imo harder to read/navigate: https://dlang.org/phobos/std_traits.html )
You can combine this with other code generation techniques like template mixins and traditional things like interfaces and constructors to create the runtime stuff.
But for a simple case, try something like this:
import std.stdio;
import std.conv;
import std.traits;
class MyClass {
void myMethod() {}
int anotherMethod(int a) { return a; }
// this is the runtime bridge function. The trick here is to do
// a switch, just like your example, but the innards are auto-generated
// from the compile time reflection.
string call(string methodName, string args) {
// it starts as a plain switch...
method_switch: switch(methodName) {
// but inside, we use a static foreach - a compile-time loop -
// over the __traits(allMembers) magic, which gives a list of all member names
static foreach(inspecting; __traits(allMembers, typeof(this))) {
case inspecting: { // you can create switch cases inside these static loops
// for this example, I only want to use callable methods, so this
// static if - a compile time if statement - will filter out anything else.
//
// It is possible to do more, like plain data members, child classes, and more,
// but that will add a lot more code. Same basic ideas with each of them though.
static if(isCallable!(__traits(getMember, this, inspecting))) {
// after we confirm it is callable, we can get a delegate of it
// (same as normally doing `&member.method`) to call later.
auto callable = &__traits(getMember, this, inspecting);
// next is building the argument list. Parameters comes from the std.traits
// module in the standard library and gives an object representing the function's
// parameters. We can loop over these and set them!
Parameters!callable arguments;
foreach(i, ref arg; arguments) { // ref loop cuz we setting the arg members..
// so for the runtime bridge here, I took everything as strings and
// want to convert them to the actual method arguments. In many cases,
// that automatic conversion will not be possible. The next bit of magic,
// __traits(compiles), will take some code and return true if it successfully
// compiles. Using the static if block, I can turn what would be compile time
// errors into a runtime exception instead.
static if(__traits(compiles, to!(typeof(arg))(args[i])))
// note that to is from the stdlib again: std.conv. It converts
// a thing from one type to another. Here, I ask it to convert our
// string (args is the runtime array of strings the user passed) to
// whatever the type is that the method expects.
//
// Note that this might throw an exception if the string is wrong.
// For example, passing "lol" to a method expecting an int will throw
// an exception saying cannot convert string "lol" to int.
arg = to!(typeof(arg))(args[i]);
else
// or if the conversion didn't compile at all, it will always throw.
throw new Exception("method " ~ methodName ~ " not callable with this reflection code because of incompatible argument type");
}
// now that we have the arguments, time to tackle the return value.
// the main special case here is a void return - that is not a value
// and thus cannot be converted. So we do it separately.
// Otherwise, though, inside we just call our callable from above with
// the arguments from above and return the value converted to string!
static if(is(ReturnType!callable == void)) {
// it returned void, so call it and return null
// because the language won't let us return void
// directly as a string nor convert it easily.
callable(arguments);
return null;
} else {
// it returned something else, just call the function
// and convert it to a string
return to!string(callable(arguments));
}
}
} break method_switch;
}
default:
throw new Exception("no such method " ~ methodName);
}
assert(0); // not reached
}
}
// and let's call them with some strings. You could also get these strings from
// the user (just btw remember to .strip them if they come from readln, otherwise
// the trailing newline character will cause a method not found exception.)
void main() {
auto obj = new MyClass();
writeln(obj.call("myMethod", ));
writeln(obj.call("anotherMethod", ["5"]));
}
It might help to copy/paste that code out of the website and read the comments in your regular editor, since Stack Overflow will often make you scroll and that is hard. I show the basic ideas in the comments.
Once you write that reflection bridge function once though and make it work somehow that you are happy with it... you can add as many methods as you want and it will work!
In fact, you can even make the call
method there part of an interface, and the definition of the body part of a mixin template
(see https://dlang.org/spec/template-mixin.html ) and pop it into any of your classes.
class MyNewClass : Reflectable {
mixin CallImplementation;
}
// it will now work there!
The interface lets you refer to it from base classes (same as Java, for the most part), and the mixin template lets you copy that reflection provider into each child class, so it gives all methods even on child classes. Java would do that for you automatically, but in D you do need to add that mixin line to each one. Not too much of a hassle, but something to consider. (It actually is possible for D to do it automatically too.. but it requires hacking the core runtime library, so it is a fairly advanced topic and only useful in special situations (since you must use that hacked library project-wide). So probably not useful to you, just hinting it is there.)
With the interface btw, you can also add a static constructor to your classes to register them in some runtime associative array or switch or whatever of class names to factory functions, and create them from strings too. No particularly special code to realize that, it is the same kind of pattern you have probably seen before, but if you need new class objects from strings instead of just editing existing objects, that's how I'd get started.
I'll leave those details for you to play with though, lemme know if anything here makes no sense.
answered Nov 24 '18 at 21:38
Adam D. RuppeAdam D. Ruppe
22.7k43353
22.7k43353
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
add a comment |
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
1
1
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
BTW I also talked about this and more similar stuff in the reflection chapter of my book: packtpub.com/application-development/d-cookbook that used to be the free sample chapter, but it looks like they changed that... still, you might be able to get more out of that if this answer doesn't get you far enough for your needs.
– Adam D. Ruppe
Nov 24 '18 at 21:39
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
Hey this could be a silly question, but could you call the constructor and set stuff with it using your version of reflection ? Like : auto obj = new MyClass(); writeln(obj.call("MyClass", ["attribute1","attribute2","attribute3"]));
– timi95
Nov 25 '18 at 17:03
1
1
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
You could, but it won't automatically cover constructors like that. You'd have to add them to the switch yourself.
– Adam D. Ruppe
Nov 25 '18 at 20:49
add a comment |
The D programming language has support for compile-time reflection, but it does not support run-time reflection (like Java for an example). For more details on the compile-time reflection see Adam's answer.
10+ years ago Thomas Kühne wrote a brilliant package called FlectioneD ( http://dsource.org/projects/flectioned ) that is still a good reference on this topic...
add a comment |
The D programming language has support for compile-time reflection, but it does not support run-time reflection (like Java for an example). For more details on the compile-time reflection see Adam's answer.
10+ years ago Thomas Kühne wrote a brilliant package called FlectioneD ( http://dsource.org/projects/flectioned ) that is still a good reference on this topic...
add a comment |
The D programming language has support for compile-time reflection, but it does not support run-time reflection (like Java for an example). For more details on the compile-time reflection see Adam's answer.
10+ years ago Thomas Kühne wrote a brilliant package called FlectioneD ( http://dsource.org/projects/flectioned ) that is still a good reference on this topic...
The D programming language has support for compile-time reflection, but it does not support run-time reflection (like Java for an example). For more details on the compile-time reflection see Adam's answer.
10+ years ago Thomas Kühne wrote a brilliant package called FlectioneD ( http://dsource.org/projects/flectioned ) that is still a good reference on this topic...
answered Nov 26 '18 at 14:54
DejanLekicDejanLekic
10.7k32957
10.7k32957
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53462128%2fdoes-d-have-reflection%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown