digitalmars.D - compile time 'address'
- Dominic Jones (50/50) Nov 29 2018 Hello,
- bauss (3/53) Nov 29 2018 Use enum instead of auto.
- Steven Schveighoffer (24/86) Nov 29 2018 So this is a combination of 2 problems.
- Dominic Jones (13/23) Nov 29 2018 Hello Steve,
- Steven Schveighoffer (17/46) Nov 29 2018 Ah, ok. Essentially you are looking for the layout of the local variable...
- Stanislav Blinov (11/15) Nov 29 2018 This compiles since 2.069.2 (according to run.dlang.io):
- Steven Schveighoffer (9/26) Nov 29 2018 I think the issue is that D is expecting you to be able to use the value...
- Stanislav Blinov (6/25) Nov 29 2018 Yeah, but this one seems a bit deliberate. The asserts hold, but
- Alex (4/7) Nov 29 2018 What would you say to this:
- Dominic Jones (9/14) Nov 29 2018 This is the same situation as C++; the addresses can only be
- Dominic Jones (24/42) Nov 29 2018 This is an interesting idea.
- Steven Schveighoffer (26/83) Nov 29 2018 Hm... the offsetof feature would break down there. But perhaps a frame
- aliak (14/20) Nov 29 2018 I think you can do that by using aliases. This seems to work:
- Steven Schveighoffer (3/24) Nov 29 2018 Sweet :) wish I had thought of that earlier...
- Dominic Jones (39/49) Nov 30 2018 Unfortunately, with the solution above, the variables are passed
Hello, I would like to capture the "address" of variables at compile time. Something not far from this can be done in C++, namely addresses can be compared but not captured (then compared, or whatever). I was hoping that it could be done in D, so I wrote something similar to test. It seems that D is more restrictive than C++ in this matter, as even comparison cannot be performed at compile time, as far as I can tell. What I wish to do may in principle not be possible, but I am not familiar enough with compilers to know for sure. Also, I am not so familiar with D, so a better transliteration from the C++ example maybe possible and yield something on par or better. The reason for wanting to do all this is to find some way of 'baking in' the name of a variable (or some locally unique identifier) into an expression tree node type, facilitating certain expression tree transforms. The closest I can get to what I want makes use of mixins, but is not ideal (https://run.dlang.io/is/Fr2b3f). C++ (at: https://godbolt.org/z/CLPzGq) template<typename T, typename U> auto constexpr cmp(T const &t, U const &u) { /* Preferable, but not possible: auto constexpr ta = &t; // capture auto constexpr ua = &u; return ta == ua; */ return &t == &u; } int main() { auto c0 = 1; auto c1 = 2; static_assert(cmp(c0, c0)); static_assert(!cmp(c0, c1)); } D (at: https://run.dlang.io/is/rf5azp) auto cmp(T, U)(const ref T t, const ref U u) { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; // error: "variable c0 cannot be read at compile time" static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); }
Nov 29 2018
On Thursday, 29 November 2018 at 15:56:54 UTC, Dominic Jones wrote:Hello, I would like to capture the "address" of variables at compile time. Something not far from this can be done in C++, namely addresses can be compared but not captured (then compared, or whatever). I was hoping that it could be done in D, so I wrote something similar to test. It seems that D is more restrictive than C++ in this matter, as even comparison cannot be performed at compile time, as far as I can tell. What I wish to do may in principle not be possible, but I am not familiar enough with compilers to know for sure. Also, I am not so familiar with D, so a better transliteration from the C++ example maybe possible and yield something on par or better. The reason for wanting to do all this is to find some way of 'baking in' the name of a variable (or some locally unique identifier) into an expression tree node type, facilitating certain expression tree transforms. The closest I can get to what I want makes use of mixins, but is not ideal (https://run.dlang.io/is/Fr2b3f). C++ (at: https://godbolt.org/z/CLPzGq) template<typename T, typename U> auto constexpr cmp(T const &t, U const &u) { /* Preferable, but not possible: auto constexpr ta = &t; // capture auto constexpr ua = &u; return ta == ua; */ return &t == &u; } int main() { auto c0 = 1; auto c1 = 2; static_assert(cmp(c0, c0)); static_assert(!cmp(c0, c1)); } D (at: https://run.dlang.io/is/rf5azp) auto cmp(T, U)(const ref T t, const ref U u) { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; // error: "variable c0 cannot be read at compile time" static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); }Use enum instead of auto.
Nov 29 2018
On 11/29/18 10:56 AM, Dominic Jones wrote:Hello, I would like to capture the "address" of variables at compile time. Something not far from this can be done in C++, namely addresses can be compared but not captured (then compared, or whatever). I was hoping that it could be done in D, so I wrote something similar to test. It seems that D is more restrictive than C++ in this matter, as even comparison cannot be performed at compile time, as far as I can tell. What I wish to do may in principle not be possible, but I am not familiar enough with compilers to know for sure. Also, I am not so familiar with D, so a better transliteration from the C++ example maybe possible and yield something on par or better. The reason for wanting to do all this is to find some way of 'baking in' the name of a variable (or some locally unique identifier) into an expression tree node type, facilitating certain expression tree transforms. The closest I can get to what I want makes use of mixins, but is not ideal (https://run.dlang.io/is/Fr2b3f). C++ (at: https://godbolt.org/z/CLPzGq) template<typename T, typename U> auto constexpr cmp(T const &t, U const &u) { /* Preferable, but not possible: auto constexpr ta = &t; // capture auto constexpr ua = &u; return ta == ua; */ return &t == &u; } int main() { auto c0 = 1; auto c1 = 2; static_assert(cmp(c0, c0)); static_assert(!cmp(c0, c1)); } D (at: https://run.dlang.io/is/rf5azp) auto cmp(T, U)(const ref T t, const ref U u) { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; // error: "variable c0 cannot be read at compile time" static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); }So this is a combination of 2 problems. 1. No you cannot read c0 or c1 at compile time, because they are runtime values. You can fix this by changing them to immutable or enum. 2. D doesn't like you taking the address of compile-time values, but the error message is really bad. If I do this: auto cmp(T, U)(const T t, const U u) // note, no ref { return t == u; // note, change to value not address } void main() { immutable c0 = 1; // note change to immutable immutable c1 = 2; // these now work. static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); } If I don't change away from ref, it complains that it can't read c0 or c1 at compile time, which clearly is shown to be readable at compile time. I think the error message should be more like "Cannot use reference to this variable at compile-time". -Steve
Nov 29 2018
Hello Steve, Thank you for your comments. A couple of replies: On Thursday, 29 November 2018 at 16:29:00 UTC, Steven Schveighoffer wrote:1. No you cannot read c0 or c1 at compile time, because they are runtime values. You can fix this by changing them to immutable or enum.The variables are deliberately 'auto' rather than 'auto constexpr' as per the C++ example, so I do wish to preserve that in the D example. Nevertheless, in C++ querying (at least to perform comparison) the sudo-address, whatever it is (perhaps, behind the scenes it is simply a stack frame offset), is permitted.2. D doesn't like you taking the address of compile-time values, but the error message is really bad. If I do this: auto cmp(T, U)(const T t, const U u) // note, no ref { return t == u; // note, change to value not address }Presumably, this is now simply comparing values held by the variables, rather than the sudo-address of the original variables as per the C++ example?
Nov 29 2018
On 11/29/18 12:59 PM, Dominic Jones wrote:Hello Steve, Thank you for your comments. A couple of replies: On Thursday, 29 November 2018 at 16:29:00 UTC, Steven Schveighoffer wrote:Ah, ok. Essentially you are looking for the layout of the local variables. One thing that is available for D is the compile-time property offsetof, which gives the offset from the start of an aggregate that an item is. for example: struct S { int x; bool y; } static assert(S.y.offsetof == 4); So while this isn't available for function locals, the idea is similar. But I don't think there's an equivalent to what you are able to do here in C++. I don't think it would be a huge stretch to apply this same property to function locals, but it would require an enhancement request. -Steve1. No you cannot read c0 or c1 at compile time, because they are runtime values. You can fix this by changing them to immutable or enum.The variables are deliberately 'auto' rather than 'auto constexpr' as per the C++ example, so I do wish to preserve that in the D example. Nevertheless, in C++ querying (at least to perform comparison) the sudo-address, whatever it is (perhaps, behind the scenes it is simply a stack frame offset), is permitted.2. D doesn't like you taking the address of compile-time values, but the error message is really bad. If I do this: auto cmp(T, U)(const T t, const U u) // note, no ref { return t == u; // note, change to value not address }Presumably, this is now simply comparing values held by the variables, rather than the sudo-address of the original variables as per the C++ example?
Nov 29 2018
On Thursday, 29 November 2018 at 18:49:27 UTC, Steven Schveighoffer wrote:I don't think it would be a huge stretch to apply this same property to function locals, but it would require an enhancement request. -SteveThis compiles since 2.069.2 (according to run.dlang.io): void main() { auto c0 = 1; auto c1 = 2; static assert(&c0 == &c0); static assert(&c0 != &c1); } I wonder if that is a bug, or that the original question not compiling is :D
Nov 29 2018
On 11/29/18 2:16 PM, Stanislav Blinov wrote:On Thursday, 29 November 2018 at 18:49:27 UTC, Steven Schveighoffer wrote:I think the issue is that D is expecting you to be able to use the value pointed at possibly in the function, which would normally be fully available at CTFE (the two variables are runtime variables). There's no way to accept a pointer that can't be used, just the value of the pointer checked. I'm not sure how it would work exactly. I suppose you could accept void *, I don't know if it's possible to recast in CTFE? -SteveI don't think it would be a huge stretch to apply this same property to function locals, but it would require an enhancement request.This compiles since 2.069.2 (according to run.dlang.io): void main() { auto c0 = 1; auto c1 = 2; static assert(&c0 == &c0); static assert(&c0 != &c1); } I wonder if that is a bug, or that the original question not compiling is :D
Nov 29 2018
On Thursday, 29 November 2018 at 20:22:16 UTC, Steven Schveighoffer wrote:Yeah, but this one seems a bit deliberate. The asserts hold, but you can't e.g. enum diff = &c0 - &c1; // 'c0' can't be used at compile timeThis compiles since 2.069.2 (according to run.dlang.io): void main() { auto c0 = 1; auto c1 = 2; static assert(&c0 == &c0); static assert(&c0 != &c1); } I wonder if that is a bug, or that the original question not compiling is :DI think the issue is that D is expecting you to be able to use the value pointed at possibly in the function, which would normally be fully available at CTFE (the two variables are runtime variables). There's no way to accept a pointer that can't be used, just the value of the pointer checked.I'm not sure how it would work exactly. I suppose you could accept void *, I don't know if it's possible to recast in CTFE?It isn't.
Nov 29 2018
On Thursday, 29 November 2018 at 21:00:57 UTC, Stanislav Blinov wrote:Yeah, but this one seems a bit deliberate. The asserts hold, but you can't e.g. enum diff = &c0 - &c1; // 'c0' can't be used at compile timeWhat would you say to this: https://forum.dlang.org/thread/tdmkajpnqkbywdnzddqc forum.dlang.org
Nov 29 2018
On Thursday, 29 November 2018 at 21:00:57 UTC, Stanislav Blinov wrote:This is the same situation as C++; the addresses can only be compared (with == or !=). Any attempt to capture their values first will cause a compilation failure, i.e. attempting: // "error: '& t' is not a constant expression", etc auto constexpr ta = &t; auto constexpr ua = &u; return ta == ua;There's no way to accept a pointer that can't be used, just the value of the pointer checked.Yeah, but this one seems a bit deliberate. The asserts hold, but you can't e.g. enum diff = &c0 - &c1; // 'c0' can't be used at compile time
Nov 29 2018
On Thursday, 29 November 2018 at 18:49:27 UTC, Steven Schveighoffer wrote:Ah, ok. Essentially you are looking for the layout of the local variables. One thing that is available for D is the compile-time property offsetof, which gives the offset from the start of an aggregate that an item is. for example: struct S { int x; bool y; } static assert(S.y.offsetof == 4); So while this isn't available for function locals, the idea is similar. But I don't think there's an equivalent to what you are able to do here in C++. I don't think it would be a huge stretch to apply this same property to function locals, but it would require an enhancement request.This is an interesting idea. Somehow it would be helpful to access the frame pointer, too, from the variable. Obtaining this offset via the variable would enable disambiguation between variables with the same local offset but from different functions. The example would then read something like: auto cmp(T, U)(const ref T t, const ref U u) { // i.e. // t.frame.offsetof := &main; // t.offsetof := main.c0.offsetof; enum f = t.frame.offsetof == u.frame.offsetof; enum v = t.offsetof == u.offsetof; return f && v; } void main() { auto c0 = 1; auto c1 = 2; static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); }
Nov 29 2018
On 11/29/18 5:56 PM, Dominic Jones wrote:On Thursday, 29 November 2018 at 18:49:27 UTC, Steven Schveighoffer wrote:Hm... the offsetof feature would break down there. But perhaps a frame (or probably __traits call) could provide the function symbol, so you could do: static assert(t.frame is u.frame); Strawman could be __traits(getAggregate, sym) => symbol that contains sym, be it a function or struct or whatever.Ah, ok. Essentially you are looking for the layout of the local variables. One thing that is available for D is the compile-time property offsetof, which gives the offset from the start of an aggregate that an item is. for example: struct S { int x; bool y; } static assert(S.y.offsetof == 4); So while this isn't available for function locals, the idea is similar. But I don't think there's an equivalent to what you are able to do here in C++. I don't think it would be a huge stretch to apply this same property to function locals, but it would require an enhancement request.This is an interesting idea. Somehow it would be helpful to access the frame pointer, too, from the variable. Obtaining this offset via the variable would enable disambiguation between variables with the same local offset but from different functions.The example would then read something like: auto cmp(T, U)(const ref T t, const ref U u) { // i.e. // t.frame.offsetof := &main; // t.offsetof := main.c0.offsetof; enum f = t.frame.offsetof == u.frame.offsetof; enum v = t.offsetof == u.offsetof; return f && v; } void main() { auto c0 = 1; auto c1 = 2; static assert(cmp(c0, c0)); static assert(!cmp(c0, c1)); }I was thinking you'd do it with D compile time features: bool cmp(alias var1, alias var2) = var1.offsetof == var2.offsetof; struct S { int x; int y; } // this actually works today: static assert(cmp!(S.x, S.x)); static assert(!cmp!(S.x, S.y)); void main() { auto c0 = 1; auto c1 = 2; // proposed static assert(cmp!(c0, c0)); static assert(!cmp!(c0, c1)); } -Steve
Nov 29 2018
On Thursday, 29 November 2018 at 15:56:54 UTC, Dominic Jones wrote:Hello, I would like to capture the "address" of variables at compile time. Something not far from this can be done in C++, namely addresses can be compared but not captured (then compared, or whatever). [...]I think you can do that by using aliases. This seems to work: auto cmp(alias t, alias u)() { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; static assert(cmp!(c0, c0)); static assert(!cmp!(c0, c1)); } Cheers, - Ali
Nov 29 2018
On 11/29/18 7:23 PM, aliak wrote:On Thursday, 29 November 2018 at 15:56:54 UTC, Dominic Jones wrote:Sweet :) wish I had thought of that earlier... -SteveHello, I would like to capture the "address" of variables at compile time. Something not far from this can be done in C++, namely addresses can be compared but not captured (then compared, or whatever). [...]I think you can do that by using aliases. This seems to work: auto cmp(alias t, alias u)() { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; static assert(cmp!(c0, c0)); static assert(!cmp!(c0, c1)); }
Nov 29 2018
On Friday, 30 November 2018 at 00:23:08 UTC, aliak wrote:I think you can do that by using aliases. This seems to work: auto cmp(alias t, alias u)() { return &t == &u; } void main() { auto c0 = 1; auto c1 = 2; static assert(cmp!(c0, c0)); static assert(!cmp!(c0, c1)); }Unfortunately, with the solution above, the variables are passed as template arguments. Somehow, I need to have the variables being passed as regular arguments since my intended use case is operator overloading for expression trees. Consider the example, below: somehow, I need to be able to know the nominal addresses of parameters passed into 'opBinary' so that the 'Binary' template may be defined with address tags which relate to the operands of 'opBinary'. If the types and tags of the two variables match then there may be a guarantee (at the file scope) that the arguments are indeed the same. struct Terminal { auto opBinary(string op, R)(const ref R r) { enum l_adr = 0; // proposed: this.scope.offsetof + this.offsetof; enum r_adr = 4; // proposed: r.scope.offsetof + r.offsetof; return Binary!(op, typeof(this), R, l_adr, r_adr)(this, r); } } struct Binary(string op, L, R, int l_adr, int r_adr) { L l; R r; } void main() { Terminal c0; Terminal c1; pragma(msg, typeof(c0 + c1)); // 'Binary!("+", Terminal, Terminal, 0, 4)' // pragma(msg, typeof(transform(c0 + c0))); // becomes: "2 * c0" // pragma(msg, typeof(transform(c0 + c1))); // becomes: "c0 + c1" } (at: https://run.dlang.io/is/hwVI5n) -Dominic
Nov 30 2018