Thread overview
How would you create this construct?
Mar 30, 2018
Chris Katko
Mar 30, 2018
Jonathan M Davis
Mar 30, 2018
Mike Parker
Mar 31, 2018
Chris Katko
March 30, 2018
void start_draw_calls(BITMAP target_bitmap); //locks onto a resource
void end_draw_calls(); //frees previous resource lock

void my_function()
    {
    //...

    start_draw_calls(target_bitmap) //whether this is a function, or class, lambda, or a "using"?
        {
        draw_call1();
        draw_call2();
        draw_call3();
        } // end_draw_calls() is automatically called because we're hitting the closing curly.

    //...
    }


The key here is, I've got a resource (setting a target bitmap) whose handling functions have to occur before, and after a series of user calls. (at which point target bitmap is restored to what it was before). So it's kind of like RAII (or maybe exactly like).

What I'm trying to do is through this experimental API, is both eliminate the user needing to call a clean-up function explicitly, and, make the "right way" to use the API basically... the only way... to use it.

The way I have written above, there is no way for you to leave my_function() without it automatically calling the cleaning up call. Even if you did a nested version, it would still work!

At first glance, I could do:

    start_draw_calls( {lambda containing all my code} )

But that's not quite as pretty and you're forcing all code to be inside a lambda which... I'm not sure if that has hidden implications / gotchas for code.

Thanks!
March 29, 2018
On Friday, March 30, 2018 02:30:01 Chris Katko via Digitalmars-d-learn wrote:
> void start_draw_calls(BITMAP target_bitmap); //locks onto a
> resource
> void end_draw_calls(); //frees previous resource lock
>
> void my_function()
>      {
>      //...
>
>      start_draw_calls(target_bitmap) //whether this is a function,
> or class, lambda, or a "using"?
>          {
>          draw_call1();
>          draw_call2();
>          draw_call3();
>          } // end_draw_calls() is automatically called because
> we're hitting the closing curly.
>
>      //...
>      }
>
>
> The key here is, I've got a resource (setting a target bitmap)
> whose handling functions have to occur before, and after a series
> of user calls. (at which point target bitmap is restored to what
> it was before). So it's kind of like RAII (or maybe exactly like).
>
> What I'm trying to do is through this experimental API, is both eliminate the user needing to call a clean-up function explicitly, and, make the "right way" to use the API basically... the only way... to use it.
>
> The way I have written above, there is no way for you to leave my_function() without it automatically calling the cleaning up call. Even if you did a nested version, it would still work!
>
> At first glance, I could do:
>
>      start_draw_calls( {lambda containing all my code} )
>
> But that's not quite as pretty and you're forcing all code to be inside a lambda which... I'm not sure if that has hidden implications / gotchas for code.

If you want to force it, then just use RAII. Put @disable this(); in the struct so that default initialization is disabled for the struct. Put @disable this(this); in the struct so that it can't be copied (so that you don't have to mess with something like reference counting). Then given the struct a constructor that takes the resource to be locked, and make the destructor unlock the resource. e.g. something like

struct S
{
public:

    @disable this();
    @disable this(this);

    this(BITMAMP bitmap)
    {
        _bitmap = bitmap;
        lock(_bitmap);
    }

    ~this()
    {
        unlock(_bitmap);
    }

private:

    BITMAP bitmap;
}

Now, you could just as easily do

    lock(bitmap);
    scope(exit) unlock(bitmap);

and get the same semantics, but that does require that the user explicitly call the lock and unlock functions.

- Jonathan M Davis

March 30, 2018
On Friday, 30 March 2018 at 02:30:01 UTC, Chris Katko wrote:
>
> What I'm trying to do is through this experimental API, is both eliminate the user needing to call a clean-up function explicitly, and, make the "right way" to use the API basically... the only way... to use it.
>
> The way I have written above, there is no way for you to leave my_function() without it automatically calling the cleaning up call. Even if you did a nested version, it would still work!
>
> At first glance, I could do:
>
>     start_draw_calls( {lambda containing all my code} )
>
> But that's not quite as pretty and you're forcing all code to be inside a lambda which... I'm not sure if that has hidden implications / gotchas for code.

Something like this?

=============
import std.stdio;

auto startFoo(int x) {
    struct DO {
        int n;
        this(int n) {
            this.n = n;
        }
        ~this() {
            import std.stdio; writeln("Finished: ", n);
        }
    }
    return DO(x);
}

void main() {
    with(startFoo(10)) {
        writeln("Doing 1");
        writeln("Doing 2");
    }
    writeln("That's all folks");
}
==============

The with statement isn't necessary of course, but I think it's a clean way to narrow the scope.

And I'd consider not using "start" in the name if you go this route, as it doesn't indicate that there's a return value and it would be easy to forget when not using the with statement. Perhaps something like "getDrawStarter".
March 31, 2018
On Friday, 30 March 2018 at 03:14:42 UTC, Mike Parker wrote:
> On Friday, 30 March 2018 at 02:30:01 UTC, Chris Katko wrote:
>> [...]
>
> Something like this?
>
> =============
> import std.stdio;
>
> [...]

This is beautiful. I mean, the struct stuff looks complicated/non-intuitive at first, but it's all boilerplate.