www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - avoid extra variable during void pointer cast

reply Kevin Brogan <kevin brogan.ca> writes:
I have a piece of code that takes a callback function.

The callback has the signature void callback(void* state, void* 
data)

There are several of these functions. All of them use state and 
data as differing types.

As an example, let's look at one that uses both of them as int*.

addInt(void* state, void* data)
{
     *cast(int*)state += *cast(int*)data;
}

Is it not possible to specify the cast as an alias so that I can 
declare the cast once at the beginning of the function?

Something like this?

addInt(void* state, void* data)
{
     alias _state = cast(int*)state; // Error: basic type 
expected, not cast
     alias _data = cast(int*)data; // Error: basic type expected, 
not cast

     *_state += *_data;
}

I can always do this:

addInt(void* state, void* data)
{
     int* _state = cast(int*)state;
     int* _data = cast(int*)data;

     *_state += *_data;
}

But I don't want to create a new variable and assign it everytime 
I call the function. The examples I'm using are contrived, but in 
the c code I am porting this from, the callback gets called 
thousands of times a second, every optimization matters, and the 
variables are used many times per function. I don't want to 
riddle the code with casts if i can avoid it and I don't want to 
create and destroy useless proxy variables every time the 
function is called.
May 14 2017
next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 14 May 2017 at 20:18:24 UTC, Kevin Brogan wrote:
 I have a piece of code that takes a callback function.

 The callback has the signature void callback(void* state, void* 
 data)

 There are several of these functions. All of them use state and 
 data as differing types.

 As an example, let's look at one that uses both of them as int*.

 addInt(void* state, void* data)
 {
     *cast(int*)state += *cast(int*)data;
 }

 Is it not possible to specify the cast as an alias so that I 
 can declare the cast once at the beginning of the function?

 Something like this?

 addInt(void* state, void* data)
 {
     alias _state = cast(int*)state; // Error: basic type 
 expected, not cast
     alias _data = cast(int*)data; // Error: basic type 
 expected, not cast

     *_state += *_data;
 }

 I can always do this:

 addInt(void* state, void* data)
 {
     int* _state = cast(int*)state;
     int* _data = cast(int*)data;

     *_state += *_data;
 }

 But I don't want to create a new variable and assign it 
 everytime I call the function. The examples I'm using are 
 contrived, but in the c code I am porting this from, the 
 callback gets called thousands of times a second, every 
 optimization matters, and the variables are used many times per 
 function. I don't want to riddle the code with casts if i can 
 avoid it and I don't want to create and destroy useless proxy 
 variables every time the function is called.
First: Any decent compiler will optimize both the variable _state, as well as the variable _data out[1][2], this sounds like a classic case of *evil* early optimization. Trust your compiler to get it right; and if you're not sure, check the generated assembly. Second: No, it is not possible, because an alias is a symbol that stands in for another type[3], not for an expression. [1] https://godbolt.org/g/X4D02i [2] https://godbolt.org/g/6i52Tt [3] https://dlang.org/spec/declaration.html#alias
May 14 2017
prev sibling next sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Sun, 14 May 2017 20:18:24 +0000
schrieb Kevin Brogan <kevin brogan.ca>:

 I have a piece of code that takes a callback function.
 
 The callback has the signature void callback(void* state, void* 
 data)
 
 There are several of these functions. All of them use state and 
 data as differing types.
 
 As an example, let's look at one that uses both of them as int*.
 
 addInt(void* state, void* data)
 {
      *cast(int*)state += *cast(int*)data;
 }
 
 Is it not possible to specify the cast as an alias so that I can 
 declare the cast once at the beginning of the function?
 
 Something like this?
 
 addInt(void* state, void* data)
 {
      alias _state = cast(int*)state; // Error: basic type 
 expected, not cast
      alias _data = cast(int*)data; // Error: basic type expected, 
 not cast
 
      *_state += *_data;
 }
No, that is not possible. An alias can only be assigned a symbol.
 I can always do this:
 
 addInt(void* state, void* data)
 {
      int* _state = cast(int*)state;
      int* _data = cast(int*)data;
 
      *_state += *_data;
 }
 
 But I don't want to create a new variable and assign it everytime 
 I call the function. The examples I'm using are contrived, but in 
 the c code I am porting this from, the callback gets called 
 thousands of times a second, every optimization matters, and the 
 variables are used many times per function. I don't want to 
 riddle the code with casts if i can avoid it and I don't want to 
 create and destroy useless proxy variables every time the 
 function is called.
Let the compiler optimize the assignment away and don't worry much about it. Inlining also works well within the same module. In this case here I would probably use "consume" functions as I dub them: import std.traits; pragma(inline, true) /* Not really needed I hope ;) */ ref T consume(T)(ref void* data) if (!hasIndirections!T) { T* ptr = cast(T*)data; data += T.sizeof; return *ptr; } You can then rewrite your addInt function like this: void add(T)(void* state, void* data) if (isNumeric!T) { state.consume!T += data.consume!T; } -- Marco
May 14 2017
parent reply Bauss <jj_1337 live.dk> writes:
On Sunday, 14 May 2017 at 21:07:36 UTC, Marco Leise wrote:
 Am Sun, 14 May 2017 20:18:24 +0000
 schrieb Kevin Brogan <kevin brogan.ca>:

 [...]
No, that is not possible. An alias can only be assigned a symbol.
 [...]
Let the compiler optimize the assignment away and don't worry much about it. Inlining also works well within the same module. In this case here I would probably use "consume" functions as I dub them: import std.traits; pragma(inline, true) /* Not really needed I hope ;) */ ref T consume(T)(ref void* data) if (!hasIndirections!T) { T* ptr = cast(T*)data; data += T.sizeof; return *ptr; } You can then rewrite your addInt function like this: void add(T)(void* state, void* data) if (isNumeric!T) { state.consume!T += data.consume!T; }
pragma(inline, true); doesn't actually do what you think it does. In lining is always done whenever possible and that only tells the compiler to spit out an error if it can't inline it.
May 15 2017
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Mon, 15 May 2017 19:30:00 +0000
schrieb Bauss <jj_1337 live.dk>:

 pragma(inline, true); doesn't actually do what you think it does. 
 In lining is always done whenever possible and that only tells 
 the compiler to spit out an error if it can't inline it.
A compiler doesn't simply inline whenever it can. A big function that's called often would lead to massive code duplication in that case. What I meant pragma(inline, true) to do is overrule this cost calculation. Since the OP asked for no extra function calls, the error on failure to inline seemed appropriate. Cross-module inlining may fail for example on some compiler(s) or with separate compilation. -- Marco
May 17 2017
prev sibling next sibling parent Mike B Johnson <Mikey Ikes.com> writes:
On Sunday, 14 May 2017 at 20:18:24 UTC, Kevin Brogan wrote:
 I have a piece of code that takes a callback function.

 The callback has the signature void callback(void* state, void* 
 data)

 There are several of these functions. All of them use state and 
 data as differing types.

 As an example, let's look at one that uses both of them as int*.

 addInt(void* state, void* data)
 {
     *cast(int*)state += *cast(int*)data;
 }

 Is it not possible to specify the cast as an alias so that I 
 can declare the cast once at the beginning of the function?

 Something like this?

 addInt(void* state, void* data)
 {
     alias _state = cast(int*)state; // Error: basic type 
 expected, not cast
     alias _data = cast(int*)data; // Error: basic type 
 expected, not cast

     *_state += *_data;
 }

 I can always do this:

 addInt(void* state, void* data)
 {
     int* _state = cast(int*)state;
     int* _data = cast(int*)data;

     *_state += *_data;
 }

 But I don't want to create a new variable and assign it 
 everytime I call the function. The examples I'm using are 
 contrived, but in the c code I am porting this from, the 
 callback gets called thousands of times a second, every 
 optimization matters, and the variables are used many times per 
 function. I don't want to riddle the code with casts if i can 
 avoid it and I don't want to create and destroy useless proxy 
 variables every time the function is called.
1. Use template, that is what they are for addInt(A, B)(A* state, B* data) { static if(is(B == int)) { // B is an int if this block is called so no reason to cast. } } 2. Use overloads, basically same as templates. addInt(int* state, int* data) { } 3. Don't worry about it, any extra temp variables will almost surely be optimized away.
May 14 2017
prev sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On the point of "not possible...", "only a symbol...", etc:

T* ptrCast(T, alias ptr)() { return cast(T*)ptr; }

void addInt(void* state, void* data)
{
     alias _state = ptrCast!(int, state);
     alias _data = ptrCast!(int, data);

     static assert(!is(typeof(_state) == int*));
     static assert(!is(typeof(_data) == int*));

     *_state += *_data;
}

But take heed to the compiler optimization advice. DMD generates 
pretty horrendous code for this. LDC does rather well though. 
Since speed matters, always look at the assembly. Look at it even 
if it doesn't ;)
May 14 2017
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 14 May 2017 at 21:16:04 UTC, Stanislav Blinov wrote:
 On the point of "not possible...", "only a symbol...", etc:

 T* ptrCast(T, alias ptr)() { return cast(T*)ptr; }

 void addInt(void* state, void* data)
 {
     alias _state = ptrCast!(int, state);
     alias _data = ptrCast!(int, data);

     static assert(!is(typeof(_state) == int*));
     static assert(!is(typeof(_data) == int*));

     *_state += *_data;
 }
That's a pretty cool workaround, but not an alias to the cast, but an alias to a parametrized function template (a type), so I will stick to my statement of "not possible". AFAIK, this would also invoke the respective function template instance for ptrCast every time _state or _data are referenced, so if we're going with the spirit of the question that's still going to be horrible if the compiler doesn't optimize ;p
May 14 2017
parent reply ag0aep6g <anonymous example.com> writes:
On 05/14/2017 11:35 PM, Moritz Maxeiner wrote:
 On Sunday, 14 May 2017 at 21:16:04 UTC, Stanislav Blinov wrote:
[...]
 T* ptrCast(T, alias ptr)() { return cast(T*)ptr; }
[...]
     alias _state = ptrCast!(int, state);
[...]
 That's a pretty cool workaround, but not an alias to the cast, but an
 alias to a parametrized function template (a type),
Not sure if I'm reading that right, but `_state` is not an alias of a (parametrized function) template. The template instantiation results in a function. `_state` is an alias of that function. `alias foo = ptrCast;` would make an alias of the template. Neither of them is a type.
May 14 2017
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Sunday, 14 May 2017 at 21:55:01 UTC, ag0aep6g wrote:
 On 05/14/2017 11:35 PM, Moritz Maxeiner wrote:
 On Sunday, 14 May 2017 at 21:16:04 UTC, Stanislav Blinov wrote:
[...]
 T* ptrCast(T, alias ptr)() { return cast(T*)ptr; }
[...]
     alias _state = ptrCast!(int, state);
[...]
 That's a pretty cool workaround, but not an alias to the cast, 
 but an
 alias to a parametrized function template (a type),
Not sure if I'm reading that right, but `_state` is not an alias of a (parametrized function) template. The template instantiation results in a function. `_state` is an alias of that function. `alias foo = ptrCast;` would make an alias of the template. Neither of them is a type.
Yep, it's an alias to template function instantiation, that is, concrete function - a symbol. But of course, it *is* going to be called on every "dereference". GDC optimizes the call away starting at -O1, LDC needs -O2. DMD makes temporaries :)
May 14 2017
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 14 May 2017 at 22:00:58 UTC, Stanislav Blinov wrote:
 [...]

 Yep, it's an alias to template function instantiation, that is, 
 concrete function - a symbol.
Yes, my bad :(
 But of course, it *is* going to be called on every 
 "dereference". GDC optimizes the call away starting at -O1, LDC 
 needs -O2. DMD makes temporaries :)
Which just reinforces my personal mantra: Develop with dmd, release with ldc or gdc.
May 14 2017
prev sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 14 May 2017 at 21:55:01 UTC, ag0aep6g wrote:
 On 05/14/2017 11:35 PM, Moritz Maxeiner wrote:
 On Sunday, 14 May 2017 at 21:16:04 UTC, Stanislav Blinov wrote:
[...]
 T* ptrCast(T, alias ptr)() { return cast(T*)ptr; }
[...]
     alias _state = ptrCast!(int, state);
[...]
 That's a pretty cool workaround, but not an alias to the cast, 
 but an
 alias to a parametrized function template (a type),
Not sure if I'm reading that right, but `_state` is not an alias of a (parametrized function) template. The template instantiation results in a function. `_state` is an alias of that function. `alias foo = ptrCast;` would make an alias of the template. Neither of them is a type.
You're right. I forgot about the alias to a symbol rule (event though I myself just linked to it :/ ). Sorry about that.
May 14 2017