www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to check if an array is a manifest constant?

reply simendsjo <simen.endsjo pandavre.com> writes:
The behavior for manifest constant arrays is different from regular arrays and
const/immutable
arrays. The problem is that typeof() returns T[]. How can I see if the array is
a manifest
constant?


	void g(int[] x) { }

	const c = [1,2,3];
	static assert(is(typeof(c) == const(int[])));
	// cannot convert const(int[]) to int[]
	static assert(!__traits(compiles, g(c)));
	auto carr = c;
	static assert(is(typeof(carr) == const(int[])));
	assert(carr.ptr == c.ptr); // referenced

	immutable i = [1,2,3];
	static assert(is(typeof(i) == immutable(int[])));
	// cannot convert immutable(int[]) to int[]
	static assert(!__traits(compiles, g(i)));
	auto iarr = i;
	static assert(is(typeof(iarr) == immutable(int[])));
	assert(iarr.ptr == i.ptr); // referenced

	enum e = [1,2,3];
	// e is reported as int[] even if it's an enum
	static assert(is(typeof(e) == int[]));
	//static assert(is(typeof(e) == enum)); // not an enum (as expected)
	// it can be passed to funtions taking dynamic arrays
	void f(int[] x) {
		assert(e.ptr != x.ptr); // then the content is copied
	}
	f(e);
	// the behavior is different from other assignments
	// as the content is copied
	auto earr = e;
	assert(earr.ptr != e.ptr); // content is copied
Apr 03 2011
next sibling parent =?UTF-8?B?QWxla3NhbmRhciBSdcW+acSNacSH?= <ruzicic.aleksandar gmail.com> writes:
As far as I understand, manifest constants and enums only share the
same keyword (enum) and are completely different things.
Enumerations (like enum A { a,b, c}) define new type, while manifest
constants (like enum N =3D 42;) are just constant values with type
inferred from value (unless specified after enum keyword).

On Sun, Apr 3, 2011 at 5:29 PM, simendsjo <simen.endsjo pandavre.com> wrote=
:
 The behavior for manifest constant arrays is different from regular array=
s and const/immutable
 arrays. The problem is that typeof() returns T[]. How can I see if the ar=
ray is a manifest
 constant?


 =C2=A0 =C2=A0 =C2=A0 =C2=A0void g(int[] x) { }

 =C2=A0 =C2=A0 =C2=A0 =C2=A0const c =3D [1,2,3];
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(is(typeof(c) =3D=3D const(int[])=
));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// cannot convert const(int[]) to int[]
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(!__traits(compiles, g(c)));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0auto carr =3D c;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(is(typeof(carr) =3D=3D const(int=
[])));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert(carr.ptr =3D=3D c.ptr); // referenced

 =C2=A0 =C2=A0 =C2=A0 =C2=A0immutable i =3D [1,2,3];
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(is(typeof(i) =3D=3D immutable(in=
t[])));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// cannot convert immutable(int[]) to int[]
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(!__traits(compiles, g(i)));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0auto iarr =3D i;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(is(typeof(iarr) =3D=3D immutable=
(int[])));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert(iarr.ptr =3D=3D i.ptr); // referenced

 =C2=A0 =C2=A0 =C2=A0 =C2=A0enum e =3D [1,2,3];
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// e is reported as int[] even if it's an enum
 =C2=A0 =C2=A0 =C2=A0 =C2=A0static assert(is(typeof(e) =3D=3D int[]));
 =C2=A0 =C2=A0 =C2=A0 =C2=A0//static assert(is(typeof(e) =3D=3D enum)); //=
not an enum (as expected)
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// it can be passed to funtions taking dynamic=
arrays
 =C2=A0 =C2=A0 =C2=A0 =C2=A0void f(int[] x) {
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert(e.ptr !=3D =
x.ptr); // then the content is copied
 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
 =C2=A0 =C2=A0 =C2=A0 =C2=A0f(e);
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// the behavior is different from other assign=
ments
 =C2=A0 =C2=A0 =C2=A0 =C2=A0// as the content is copied
 =C2=A0 =C2=A0 =C2=A0 =C2=A0auto earr =3D e;
 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert(earr.ptr !=3D e.ptr); // content is cop=
ied

Apr 03 2011
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-04-03 08:29, simendsjo wrote:
 The behavior for manifest constant arrays is different from regular arrays
 and const/immutable arrays. The problem is that typeof() returns T[]. How
 can I see if the array is a manifest constant?
 
 
 	void g(int[] x) { }
 
 	const c = [1,2,3];
 	static assert(is(typeof(c) == const(int[])));
 	// cannot convert const(int[]) to int[]
 	static assert(!__traits(compiles, g(c)));
 	auto carr = c;
 	static assert(is(typeof(carr) == const(int[])));
 	assert(carr.ptr == c.ptr); // referenced
 
 	immutable i = [1,2,3];
 	static assert(is(typeof(i) == immutable(int[])));
 	// cannot convert immutable(int[]) to int[]
 	static assert(!__traits(compiles, g(i)));
 	auto iarr = i;
 	static assert(is(typeof(iarr) == immutable(int[])));
 	assert(iarr.ptr == i.ptr); // referenced
 
 	enum e = [1,2,3];
 	// e is reported as int[] even if it's an enum
 	static assert(is(typeof(e) == int[]));
 	//static assert(is(typeof(e) == enum)); // not an enum (as expected)
 	// it can be passed to funtions taking dynamic arrays
 	void f(int[] x) {
 		assert(e.ptr != x.ptr); // then the content is copied
 	}
 	f(e);
 	// the behavior is different from other assignments
 	// as the content is copied
 	auto earr = e;
 	assert(earr.ptr != e.ptr); // content is copied
There should be _no_ difference between arrays which are manifest constants and regular arrays which are immutable except that they're computed at compile time. However, there is currently a bug in CTFE which makes it so that an array is copied _every_ time that it is accessed in CTFE (which, among other things, causes a massive memory leak). I don't remember what the bug report is for it, but Don is currently overhauling CTFE so that this won't be the case anymore. So, this _should_ be fixed by the next release. But there should be no need to worry about whether something is a manifest constant or not. If there is, you're either doing something funny/wrong, or it's because of a bug like the one that Don is fixing. - Jonathan M Davis
Apr 03 2011
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 But there should be no need to worry about whether something is a manifest 
 constant or not.
I'd like in DMD a way to know if something is a compile-time constant. There is a GCC extension that sometimes is able to do it, named __builtin_constant_p: http://www.delorie.com/gnu/docs/gcc/gcc_81.html#IDX629 Bye, bearophile
Apr 03 2011
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-04-03 17:25, bearophile wrote:
 Jonathan M Davis:
 But there should be no need to worry about whether something is a
 manifest constant or not.
I'd like in DMD a way to know if something is a compile-time constant. There is a GCC extension that sometimes is able to do it, named __builtin_constant_p: http://www.delorie.com/gnu/docs/gcc/gcc_81.html#IDX629
To what end? Why would you care? It matters whether a value is mutable, const, or immutable, but what does it matter if it was calculated at compile time or not? Unless it's a property, it's definitely going to have been calculated before you use it. The only reason that I see that it matters at the moment is due to bugs in dmd. Once those are fixed, it should be irrelevant. - Jonathan M Davis
Apr 03 2011
prev sibling parent reply simendsjo <simen.endsjo pandavre.com> writes:
But there is a difference in how they behave, and I have no way of checking
this behavior.
Consider the following little snippet:

void f(int[] a) {
    a[0] = -1;
}

void main() {
    int[] a = [1,2,3];
    static assert(is(typeof(a) == int[]));
    f(a);
    assert(a[0] == -1); // a passed by reference, a[0] changed

    enum b = [1,2,3];
    static assert(is(typeof(b) == int[])); // a regular int[] you say?
    f(b);
    assert(b[0] == -1); // b passed by value, b[0] unchanged
}
Apr 04 2011
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-04-04 01:16, simendsjo wrote:
 But there is a difference in how they behave, and I have no way of checking
 this behavior. Consider the following little snippet:
 
 void f(int[] a) {
     a[0] = -1;
 }
 
 void main() {
     int[] a = [1,2,3];
     static assert(is(typeof(a) == int[]));
     f(a);
     assert(a[0] == -1); // a passed by reference, a[0] changed
 
     enum b = [1,2,3];
     static assert(is(typeof(b) == int[])); // a regular int[] you say?
     f(b);
     assert(b[0] == -1); // b passed by value, b[0] unchanged
 }
Okay. I just re-read the appropriate section in TDPL, and it's clear from the book that it's definitely intended that a new copy of the enum value be created every time that it's used (I was under the impression that that was buggy behavior, but apparently that's not true - though the fact that accessing any array during CTFE results in a copy of that array _is_ buggy behavior and should be fixed by Don before the next release). Regardless of that, however, enum values shouldn't be used in any place where they're going to be altered. So, passing such a value to a function which is intended to alter the value that it's passed to is just plain a bug on the part of the caller. It shouldn't matter to the function being called. That being the case, I don't see why you would need to determine whether a value that you're dealing with is an enum or not using template constraints or static if or whatnot. You should know whether the variable that you're using is an enum or not. If it is, don't alter it until you've set it to a variable - then you alter the variable. What use case do you have for wanting to know whether a variable is an enum or not? - Jonathan M Davis
Apr 04 2011
parent reply simendsjo <simen.endsjo pandavre.com> writes:
 What use case do you have for wanting to know whether a variable is an enum or
not?
The same reason I'd like to check if it's const/immutable; if an algorithm requires a modifiable array. void sortInPlace(int[] array) { array.sort; } void main() { int[] a = [3,2,1]; sortInPlace(a); assert(a == [1,2,3]); auto i = [3,2,1].idup; //sortInPlace(i); // Fails at compile time enum e = [3,2,1]; sortInPlace(e); assert(e == [1,2,3]); // fails at runtime } I can check the type for const or immutable with template constraits or static if's, but I cannot check for enum. I can use ref int[] as a parameter to get a compile time error, but that's misleading as I often don't want to reassign the parameter. Cannot the type of enum e = [1] be immutable(int[])? Or is that wrong? Or am I missing something crucial because of CTFE or other stuff? I've just started looking into D2, so theres a good possibility my reasoning is wrong.
Apr 04 2011
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-04-04 03:11, simendsjo wrote:
 What use case do you have for wanting to know whether a variable is an
 enum or not?
The same reason I'd like to check if it's const/immutable; if an algorithm requires a modifiable array. void sortInPlace(int[] array) { array.sort; } void main() { int[] a = [3,2,1]; sortInPlace(a); assert(a == [1,2,3]); auto i = [3,2,1].idup; //sortInPlace(i); // Fails at compile time enum e = [3,2,1]; sortInPlace(e); assert(e == [1,2,3]); // fails at runtime } I can check the type for const or immutable with template constraits or static if's, but I cannot check for enum. I can use ref int[] as a parameter to get a compile time error, but that's misleading as I often don't want to reassign the parameter. Cannot the type of enum e = [1] be immutable(int[])? Or is that wrong? Or am I missing something crucial because of CTFE or other stuff? I've just started looking into D2, so theres a good possibility my reasoning is wrong.
Apparently, part of the point of using enums is to have them be replaced with the value without having any kind of address. So, they're _supposed_ to be a copy and therefore don't have to be const or immutable. Generally, it should be clear whether a function alters its arguments or not. You can't use const or immutable arrays with a function which alters its array argument or the elements of its array argument, because the array itself can't be altered. In the case of enums, it _can_ be altered, because when you use it, you get a copy. It literally copies and pastes the value of the enum where you use it. So, there's _nothing_ wrong with passing an enum which is an array to a function which alters the elements of its array argument. It's perfectly legal. It's just generally a bad idea, since it's a temporary. There's no need to check whether a function's argument is an enum anymore than there's a need to check whether the argument is a temporary. Both are perfectly legal, just generally pointless. If a slice of the array is returned or saved in a global variable or whatnot, then it's not pointless. But if it's just altering the function's argument, then it is. But the function doesn't need to worry about it. It's still perfectly legal. It's up to the caller to actually pass an array that's worth altering. And since the function should be clear on whether it alters its arguments, it shouldn't be a problem that it's up to the caller to use it correctly with regards to temporaries and enums - just like it's up to the caller not to be stupid and pass any other array that it doesn't actually want to alter. So, really there's no reason to stop an enum from working with a function that alters the elements of an array. All temporaries have the same issue. If you really want to prevent it, then just make the argument a ref. It also makes it clear that you're altering the array. If you don't want to make it a ref, then just make the documentation clear, and it's up to the caller to use the function appropriately. For instance, it should be obvious to the caller that passing an enum to a function like sortInPlace would be just stupid. - Jonathan M Davis
Apr 04 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-04-03 11:03, Aleksandar Ru=C5=BEi=C4=8Di=C4=87 wrote:
 As far as I understand, manifest constants and enums only share the
 same keyword (enum) and are completely different things.
 Enumerations (like enum A { a,b, c}) define new type, while manifest
 constants (like enum N =3D 42;) are just constant values with type
 inferred from value (unless specified after enum keyword).
They're the same and they're not. A named enum creates a new type of sorts = and=20 namespaces the values in it, whereas a manifest constant does not create a = new=20 type and its values (be it one or many) go directly in the namespace that i= t's=20 declared in. But even a named enum doesn't exactly create a new type. It's= =20 really its base type everywhere it's used, but extra restrictions are put o= n=20 it to help ensure that only valid values are used with it. For example, wit= h=20 std.datetime.Month, you either have to use Month.jan, Month.feb, etc. or ca= st=20 an integral value to a Month - e.g. cast(Month)12. However, it's still only= a=20 ubyte when it's compiled in, because Month is an enum of the type ubyte. So, manifest constants and named enums are not quite the same thing, but=20 they're not drastically differente either. Their values act essentially the= =20 same way. They're just referenced differently, and you can refer to the nam= e=20 of a named enum, using it as a type, whereas there is no name for a manifes= t=20 constant. Their values are pretty much identical when they're used though. =2D Jonathan M Davis
Apr 03 2011