www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - struct to/from void, object to/from void

reply Jason King <jhking airmail.net> writes:
I'm another of what seem to be legions of people trying to interface 
with OS stores that keep void * in c/c++.  My particular one is Windows 
TLSData, but for my example I don't need any Windows code, just D.

// this based on code I snagged from this group
// DMD 2.059 Win 7 Home Premium 64 bit.
import std.stdio: writeln;

struct MyStruct {
     string structName;

     public string toString() {
         return structName;
     }

     void* opCast() {
         return &this;
     }
}

class MyObject {
   string oname;

   this(string who) {
      oname = who;
   }

   public string toString() {
    return oname;
   }

   void* opCast() {
     return &this;
   }
}

void main() {
     MyStruct test = MyStruct("Example struct");
     MyStruct test2;
     void* temp1 = cast(void*)test;
     void* temp2 = &test;
     assert(temp1 == temp2);
     writeln(*cast(MyStruct*)temp1);
     test2 = *cast(MyStruct*)temp1;
     writeln(test2);

     auto o1 = new MyObject("Ok");
     MyObject o2;
     temp1 = cast(void*)o1;
     temp2 = &o1;
     //assert(temp1 == temp2);   // this is true for struct,
                                 // why not object?
     writeln(*cast(MyObject*)temp1);
     o2 = *cast(MyObject*)temp1;
     writeln(o2);
}

Structs seem to go both directions just fine, although something 
prettier than *cast(MyStruct*) would be nice for the return trip.

Objects seem to require a dedicated opCast and from my ng spelunking it 
appears opCast is frowned upon.

How would I do the to void* w/o opCast and is there a prettier way than
*cast(MyThing*) to get back the original "thing" from a void *?

If I get prettier code I'll  post it to the FAQ on the Wiki so that you 
gurus can quit dealing with the question so often.
Apr 29 2012
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Sunday, 29 April 2012 at 22:35:19 UTC, Jason King wrote:
 I'm another of what seem to be legions of people trying to 
 interface with OS stores that keep void * in c/c++.  My 
 particular one is Windows TLSData, but for my example I don't 
 need any Windows code, just D.
     void* opCast() {
         return &this;
     }
Honestly, in my opinion, drop this. Don't make it more 'pointer-like'. Unless there's a good reason, or it makes sense (Mathematical), then don't. You keep having half the tests using regular &(address) operator; what's wrong with that? Structs having no inheritance or virtual functions hold no extra information, which is one reason you can void cast them from one type or from void to their type without any issues. Casting to an object though I am not sure, but I think it won't let you unless it is able to. Besides once you have the pointer you don't really need to deference unless you want to copy the struct. I'm not sure how safe casting an object to void* would be, or back. If you can't avoid it, I would say make overloaded methods that do the intermediate work and make it transparent for you afterwards.
Apr 29 2012
parent reply "Jason King" <jhking airmail.net> writes:
Thanks for the rapid reply.

    void* opCast() {
        return &this;
    }
Honestly, in my opinion, drop this. Don't make it more 'pointer-like'. Unless there's a good reason, or it makes sense (Mathematical), then don't. You keep having half the tests using regular &(address) operator; what's wrong with that?
Since &thing works, I'm fine with it instead of opCast. The opCast was in the code I borrowed so I wanted to show both approaches for someone to say "this way is better" (which you did).
  I'm not sure how safe casting an object to void* would be, or 
 back. If you can't avoid it, I would say make overloaded 
 methods that do the intermediate work and make it transparent 
 for you afterwards.
myobject.sizeof returns 4 (in 32 bit DMD) for every object I've tested, so I'm inclined to suspect its a bog-standard pointer, just what I'm looking to save and retrieve. Anybody else want to chime in?
Apr 29 2012
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Monday, 30 April 2012 at 00:28:15 UTC, Jason King wrote:
 myobject.sizeof returns 4 (in 32 bit DMD) for every object I've
 tested, so I'm inclined to suspect its a bog-standard pointer,
 just what I'm looking to save and retrieve.
 Anybody else want to chime in?
I'd say that's right and wrong. Someone correct me if I'm wrong. The 4(bytes) is likely the fat pointer of the string. It still has to contain information saying it's this type of object, and inheritance if it was of another type. I believe it will be something like you have a 16 byte header specifying the object(s) information, and then the object data (4 bytes in this case). This is where it differs from C++ and acts more like java. A heavy part of why this is the case is not only for casting up and down, but management of virtual functions (overloaded). Otherwise you get into C++'s old mess of needing to explicitly saying virtual for every function that 'MIGHT' be overloaded in the future. I haven't tested this, but you should get the idea. class A {} class BA : A{} //returns a BA object from it's A superclass A test() { return cast(A) new BA(); //cast down } void test2() { A a = test(); BA ba = cast(BA) a; //fails if we have no information, we only see class A. assert(ba !is null, "Failed! ba not inherited from A?"); }
Apr 29 2012
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 04/29/2012 06:01 PM, Era Scarecrow wrote:
 On Monday, 30 April 2012 at 00:28:15 UTC, Jason King wrote:
 myobject.sizeof returns 4 (in 32 bit DMD) for every object I've
 tested, so I'm inclined to suspect its a bog-standard pointer,
 just what I'm looking to save and retrieve.
 Anybody else want to chime in?
I'd say that's right and wrong. Someone correct me if I'm wrong. The 4(bytes) is likely the fat pointer of the string.
You mean "object"? A class variable is just a handle to the class object. Class variables are implemented as pointers, so yes, they will be the size of a pointer. Since I suspect that the pointers are 4 bytes on the OP's 32-bit system, I don't see any fatness there. It is just a plain pointer.
 It still has to
 contain information saying it's this type of object, and inheritance if
 it was of another type. I believe it will be something like you have a
 16 byte header specifying the object(s) information, and then the object
 data (4 bytes in this case). This is where it differs from C++ and acts
 more like java.
The object itself has some data that makes it behave like its actual type but I wouldn't be surprised if it contained a virtual function table pointer just like in C++. But note that the object, not the variable is fat in that sense.
 A heavy part of why this is the case is not only for casting up and
 down, but management of virtual functions (overloaded). Otherwise you
 get into C++'s old mess of needing to explicitly saying virtual for
 every function that 'MIGHT' be overloaded in the future.

 I haven't tested this, but you should get the idea.
 class A {}
 class BA : A{}

 //returns a BA object from it's A superclass
 A test() {
 return cast(A) new BA(); //cast down
Correction: It is actually an upcast, which does not require the explicit cast anyway.
 }

 void test2() {
 A a = test();
 BA ba = cast(BA) a; //fails if we have no information, we only see 
class A. And that's a down cast.
 assert(ba !is null, "Failed! ba not inherited from A?");
 }
I find the following message more descriptive: assert(ba !is null, "a is not actually a BA?"); To be more pedantic, 'a' is neither an A nor a BA. It is a variable that provides access to an object through the A interface. Ali
Apr 29 2012
parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Monday, 30 April 2012 at 05:07:04 UTC, Ali Çehreli wrote:
 You mean "object"? A class variable is just a handle to the 
 class object. Class variables are implemented as pointers, so 
 yes, they will be the size of a pointer. Since I suspect that 
 the pointers are 4 bytes on the OP's 32-bit system, I don't see 
 any fatness there. It is just a plain pointer.
I was thinking the 'string' which was a fat pointer, but likely I misread it and assumed the size was referring to the object's contents. 4bytes would be a single pointer, so my mistake on that part.
 The object itself has some data that makes it behave like its 
 actual type but I wouldn't be surprised if it contained a 
 virtual function table pointer just like in C++. But note that 
 the object, not the variable is fat in that sense.
But unlike in C++, it only incorporates it if you add 'virtual', so the up and down casting can revert to it's previous version. I've read about it more than used it so.. //c++ - not tested.. class Base { void func() { stdout << "base!";} } bass Derived : Base { virtual void func() {stdout << "derived (virtual)";} } void main(){ //use pointers? I can't remember... Derived d = new Derived; base b = (Base) d; b.func(); //Logically should call the derived one, but since there's no virtual table, we get the base's one instead!! } Inside the derived class it's a virtual call, change it back to it's base and it loses that extra information. You may be able to up/down cast, but it's a problem in C++. One more reason I don't use C++ if I don't have to.
 Correction: It is actually an upcast, which does not require 
 the explicit cast anyway.
I am getting up and down backwards. My bad..
 I find the following message more descriptive:

   assert(ba !is null, "a is not actually a BA?");

 To be more pedantic, 'a' is neither an A nor a BA. It is a 
 variable that provides access to an object through the A 
 interface.
Apr 29 2012
prev sibling parent Mike Wey <mike-wey example.com> writes:
On 04/30/2012 03:01 AM, Era Scarecrow wrote:
 On Monday, 30 April 2012 at 00:28:15 UTC, Jason King wrote:
 myobject.sizeof returns 4 (in 32 bit DMD) for every object I've
 tested, so I'm inclined to suspect its a bog-standard pointer,
 just what I'm looking to save and retrieve.
 Anybody else want to chime in?
I'd say that's right and wrong. Someone correct me if I'm wrong. The 4(bytes) is likely the fat pointer of the string. It still has to contain information saying it's this type of object, and inheritance if it was of another type. I believe it will be something like you have a 16 byte header specifying the object(s) information, and then the object data (4 bytes in this case). This is where it differs from C++ and acts more like java. A heavy part of why this is the case is not only for casting up and down, but management of virtual functions (overloaded). Otherwise you get into C++'s old mess of needing to explicitly saying virtual for every function that 'MIGHT' be overloaded in the future. I haven't tested this, but you should get the idea.
I have ;)
 class A {}
 class BA : A{}

 //returns a BA object from it's A superclass
 A test() {
 return cast(A) new BA(); //cast down
 }

 void test2() {
 A a = test();
 BA ba = cast(BA) a; //fails if we have no information, we only see class A.
Works because a was originally created as an BA we can cast is back to an BA.
 assert(ba !is null, "Failed! ba not inherited from A?");
 }
-- Mike Wey
Apr 30 2012