String[] pointer to void* and back

Ali Çehreli via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Fri Sep 19 10:47:18 PDT 2014


On 09/19/2014 05:14 AM, seany wrote:

 > 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,

I assume you mean 'program stack' as opposed to the 'stack' data structure.

Objects on the program stack don't work because program stack is like a 
scratch pad and the stack data structure won't work in some situations 
if it's implemented in terms of an array and if the array gets larger 
over time, because array elements get moved around as the capacity is 
consumed.

The important part is to ensure that the object will be there when the 
void* is actually used.

Here is an example that uses a fixed-length array. string[] objects are 
passed as void* parameters to a function (perhaps a C library function). 
That function uses the void* argument when calling the callback function.

When the callback function received the void*, the object is still in 
the global (more correctly, module-scoped) array.

Note that although I used literals string arrays when populating the 
tables, they can be stack variables as well, as long as they are put in 
to the array and addresses of the array elements are used.

import std.stdio;

string[][2] stringTables;

void populateTables()
{
     stringTables[0] = [ "apple", "pear" ];
     stringTables[1] = [ "coffee", "tea" ];
}

void main()
{
     populateTables();

     foreach (i; 0 .. 4) {
         const index = i % 2;
         void* param = &(stringTables[index]);
         callWith(&callbackFunction, param);
     }
}

alias CallbackFunction = void function(void*);

void callWith(CallbackFunction func, void* param)
{
     func(param);
}

void callbackFunction(void* param)
{
     string[]* table = cast(string[]*)param;
     writefln("Called with %s", *table);
}

 > unless i define such variable species beforehand?

Unlike the example above, you can define the objects during runtime as 
well. The problem with slices is that it is not possible to create the 
actual slice object dynamically:

     alias Numbers = int[];
     auto p = new Numbers;

Error: new can only create structs, dynamic arrays or class objects, not 
int[]'s

Ironically, the error message mentions 'dynamic arrays' but it is 
talking about the actual storage for elements, not the slice object itself.

So, one way is to wrap the slice in a struct:

struct Table
{
     int[] table;
}

void foo()
{
     void* p = new Table;
     // ...
}

Now it works and 'p' can be used as a void*. Although the struct object 
(and the slice inside it) is on the GC heap, there is a different kind 
of problem: There may not be any other pointer to that object and the GC 
may collect the memory before the void* is used in the program later on. 
(This may not be a concern if the void* remains in the program as a 
reference. However, if the void* is passed to e.g. a C library function, 
which puts it in a container there, then GC will not know about it.

Although GC.addRange helps with that, the void* can safely be put in a 
void* slice as well:

void*[] persistingTable;

     persistingTable ~= p;

This time, even if persistingTable moves its void* elements around, it 
won't matter because persistingTable is only for keeping a visible 
pointer in the program so that GC doesn't free our objects.

As you can see, there are a lot of things to consider. :) What are you 
thinking of using the void* for?

Ali



More information about the Digitalmars-d-learn mailing list