Jump to page: 1 2
Thread overview
String[] pointer to void* and back
Sep 18, 2014
seany
Sep 18, 2014
seany
Sep 18, 2014
Ali Çehreli
Sep 18, 2014
seany
Sep 18, 2014
Ali Çehreli
Sep 18, 2014
seany
Sep 18, 2014
Ali Çehreli
Sep 19, 2014
seany
Sep 19, 2014
Ali Çehreli
Sep 19, 2014
anonymous
Sep 18, 2014
anonymous
Sep 18, 2014
bearophile
September 18, 2014
Consider this snippet:



import std.stdio;
import std.conv;
import core.vararg;





void main() {

     string[] s = ["aa", "bb", "cc"];
     string []* ss;
     void * v;

     ss = &s;
     v = cast(void*)s;

     ss = cast(string[]*) v;

     s = *ss;

     writeln(s);

}

This fails, Stack overflow.

If s was a double array, it works.

What am I doing wrong?
September 18, 2014
Found, it should have been       v = cast(void*)ss;

sorry.
September 18, 2014
On 09/18/2014 01:59 PM, seany wrote:

>       string[] s = ["aa", "bb", "cc"];
>       string []* ss;
>       void * v;
>
>       ss = &s;
>       v = cast(void*)s;

Not s, but its address should be assigned to v:

    v = cast(void*)&s;

Only then it will match its reverse operation:

>       ss = cast(string[]*) v;
>
>       s = *ss;
>
>       writeln(s);

Ali

September 18, 2014
Yes, thank you, I corrected that.

However, if this v is a member of a class, like



import std.stdio;
import std.conv;
import core.vararg;

struct S
  {
    void *v;
  }

class C
{


  S* sx = new S;

  void dothings()
  {
     string[] ss = ["1", "2", "4"];
     string[] *s;
     void *vv;

     s = &ss;
     vv = s;

     sx.v = vv;

  }

}


void main() {

     C c = new C;
     c.dothings();
     writeln("done");

     string[]* sh;
     sh = cast(string[]*)c.sx.v;
     writeln(sh);        // upto this, works, the same pointer as set in
                         // c.dothings(), checked with write instructions

     string[] si = *sh;
     writeln(si);
}



and then casted back, then i notice that it does not work. Wondering why.

September 18, 2014
On 09/18/2014 02:35 PM, seany wrote:

> struct S
>    {
>      void *v;
>    }
>
> class C
> {
>
>
>    S* sx = new S;
>
>    void dothings()
>    {
>       string[] ss = ["1", "2", "4"];

Note that ss is a local variable of a druntime type equivalent of the following:

struct D_Slice_of_strings_
{
    size_t length;
    string * ptr;
}

Although the elements that are accessed through .ptr are in dynamic memory and owned by the GC, the local struct object (i.e. ss) itself is on the program stack. It will be gone upon leaving dothings().

>       string[] *s;
>       void *vv;
>
>       s = &ss;
>       vv = s;
>
>       sx.v = vv;
>
>    }

As a demonstration, one solution is to make ss a member variable. Then, it would live as long as v lived. However, ss can be in some other long-lived container as well.

Ali

September 18, 2014
what if i needed to access many such runtime variables  of many types, and did not want to create a member for each type?
September 18, 2014
On Thursday, 18 September 2014 at 21:35:50 UTC, seany wrote:
> Yes, thank you, I corrected that.
>
> However, if this v is a member of a class, like
>
>
>
> import std.stdio;
> import std.conv;
> import core.vararg;
>
> struct S
>   {
>     void *v;
>   }
>
> class C
> {
>
>
>   S* sx = new S;
>
>   void dothings()
>   {
>      string[] ss = ["1", "2", "4"];

ss is a local variable. I.e., ss.ptr and ss.length are on the
stack.

>      string[] *s;
>      void *vv;
>
>      s = &ss;
>      vv = s;

vv now holds a pointer to the stack.

>
>      sx.v = vv;

Here, the pointer to the stack escapes the function. Don't do
that! When dothings returns, its portion of the stack becomes
unoccupied. Other functions will re-use it and stomp over the
data. The escaped pointer then points to some completely unrelated data.

>
>   }
>
> }
>
>
> void main() {
>
>      C c = new C;
>      c.dothings();
>      writeln("done");
>
>      string[]* sh;
>      sh = cast(string[]*)c.sx.v;
>      writeln(sh);        // upto this, works, the same pointer as set in
>                          // c.dothings(), checked with write instructions
>
>      string[] si = *sh;
>      writeln(si);
> }
>
>
>
> and then casted back, then i notice that it does not work. Wondering why.
September 18, 2014
anonymous:

> Here, the pointer to the stack escapes the function. Don't do that!

Hopefully the D type system will be improved with scoping tracking & management, to turn similar operations into compilation errors (as in Rust, but perhaps in a less refined way).

Bye,
bearophile
September 18, 2014
On 09/18/2014 02:52 PM, seany wrote:
> what if i needed to access many such runtime variables  of many types,
> and did not want to create a member for each type?

If you are holding an address in a void*, you must make sure that the original object is still at that location when you attempt to access the object.

If there are limited number of such string[] arrays then you can populate an array in the beginning and pass around void* values to elements in there.

However, as soon as an element is added or removed from an array, all (or some) of the references to those elements get invalidated. (An exceptions is where the array has capacity and you add an element.)

Linked lists, trees, etc. don't have that problem: Their elements stay where they are even after elements are added to or removed from the container.

If you can, storing an index instead of a void* is a better way to go. Even if the elements are relocated, as long as no element is removed from the collection, an index value will always be valid.

Ali

September 19, 2014
On Thursday, 18 September 2014 at 22:16:48 UTC, Ali Çehreli wrote:

> If you are holding an address in a void*, you must make sure that the original object is still at that location when you attempt to access the object.

Does that mean, that there is no way to make a global stack accessible accross modules, of runtime generated stack variables, unless i define such variable species beforehand?
« First   ‹ Prev
1 2