Jump to page: 1 2
Thread overview
avoid extra variable during void pointer cast
May 15
Bauss
May 14
ag0aep6g
May 14
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
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
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
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
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
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
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
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
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
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.
« First   ‹ Prev
1 2