www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Delegates and classes for custom code.

reply Chris Katko <ckatko gmail.com> writes:
What I want:

class viewport_t
   {
   int x,y,w,h;
   }

class dialog_t
   {
   int x,y;

   this( int x, int y, delegate void (viewport_t) on_draw )
     {
     this.x = x;
     this.y = y;
     this.execute = execute;
     }

   void draw_text(string text)
     {
     }

   delegate void (viewport_t) on_draw;
   }

void function()
   {
   viewport_t v;
   dialog_t (15, 15,
         delegate void (viewport_t)
           {
           draw_text("hello world"); //calls dialog_t function
           }
         )
   }

Is this possible? Pass to a class, the code to run. But the code 
has to somehow know about the class methods.

I don't think you can pass "dialog_t.this" as it's being 
constructed!
Apr 16 2018
next sibling parent reply Chris Katko <ckatko gmail.com> writes:
Some typos in there.

execute == on_draw.

Basically, I'm just sending a delegate/lambda "custom function" 
at initialization time. But I'd like that delegate to somehow 
access the holding classes functions. Or figure out how to do 
that.

Maybe the class somehow sends the delegate a this parameter when 
the class goes to execute it?
Apr 16 2018
parent reply Chris Katko <ckatko gmail.com> writes:
I'm having trouble conceptualizing this issue at the moment. But 
it seems if I pass to the delegate my object, then I can ONLY use 
one class type.

Say, the delegate takes a "this" from... some class that wants to 
have a dialog. A window. Now the delegate NEEDS a this from a 
window, and only a window. So if I have a delegate in... 
something else, I'll either need to figure out some way for 
templates to work with that... or some kind of wrapping class / 
interface.

But all I want is a kind of Javascript'y:

Every time this object gets .onDraw() called. It does whatever I 
told it to do.

auto t = new object1( onDraw(){ do stuff} );
auto t2 = new object2( onDraw() { do other stuff} );

I'm trying to do the same kind of API for button handling where 
it automatically links into event queues (which works) and the 
button does whatever I want it to. But, it can only access PUBLIC 
INTERFACES (globals) because it can't access the underlying 
object and globals are the only ones it can statically figure out 
at compile-time.
Apr 16 2018
parent Nathan S. <no.public.email example.com> writes:
On Tuesday, 17 April 2018 at 04:09:57 UTC, Chris Katko wrote:
 I'm having trouble conceptualizing this issue at the moment. 
 But it seems if I pass to the delegate my object, then I can 
 ONLY use one class type.
Can you post the code you're trying to run?
Apr 16 2018
prev sibling next sibling parent bauss <jj_1337 live.dk> writes:
On Tuesday, 17 April 2018 at 03:55:55 UTC, Chris Katko wrote:
 What I want:

 class viewport_t
   {
   int x,y,w,h;
   }

 class dialog_t
   {
   int x,y;

   this( int x, int y, delegate void (viewport_t) on_draw )
     {
     this.x = x;
     this.y = y;
     this.execute = execute;
     }

   void draw_text(string text)
     {
     }

   delegate void (viewport_t) on_draw;
   }

 void function()
   {
   viewport_t v;
   dialog_t (15, 15,
         delegate void (viewport_t)
           {
           draw_text("hello world"); //calls dialog_t function
           }
         )
   }

 Is this possible? Pass to a class, the code to run. But the 
 code has to somehow know about the class methods.

 I don't think you can pass "dialog_t.this" as it's being 
 constructed!
First of all your code is not valid D code. To construct a delegate you use the following syntax: RETURN_TYPE delegate(PARAMS); Ex. in your case: void delegate(viewport_t); Secondly, classes are reference types and thus you cannot construct just like: viewport_t v; It must be assigned using "new". Ex. auto v = new viewport_t; The same goes for your dialog_t instance. What you can do to avoid double allocation of dialog_t is to initially set it to void and thus you can point to the initial variable within your delegate that is constructed when dialog_t is actually constructed. Ex. dialog_t d = void; d = new dialog_t (15, 15, delegate void (viewport_t) { d.draw_text("hello world"); //calls dialog_t function } ); I could go into more details, but that would be completely unrelated to your issue at hand.
Apr 17 2018
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 17 April 2018 at 03:55:55 UTC, Chris Katko wrote:
 What I want:

 class viewport_t
   {
   int x,y,w,h;
   }

 class dialog_t
   {
   int x,y;

   this( int x, int y, delegate void (viewport_t) on_draw )
     {
     this.x = x;
     this.y = y;
     this.execute = execute;
     }

   void draw_text(string text)
     {
     }

   delegate void (viewport_t) on_draw;
   }

 void function()
   {
   viewport_t v;
   dialog_t (15, 15,
         delegate void (viewport_t)
           {
           draw_text("hello world"); //calls dialog_t function
           }
         )
   }

 Is this possible? Pass to a class, the code to run. But the 
 code has to somehow know about the class methods.

 I don't think you can pass "dialog_t.this" as it's being 
 constructed!
Depending on exactly what you want to do, this may be impossible, or bauss' code might fit the bill, or you could use anonymous classes: class viewport_t { int x,y,w,h; } class dialog_t { int x,y; this( int x, int y) { this.x = x; this.y = y; } void draw_text(string text) { } abstract void on_draw(viewport_t); } void fn() { viewport_t v; auto d = new class dialog_t { this() { super(15,15); } override void on_draw(viewport_t) { draw_text("hello world"); } }; } -- Simen
Apr 17 2018
parent reply Chris Katko <ckatko gmail.com> writes:
That was all pseudo-code typed by hand.

I got my code to work today. I don't know if it's the prettiest 
it can be, but it works:

// TESTING ACCESS TO the OWNING function
//-------------------------------------------
class test_window
	{
	float x;
	float y;
	
	void draw_text(string text)
		{
		writeln(text);		
		}

	this( void function(test_window) onDraw  )
		{	
		this.onDraw = onDraw;
		}

	void run() //called every frame
		{
		onDraw(this);
		}

	void function (test_window) onDraw;
	}


void test_dialog()
	{
	auto t = new test_window(function void(test_window ptr)
		{
		with(ptr)
			{
			draw_text( format("Hello, world. [x,y]=[%f,%f]", x, y));
			}
		});
		
	t.run();
	}





And a second attempt/version:





// TESTING ACCESS to anything
// ----------------------------------------------------------

struct data_to_access_t
	{
	int tacos;
	}

struct data_to_access2_t
	{
	struct beans
		{
		int x;
		};
		
	beans b;
	}

class abc(T)
	{
	int x;
	void delegate(T) run_delegate;
		
	T data;
		
	this(T t, void delegate(T) d)
		{
		data = t;
		run_delegate = d;
		}
	
	void execute()
		{
		run_delegate(data);
		}
	}

void test_dialog_anything()
	{	
	data_to_access_t  d;
	data_to_access2_t d2;
	d.tacos = 4;
	d2.b.x  = 5;

	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
%d", d.tacos)  );
	auto y = new abc!data_to_access_t ( d, (d){writefln("test  %d", 
d.tacos);}  );
	auto z = new abc!data_to_access2_t(d2, delegate void 
(d2){writefln("test2 %d", d2.b.x);}  );
	
	x.execute();
	y.execute();
	z.execute();
	}





My only questions are:

  -  is there any way to make it "smart" enough to take the type 
of the argument, instead of me manually giving it a type.

	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
%d", d.tacos)  );
becomes
	auto x = new abc( d, (d) => writefln("test  %d", d.tacos)  );

  - Is there any way to eliminate the first d? Which is 
essentially a "this" pointer.

	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
%d", d.tacos)  );
becomes
	auto x = new abc!data_to_access_t ( (d) => writefln("test  %d", 
d.tacos)  );

  - And preferably, if possible, both. But I'll take what I can 
get.
Apr 17 2018
next sibling parent reply arturg <var.spool.mail700 gmail.com> writes:
On Wednesday, 18 April 2018 at 01:12:33 UTC, Chris Katko wrote:
 That was all pseudo-code typed by hand.

 I got my code to work today. I don't know if it's the prettiest 
 it can be, but it works:

 // TESTING ACCESS TO the OWNING function
 //-------------------------------------------
 class test_window
 	{
 	float x;
 	float y;
 	
 	void draw_text(string text)
 		{
 		writeln(text);		
 		}

 	this( void function(test_window) onDraw  )
 		{	
 		this.onDraw = onDraw;
 		}

 	void run() //called every frame
 		{
 		onDraw(this);
 		}

 	void function (test_window) onDraw;
 	}


 void test_dialog()
 	{
 	auto t = new test_window(function void(test_window ptr)
 		{
 		with(ptr)
 			{
 			draw_text( format("Hello, world. [x,y]=[%f,%f]", x, y));
 			}
 		});
 		
 	t.run();
 	}





 And a second attempt/version:





 // TESTING ACCESS to anything
 // ----------------------------------------------------------

 struct data_to_access_t
 	{
 	int tacos;
 	}

 struct data_to_access2_t
 	{
 	struct beans
 		{
 		int x;
 		};
 		
 	beans b;
 	}

 class abc(T)
 	{
 	int x;
 	void delegate(T) run_delegate;
 		
 	T data;
 		
 	this(T t, void delegate(T) d)
 		{
 		data = t;
 		run_delegate = d;
 		}
 	
 	void execute()
 		{
 		run_delegate(data);
 		}
 	}

 void test_dialog_anything()
 	{	
 	data_to_access_t  d;
 	data_to_access2_t d2;
 	d.tacos = 4;
 	d2.b.x  = 5;

 	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
 %d", d.tacos)  );
 	auto y = new abc!data_to_access_t ( d, (d){writefln("test  
 %d", d.tacos);}  );
 	auto z = new abc!data_to_access2_t(d2, delegate void 
 (d2){writefln("test2 %d", d2.b.x);}  );
 	
 	x.execute();
 	y.execute();
 	z.execute();
 	}





 My only questions are:

  -  is there any way to make it "smart" enough to take the type 
 of the argument, instead of me manually giving it a type.

 	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
 %d", d.tacos)  );
 becomes
 	auto x = new abc( d, (d) => writefln("test  %d", d.tacos)  );

  - Is there any way to eliminate the first d? Which is 
 essentially a "this" pointer.

 	auto x = new abc!data_to_access_t ( d, (d) => writefln("test  
 %d", d.tacos)  );
 becomes
 	auto x = new abc!data_to_access_t ( (d) => writefln("test  
 %d", d.tacos)  );

  - And preferably, if possible, both. But I'll take what I can 
 get.
is it this what you want? class A { int a; void delegate() onDraw; this(void delegate() dg) { onDraw = dg; } void drawText(string s) { s.writeln; } } void main() { A a; a = new A((){ a.a = 5; a.drawText("test"); "drawing all".writeln; }); } but if you do A a = new A((){ a.a = 5; ...}); the dg cant capture 'a' yet. so maybe it would be better to just do: A a = new A; a.onDraw = (){ a.drawText("test"); "draw rest".writeln; };
Apr 17 2018
parent arturg <var.spool.mail700 gmail.com> writes:
On Wednesday, 18 April 2018 at 01:58:40 UTC, arturg wrote:
 is it this what you want?

    class A
    {
        int a;
        void delegate() onDraw;

        this(void delegate() dg)
        {
            onDraw = dg;
        }

        void drawText(string s)
        {
            s.writeln;
        }
    }

    void main()
    {
         A a;
         a = new A((){ a.a = 5; a.drawText("test"); "drawing 
 all".writeln; });
     }

 but if you do A a = new A((){ a.a = 5; ...});
 the dg cant capture 'a' yet.
 so maybe it would be better to just do:
 A a = new A;
 a.onDraw = (){ a.drawText("test"); "draw rest".writeln; };
ah i see bauss already wrote the same. some other aproach could be: class Base { final void draw() { drawSelf(); } abstract void drawSelf(); } class A : Base { override void drawSelf() { ... } }
Apr 17 2018
prev sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 18 April 2018 at 01:12:33 UTC, Chris Katko wrote:
 My only questions are:
[snip] How's about this one: import std.stdio : writefln; struct data_to_access_t { int tacos; } class Abc(T, alias Fn) { T data; this(T data) { this.data = data; } void execute() { Fn(data); } } Abc!(T, Fn) abc(alias Fn, T)(T data) { return new Abc!(T, Fn)(data); } unittest { data_to_access_t d; auto a = abc!((f) => writefln("test %d", f.tacos))(d); a.execute(); } One problem with this approach is that two seemingly identical instantiations are different types. This may or may not be a problem in your specific case. If it is: import std.stdio : writefln; import std.functional : toDelegate; struct data_to_access_t { int tacos; } class Abc(T) { T data; void delegate(T) Fn; this(T data, void delegate(T) Fn) { this.data = data; this.Fn = Fn; } void execute() { Fn(data); } } Abc!(T) abc(alias Fn, T)(T data) { static if (__traits(isTemplate, Fn)) return new Abc!(T)(data, Fn!T); else return new Abc!(T)(data, Fn.toDelegate); } unittest { data_to_access_t d; auto a = abc!((f) => writefln("test %d", f.tacos))(d); auto b = abc!((data_to_access_t f) => writefln("test %d", f.tacos))(d); a.execute(); b.execute(); } -- Simen
Apr 17 2018