Jump to page: 1 2
Thread overview
function pointer bug?
Oct 25, 2014
bitwise
Oct 27, 2014
Kagamin
Oct 27, 2014
Solomon E
Oct 27, 2014
bitwise
Oct 27, 2014
ketmar
Oct 27, 2014
bitwise
Oct 27, 2014
bitwise
Oct 28, 2014
ketmar
Oct 28, 2014
bitwise
Oct 28, 2014
bitwise
Oct 28, 2014
ketmar
Oct 28, 2014
Kapps
Oct 29, 2014
bitwise
Oct 29, 2014
ketmar
Oct 29, 2014
deadalnix
Oct 29, 2014
Kagamin
Oct 29, 2014
bitwise
Oct 28, 2014
Kapps
Oct 29, 2014
bitwise
October 25, 2014
I am trying to store a function pointer in a class in a generic way(works with all member/non-member/global functions).

In the main() function below, there are two cases(A, B).

If case B is commented out, this code complies/runs fine. Otherwise, I get these errors, and the compiler pointing at "dg.funcptr = &T[0];" in TestClas.invoke(void*).

Error: this for instanceMethod needs to be type TestClass not type main.FuncPtr!(instanceMethod).FuncPtr
Error: cannot implicitly convert expression (&(__error).instanceMethod) of type void delegate() to void function()
Error: template instance main.FuncPtr!(instanceMethod) error instantiating


Am I missing something here or is this a bug?

class TestClass {
    void instanceMethod() {
        writeln("Instance Method!");
    }

    static void staticMethod() {
        writeln("Static Method!");
    }
}

void GlobalMethod() {
    writeln("Global Method!");
}

void invokeFunction(T...)(void *instance){
    alias typeof(T[0])                       method_type;
    alias ReturnType!method_type             return_type;
    alias ParameterTypeTuple!method_type     param_types;
    alias return_type delegate(param_types)  delegate_type;

    delegate_type dg;
    dg.ptr = instance;
    dg.funcptr = &T[0];
    dg();
}

class FuncPtr(T...) {
    void invoke(void *instance) {
        alias typeof(T[0])                       method_type;
        alias ReturnType!method_type             return_type;
        alias ParameterTypeTuple!method_type     param_types;
        alias return_type delegate(param_types)  delegate_type;

        delegate_type dg;
        dg.ptr = instance;
        dg.funcptr = &T[0];
        dg();
    }
}

void main() {
    TestClass testClass = new TestClass();

    // case A
    invokeFunction!(TestClass.instanceMethod)(cast(void*)testClass);
    invokeFunction!(TestClass.staticMethod)(null);
    invokeFunction!(GlobalMethod)(null);

    // case B
    auto fp1 = new FuncPtr!(TestClass.instanceMethod);
    auto fp2 = new FuncPtr!(TestClass.staticMethod);
    auto fp3 = new FuncPtr!(GlobalMethod);

    fp1.invoke(cast(void*)testClass);
    fp2.invoke(null);
    fp3.invoke(null);
}
October 27, 2014
Looks like &T[0] tries to take delegate to instanceMethod, hence complains about this type.
October 27, 2014
It works after I added 'static' to the declaration of 'invoke()' (and import std.stdio, std.traits.)

I fiddled around with it for hours before I tried 'static' there, because I've only been studying D for a week, so only about half of this code and the error messages made any sense to me when I started on it.

Once it started passing all the tests, I still didn't quite get how it all worked, because of suspecting there was a typo in the code between writing TestClass and testClass. So I tested whether specific instances are called (instead of maybe just the first one constructed) by adding a static counter to TestClass, and storage of the counter by each instance. It looks all right.

October 27, 2014
On Monday, 27 October 2014 at 16:08:26 UTC, Solomon E wrote:
> It works after I added 'static' to the declaration of 'invoke()' (and import std.stdio, std.traits.)
>
> I fiddled around with it for hours before I tried 'static' there, because I've only been studying D for a week, so only about half of this code and the error messages made any sense to me when I started on it.
>
> Once it started passing all the tests, I still didn't quite get how it all worked, because of suspecting there was a typo in the code between writing TestClass and testClass. So I tested whether specific instances are called (instead of maybe just the first one constructed) by adding a static counter to TestClass, and storage of the counter by each instance. It looks all right.

I'm pretty sure this is a bug. I'm going to file a report. If one
of those template works, both should.

I have been trying to build a reflection system over the last few
weeks, and noticed different incarnations of this problem several
times:

<b>
&(__error).instanceMethod
</b>

In certain cases, the compiler seems to drop the enclosing type
of the method, but I'm not sure why.


This error seems like it may be related some how:

enum index = __traits(getVirtualIndex, TestClass.instanceMethod);
enum p = TestClass.classinfo.vtbl[index];

The above code will produce this error:
Error: typeid(main.TestClass).vtbl is not yet implemented at
compile time

but if this is the problem, shouldn't Both of the test cases fail?
October 27, 2014
On Mon, 27 Oct 2014 22:17:24 +0000
bitwise via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> I have been trying to build a reflection system over the last few weeks, and noticed different incarnations of this problem several times:
> 
> &(__error).instanceMethod
this means that something gone wrong in the process. '(__error)' is the "pseudotype" for failed CTFE/instantiation.

> This error seems like it may be related some how:
> 
> enum index = __traits(getVirtualIndex, TestClass.instanceMethod); enum p = TestClass.classinfo.vtbl[index];
> 
> The above code will produce this error:
> Error: typeid(main.TestClass).vtbl is not yet implemented at
> compile time
> 
> but if this is the problem, shouldn't Both of the test cases fail?
why? everything is ok here. you can get method index in compile-time, but there is no built VMT in compile time. what do you want to achieve with second enum?


October 27, 2014
> this means that something gone wrong in the process. '(__error)' is the
> "pseudotype" for failed CTFE/instantiation.

But why did the instantiation fail? and more importantly, why did it not have consistent behaviour between the two templates above?


>what do you want to achieve
>with second enum?

I'm just trying to come up with a test case which may offer some insight into what's going wrong with the code in the OP.

for example, I am wondering how far this issue extends; meaning, is it only classinfo.vtbl that's not available at compile time? or is it the address of member functions as a whole that are unavailable?

The code below suggests the latter, although it doesn't explicitly state it:

static addr = &TestClass.instanceMethod;
Error: non-constant expression & instanceMethod

I may be missing a subtle difference, but in C++, this code works:

class TestAddr {
    public: virtual void test() { cout << "test" << endl; }
};

int main(int argc, const char * argv[])
{
    TestAddr test;
    static auto ptr = &TestAddr::test;
    (test.*ptr)();
    return 0;
}

so why would it not in D?

October 27, 2014
>> quotes self

Here is a better example, showing that virtual function pointers are available at compile time in C++. Essentially, I would expect my D code to function similarly, but it won't compile.

class TestAddr {
    public: virtual void test() { cout << "test" << endl; }
};

template<void (TestAddr::*FN)()>
class PtrWrapper
{
public:
    void invoke(TestAddr *instance) { (instance->*FN)(); }
};

int main(int argc, const char * argv[])
{
    TestAddr test;
    PtrWrapper<&TestAddr::test> wrapper;
    wrapper.invoke(&test);
    return 0;
}
October 28, 2014
On Mon, 27 Oct 2014 23:52:38 +0000
bitwise via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

> The code below suggests the latter, although it doesn't explicitly state it:
> 
> static addr = &TestClass.instanceMethod;
> Error: non-constant expression & instanceMethod
> 
> I may be missing a subtle difference, but in C++, this code works:
> 
> class TestAddr {
>      public: virtual void test() { cout << "test" << endl; }
> };
> 
> int main(int argc, const char * argv[])
> {
>      TestAddr test;
>      static auto ptr = &TestAddr::test;
>      (test.*ptr)();
>      return 0;
> }
C++ compiler does some trickery behind the curtains. besides, you aren't supposed to make such hackish things easily in D. yet you can:

  class TestClass {
    int n = 42;
    void test() { writeln("test: ", n); }
  }

  void main () {
    auto test = new TestClass();
    void delegate () a;
    {
      a.ptr = *cast(void**)&test; // protection from opCast()
        // this can be done as `cast(void*)test;` too
      enum idx = __traits(getVirtualIndex, TestClass.test);
      a.funcptr = cast(void function())TestClass.classinfo.vtbl[idx];
      a(); // outputs 'test: 42'
    }
  }


you need to manually create initialization code that C++ compilers creates behind the curtains. if you aren't in urgent need of that code, may i suggest you to read D books and D specs? D is not C++, and D delegates aren't C++ member function pointers (yet they works nearly the same).

this is kind of advanced topic, and i don't think that dumping working source code at you will help without you grasp the low-level mechanics first.

besides, you can use CTFE to build wrapper code. Adam Ruppe has that in his jsvar.d, and i have that in my cong.d (cmdcon-ng) too.

not that i'm not willing to help you, but i can't see what you understand and what not, so i don't know where i should start explaining.


October 28, 2014
> C++ compiler does some trickery behind the curtains. besides, you
> aren't supposed to make such hackish things easily in D. yet you can:

There is nothing hackish in the above code. It's a non-type template parameter and a member function pointer. If I was trying to access the (implementation dependant)vtable pointer in C++ to call the function manually, I may concede to calling it a hack, but there is nothing non-standard about the above code.

>>D is not C++, and D delegates aren't C++ member function pointers (yet they works nearly the same).

I was simply trying to show that what I wanted to do was possible in a similar language.

I still believe it's a bug in the language. In the original code, both test cases should either both work, or both fail to compile.

I have looked through the D docs online, and can't find anything supporting the argument that this is the intended behaviour of the compiler.
October 28, 2014
>> besides, you can use CTFE to build wrapper code. Adam Ruppe has that in
>> his jsvar.d, and i have that in my cong.d (cmdcon-ng) too.

I have actually found a work around as well, which was to wrap the actual retrieval of the function address in a lambda, and pass the lambda by template parameter instead:

auto getMethod = function() {
    return &__traits(getMember, SCOPE, member);
};

this is later assigned to a delegate when called:

delegate_type dg;
dg.ptr = instance;
dg.funcptr = getMethod();
dg();

The above works for global or static functions too. This seems to me like an unnecessary workaround though..

my current project can be viewed here:
https://github.com/bitwise-github/D-Reflection/blob/master/reflection.d#L796

It's been implemented based on Andrei's suggestions here:
http://forum.dlang.org/thread/juf7sk$16rl$1@digitalmars.com

as far as I could google, no one has attempted this yet. My prototype works pretty much as he has described, less a feature here and there.
« First   ‹ Prev
1 2