Thread overview
Delegates and classes for custom code.
Apr 17, 2018
Chris Katko
Apr 17, 2018
Chris Katko
Apr 17, 2018
Chris Katko
Apr 17, 2018
Nathan S.
Apr 17, 2018
bauss
Apr 17, 2018
Simen Kjærås
Apr 18, 2018
Chris Katko
Apr 18, 2018
arturg
Apr 18, 2018
arturg
Apr 18, 2018
Simen Kjærås
April 17, 2018
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!
April 17, 2018
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?
April 17, 2018
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.

April 17, 2018
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?
April 17, 2018
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.
April 17, 2018
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
April 18, 2018
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.
April 18, 2018
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; };
April 18, 2018
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()
    { ... }
}
April 18, 2018
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