www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - hijacking a class's members

reply Rory Mcguire <rjmcguire gm_no_ail.com> writes:
Hi,

The code below is my beginning to attempt a class which implements any class
and throws an exception if one tries to access any member of that class.

Problem is that if I use:
auto a1 = noinit!(A)();

it works and accesses the int x() {...} member of the generated class, but 
if I use:
A a1 = noinit!(A)();

it accesses A.x instead of the generated classes x.

So am I wrong in making a sub class have a member function which hides a 
parent class's member variable or is the compiler wrong and it should 
generate a call to generated sub class?


Thanks!!!
-Rory











================================================
import std.algorithm;
import std.contracts;
import std.traits;

class A {
	int x;
}

string thrower(T)(string name) {
	static if (isNumeric!T) {
		return " property ref "~ T.stringof ~" "~ name ~"() { 
throw new Exception(\"Uninitialized access!\"); }"
				" property ref "~ T.stringof ~" 
"~ name ~"(int ignored) { throw new Exception(\"Uninitialized access\"); }";
	} else {
		return "error";
	}
}
string build(alias T, alias generator)(string myname) {
	string s = "auto "~ myname ~" = new class "~T.stringof~" { 
invariant() { throw new Exception(\"inv\");}\n";
	foreach (i,t; typeof(T.tupleof)) {
		string name = find(T.tupleof[i].stringof, '.');
		enforce(name.length >= 2);
		name = name[1..$];
		//pragma(msg, t," ", A.tupleof[i]);
		s ~= "\t"~generator!t(name) ~"\n";
	}
	return s~ "};";
}

auto noinit(alias T)() {
	mixin(build!(T,thrower)("tmp"));
	return tmp;
}


void main() {
	A a = noinit!(A)();
	auto a1 = noinit!(A)();
//	pragma(msg, build!(A,thrower)("Athrower"));
	
	int i = a.x = 3; // uses A.x not the generated ??.x
	int j = a1.x = 3; // uses ??.x
	assert(a.x == 3);
}
Aug 04 2010
next sibling parent reply Mafi <mafi example.org> writes:
Am 04.08.2010 12:11, schrieb Rory Mcguire:
 Hi,

 The code below is my beginning to attempt a class which implements any class
 and throws an exception if one tries to access any member of that class.

 Problem is that if I use:
 auto a1 = noinit!(A)();

 it works and accesses the int x() {...} member of the generated class, but
 if I use:
 A a1 = noinit!(A)();

 it accesses A.x instead of the generated classes x.

 So am I wrong in making a sub class have a member function which hides a
 parent class's member variable or is the compiler wrong and it should
 generate a call to generated sub class?


 Thanks!!!
 -Rory

if x is a field (ie a member variable) it's statically bound. In your example it is a field so it gets A.x of your subclass which is still there becuase of the methoda of A which could use A.x. Fields have to be statically bound because there's no covariance guarateed with them. Use getters and setters instead. BTW are propertys statically or dynamically bound. They're kind of both: fields and methods. Mafi
Aug 04 2010
parent reply Rory Mcguire <rjmcguire gm_no_ail.com> writes:
Mafi wrote:

 Am 04.08.2010 12:11, schrieb Rory Mcguire:
 Hi,

 The code below is my beginning to attempt a class which implements any
 class and throws an exception if one tries to access any member of that
 class.

 Problem is that if I use:
 auto a1 = noinit!(A)();

 it works and accesses the int x() {...} member of the generated class,
 but if I use:
 A a1 = noinit!(A)();

 it accesses A.x instead of the generated classes x.

 So am I wrong in making a sub class have a member function which hides a
 parent class's member variable or is the compiler wrong and it should
 generate a call to generated sub class?


 Thanks!!!
 -Rory

if x is a field (ie a member variable) it's statically bound. In your example it is a field so it gets A.x of your subclass which is still there becuase of the methoda of A which could use A.x. Fields have to be statically bound because there's no covariance guarateed with them. Use getters and setters instead. BTW are propertys statically or dynamically bound. They're kind of both: fields and methods. Mafi

Thats what feels weird to me. a.x can result in different things happening even though x exists in both A and the generated class. However the generated class has two "fields" called x one you can't access anymore and the property one. When I create an instance of the generated class I would expect it to always to the same thing if I use one of its methods/properties/etc... Kind of like you would expect the following to print out "hello 2": class A { string toString() { return "hello"; } } class B : A { string toString() { return super.toString() ~ " 2"; } } void main() { A a = new B(); writeln(a.toString()); // uses B.toString() } What I've got is: class A { int x; } class B : A { int x() { throw new Exception(); } } void main() { A a = new B(); writeln(a.x); // this accesses A.x not B.x!!!! } just seems like properties are not quite right or something. -Rory
Aug 04 2010
parent reply Mafi <mafi example.org> writes:
 Thats what feels weird to me. a.x can result in different things happening
 even though x exists in both A and the generated class. However the
 generated class has two "fields" called x one you can't access anymore and
 the  property one.
 When I create an instance of the generated class I would expect it to always
 to the same thing if I use one of its methods/properties/etc...
 Kind of like you would expect the following to print out "hello 2":
 class A {
      string toString() { return "hello"; }
 }
 class B : A {
      string toString() { return super.toString() ~ " 2"; }
 }
 void main() {
      A a = new B();
      writeln(a.toString()); // uses B.toString()
 }

 What I've got is:
 class A {
      int x;
 }
 class B : A {
      int x() { throw new Exception(); }
 }
 void main() {
      A a = new B();
      writeln(a.x); // this accesses A.x not B.x!!!!
 }

 just seems like properties are not quite right or something.

 -Rory

If you want that to work, both x have to be virtual (ie dynamically bound). In D all non-final non-ststic clsss-methods are virtual(1). Consider the following: /////// class A { int x = 0; int getX() { //<-- it's virtual return x; } } class B : A{ int x = 5; override int getX() { //<-- it's virtual too return x; } } void thinAboutA(A a) { /* Now a call to a non virtual method * which results in vtbl lookup. a's vtbl * contains B.getX(). writeln(a.getX()); /* Non virtual field. * Has to be statically bound * to a lookup in A.x */ writeln(a.x); } void main() { thinkAboutA(new B); } //////// Fields have to be statically bound bcause the compiler doesn't enforce covariance with fields. So you can: /////////////// class C : A { string x = ""; } void main() { thinkAboutA(new C); //Should evrything crash now? I won't. } ////////////// 1) I'm not sure about propertys.
Aug 04 2010
parent Rory Mcguire <rjmcguire gm_no_ail.com> writes:
Mafi wrote:

 Thats what feels weird to me. a.x can result in different things
 happening even though x exists in both A and the generated class. However
 the generated class has two "fields" called x one you can't access
 anymore and the  property one.
 When I create an instance of the generated class I would expect it to
 always to the same thing if I use one of its methods/properties/etc...
 Kind of like you would expect the following to print out "hello 2":
 class A {
      string toString() { return "hello"; }
 }
 class B : A {
      string toString() { return super.toString() ~ " 2"; }
 }
 void main() {
      A a = new B();
      writeln(a.toString()); // uses B.toString()
 }

 What I've got is:
 class A {
      int x;
 }
 class B : A {
      int x() { throw new Exception(); }
 }
 void main() {
      A a = new B();
      writeln(a.x); // this accesses A.x not B.x!!!!
 }

 just seems like properties are not quite right or something.

 -Rory

If you want that to work, both x have to be virtual (ie dynamically bound). In D all non-final non-ststic clsss-methods are virtual(1). Consider the following: /////// class A { int x = 0; int getX() { //<-- it's virtual return x; } } class B : A{ int x = 5; override int getX() { //<-- it's virtual too return x; } } void thinAboutA(A a) { /* Now a call to a non virtual method * which results in vtbl lookup. a's vtbl * contains B.getX(). writeln(a.getX()); /* Non virtual field. * Has to be statically bound * to a lookup in A.x */ writeln(a.x); } void main() { thinkAboutA(new B); } //////// Fields have to be statically bound bcause the compiler doesn't enforce covariance with fields. So you can: /////////////// class C : A { string x = ""; } void main() { thinkAboutA(new C); //Should evrything crash now? I won't. } ////////////// 1) I'm not sure about propertys.

cool thanks good example
Aug 04 2010
prev sibling parent Stanislav Blinov <blinov loniir.ru> writes:
  04.08.2010 14:11, Rory Mcguire wrote:
 Hi,

 The code below is my beginning to attempt a class which implements any class
 and throws an exception if one tries to access any member of that class.

 Problem is that if I use:
 auto a1 = noinit!(A)();

 it works and accesses the int x() {...} member of the generated class, but
 if I use:
 A a1 = noinit!(A)();

 it accesses A.x instead of the generated classes x.

 So am I wrong in making a sub class have a member function which hides a
 parent class's member variable or is the compiler wrong and it should
 generate a call to generated sub class?


 Thanks!!!
 -Rory











 ================================================
 import std.algorithm;
 import std.contracts;
 import std.traits;

 class A {
 	int x;
 }

 string thrower(T)(string name) {
 	static if (isNumeric!T) {
 		return " property ref "~ T.stringof ~" "~ name ~"() {
 throw new Exception(\"Uninitialized access!\"); }"
 				" property ref "~ T.stringof ~"
 "~ name ~"(int ignored) { throw new Exception(\"Uninitialized access\"); }";
 	} else {
 		return "error";
 	}
 }
 string build(alias T, alias generator)(string myname) {
 	string s = "auto "~ myname ~" = new class "~T.stringof~" {
 invariant() { throw new Exception(\"inv\");}\n";
 	foreach (i,t; typeof(T.tupleof)) {
 		string name = find(T.tupleof[i].stringof, '.');
 		enforce(name.length>= 2);
 		name = name[1..$];
 		//pragma(msg, t," ", A.tupleof[i]);
 		s ~= "\t"~generator!t(name) ~"\n";
 	}
 	return s~ "};";
 }

 auto noinit(alias T)() {
 	mixin(build!(T,thrower)("tmp"));
 	return tmp;
 }


 void main() {
 	A a = noinit!(A)();
 	auto a1 = noinit!(A)();
 //	pragma(msg, build!(A,thrower)("Athrower"));
 	
 	int i = a.x = 3; // uses A.x not the generated ??.x
 	int j = a1.x = 3; // uses ??.x
 	assert(a.x == 3);
 }

 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Filtered-With-Copfilter: Version 0.84beta4 (ProxSMTP 1.8)
 Copfilter-Filtered-With: SpamAssassin 3.2.5
 Copfilter-Virus-Scanned: ClamAV 0.94.2
 by Markus Madlener   http://www.copfilter.org

You're working with 'a' through 'class A' public interface, because you plainly declared 'a' to be of 'class A'. A.x is not a function, but a member variable, so the compiler fairly accesses it as it should. Regards, Stanislav Blinov
Aug 04 2010