Search code examples
parametersdelegatesargumentsparameter-passingd

Dlang / Passing arguments to delegates


I wish to be able to store a function for execution at a later time with a variable number of arguments. I have done this by wrapping the delegate within a class. When passing in the arguments to execute the delegate, the arguments are never received. What is the nature of this behavior and how can I have the delegate receive the arguments to execute with them?


import std.variant;
import std.stdio;

class Callback
{
    bool delegate(...) callback;
    Variant[] params;

    this(T)(T t)
    {
        this.callback = cast(bool delegate(...)) t;
    }
    
    this(T, A...)(T t, A a)
    {
        this.callback = cast(bool delegate(...)) t;
        
        foreach(item;a)
        {            
            Variant temp = item;
            this.params ~= temp;
        }
    }
    
    void exec()
    {
        if(this.params.length)
        {
            writeln("Passed args: ");
            writeln(this.params);
            callback(this.params);
        }
        else
        {
            callback();
        }
        
    }
}

void main()
{
    bool test()
    {
        writeln("Executing first test");
        return false;
    }
    
    bool test2(Variant[] params)
    {
        writeln("Received args: ");
        writeln(params);
        return false;
    }
    
    Callback cb = new Callback(&test);    
    cb.exec();
    
    writeln();
    
    Callback cb2 = new Callback(&test2, "test2 executing", 1);
    cb2.exec();
}

C:\Users\Nulrie\Documents\D>test
Executing first test

Passed args:
[test2 executing, 1]
Received args:
[]

C:\Users\Nulrie\Documents\D>

Solution

  • Your cast in the constructor is invalid. You cannot cast any function to a (...) because under the hood the way arguments are passed is very different. On Linux, your code actually segfault.

    If you wish to store delegates, and you're happy with using the GC, just use delegates:

    import std.variant;
    import std.stdio;
    
    void main()
    {
        bool test()
        {
            writeln("Executing first test");
            return false;
        }
        
        bool test2(Variant[] params)
        {
            writeln("Received args: ");
            writeln(params);
            return false;
        }
        
        auto cb = () => test();
        cb();
        
        writeln();
        
        auto cb2 = () => test2([Variant("test2 executing"), Variant(1)]);
        cb2();
    }
    

    There's a few variations you have have, e.g. (...) { /* something */ } will allow you to accept arbitrary arguments, but you'll need to wrap them in Variant anyway.