digitalmars.D.learn - C callbacks getting a value of 0! Bug in D?
- Johnson Jones (159/159) Aug 27 2017 Trying to set a callback for portaudio and it's seeing zero for
- Johnson Jones (23/23) Aug 27 2017 Looking at the assembly shows something like this:
- Steven Schveighoffer (6/32) Aug 28 2017 For C/C++ interaction, always use c_... types if they are available. The...
- Johnson Jones (4/12) Aug 28 2017 and where are these c_ types defined? The reason I replaced them
- Moritz Maxeiner (10/24) Aug 28 2017 core.stdc.config
- Johnson Jones (3/19) Aug 28 2017 Thanks. I copied over stuff from the bindings and from the
- Johnson Jones (11/47) Aug 28 2017 In portaudio, this doesn't seem to be the case. I changed all the
- Steven Schveighoffer (21/64) Aug 28 2017 Then I think possibly the port audio bindings are not correct. It's also...
- Johnson Jones (18/40) Aug 28 2017 import core.stdc.config;
- Moritz Maxeiner (7/10) Aug 28 2017 There are different 64bit data models [1] and it seems your
- Johnson Jones (5/17) Aug 28 2017 Yes.
- Moritz Maxeiner (5/7) Aug 28 2017 With respect to the currently major platforms you can reasonable
- Mike Parker (5/21) Aug 28 2017 It's because you're on Windows. There, long/ulong are 4 bytes in
- Steven Schveighoffer (4/5) Aug 29 2017 Yes, this is exactly why you should use c_long and c_ulong, because just...
Trying to set a callback for portaudio and it's seeing zero for the value passed. Pa_OpenStream(&stream, input, output, sampleRate, cast(ulong)0, cast(PaStreamFlags)(PaStreamFlags.NoFlag + 0*PaStreamFlags.PrimeOutputBuffersUsingStreamCallback), cast(PaStreamCallback)(a,b,c,d,e,f){ callbackCalled = true; return 0; }, null); I am using a debug build of portaudio that prints out all the parameters before anything else, so it's not an issue with portaudio. I've tried passing a normal function: __gshared int sawtooth(const(void)* inputBuffer, void* outputBuffer, ulong framesPerBuffer, const(PaStreamCallbackTimeInfo)* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { return 0; } and passing it as &sawtooth. with the same issue. I've tried changing the calling convention and using __gshared but the value is always 0. It seems other values are 0 too and can't seem to get any value in to the callback(Even a random one). The output essentially always looks like this: Opening Stream! Pa_OpenStream called: PaStream** stream: 0x00823EA0 PaStreamParameters *inputParameters: NULL PaStreamParameters *outputParameters: 0x02C6C1C0 PaDeviceIndex outputParameters->device: 3 int outputParameters->channelCount: 4 PaSampleFormat outputParameters->sampleFormat: 1 PaTime outputParameters->suggestedLatency: 0.135000 void *outputParameters->hostApiSpecificStreamInfo: 0x00000000 double sampleRate: 44100 unsigned long framesPerBuffer: 256 PaStreamFlags streamFlags: 0x0 PaStreamCallback *streamCallback: 0x00000000 void *userData: 0x00000000 Pa_GetSampleSize called: PaSampleFormat format: 8 Pa_GetSampleSize returned: int: 2 Pa_GetSampleSize called: PaSampleFormat format: 8 Pa_GetSampleSize returned: int: 2 Pa_GetSampleSize called: PaSampleFormat format: 1 Pa_GetSampleSize returned: int: 4 Pa_GetSampleSize called: PaSampleFormat format: 8 Pa_GetSampleSize returned: int: 2 Pa_GetSampleSize called: PaSampleFormat format: 8 Pa_GetSampleSize returned: int: 2 Pa_OpenStream returned: *(PaStream** stream): 0x03BEAD50 PaError: 0 ( Success ) everything seems correct except: PaStreamCallback *streamCallback: 0x00000000 void *userData: 0x00000000 You can find the full code at https://forum.dlang.org/thread/lkbswgpsgxynhfyzwfqz forum.dlang.org This is either a bug in D, an issue with calling conventions, or how one passes the data. The original portaudio open stream function, so you can see that it simply prints it's arguments: PaError Pa_OpenStream( PaStream** stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, unsigned long framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback *streamCallback, void *userData ) { PaError result; PaUtilHostApiRepresentation *hostApi = 0; PaDeviceIndex hostApiInputDevice = paNoDevice, hostApiOutputDevice = paNoDevice; PaStreamParameters hostApiInputParameters, hostApiOutputParameters; PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; #ifdef PA_LOG_API_CALLS PA_LOGAPI_ENTER_PARAMS( "Pa_OpenStream" ); PA_LOGAPI(("\tPaStream** stream: 0x%p\n", stream )); if( inputParameters == NULL ){ PA_LOGAPI(("\tPaStreamParameters *inputParameters: NULL\n" )); }else{ PA_LOGAPI(("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters )); PA_LOGAPI(("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device )); PA_LOGAPI(("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount )); PA_LOGAPI(("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat )); PA_LOGAPI(("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency )); PA_LOGAPI(("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo )); } if( outputParameters == NULL ){ PA_LOGAPI(("\tPaStreamParameters *outputParameters: NULL\n" )); }else{ PA_LOGAPI(("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters )); PA_LOGAPI(("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device )); PA_LOGAPI(("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount )); PA_LOGAPI(("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat )); PA_LOGAPI(("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency )); PA_LOGAPI(("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo )); } PA_LOGAPI(("\tdouble sampleRate: %g\n", sampleRate )); PA_LOGAPI(("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer )); PA_LOGAPI(("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags )); PA_LOGAPI(("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback )); PA_LOGAPI(("\tvoid *userData: 0x%p\n", userData )); #endif and this is how I define it in the code(which you can see if you follow the link): PaError function(PaStream** stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, ulong framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback streamCallback, void *userData) Pa_OpenStream; I can play audio if I do a hard write, but obviously that is generally useless. This seems like it might be a bug in D in that the last parameters are invalid. user_data, which is passing a simple pointer is also 0, when it isn't. Even trying something like PaError function(PaStream** stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, ulong framesPerBuffer, PaStreamFlags streamFlags, size_t streamCallback, size_t userData) Pa_OpenStream; and passing numbers still shows zero values! Any ideas?
Aug 27 2017
Looking at the assembly shows something like this: 0041ea98 push 0x0 0041ea9a push 0x0 0041ea9c push 0x0 0041ea9e push dword 0x100 0041eaa3 mov ecx, [typeid(PaStreamParameters)+0xe36fc (0x80d4cc)] 0041eaa9 mov eax, [fs:0x2c] 0041eaaf mov edx, [eax+ecx*4] 0041eab2 push dword [edx+0x1c] 0041eab8 push dword [edx+0x18] 0041eabe push dword [ebp-0x54] 0041eac1 push dword [ebp-0x5c] 0041eac4 mov ebx, PA.stream (0x823f30) 0041eac9 push ebx 0041eaca call dword near [Pa_OpenStream (0x823f18)] I noticed that those 0's were the values being fed in to the function. I remember converting c_ulong's to ulong's and that they were probably uint's in D. Converting those fixed the problem and the callback is now called! I converted all the ulongs to uint's but there were a few longs and I don't know if they are c_longs or d_longs... Anyways, At least I'm on the right track.
Aug 27 2017
On 8/27/17 10:17 PM, Johnson Jones wrote:Looking at the assembly shows something like this: 0041ea98 push 0x0 0041ea9a push 0x0 0041ea9c push 0x0 0041ea9e push dword 0x100 0041eaa3 mov ecx, [typeid(PaStreamParameters)+0xe36fc (0x80d4cc)] 0041eaa9 mov eax, [fs:0x2c] 0041eaaf mov edx, [eax+ecx*4] 0041eab2 push dword [edx+0x1c] 0041eab8 push dword [edx+0x18] 0041eabe push dword [ebp-0x54] 0041eac1 push dword [ebp-0x5c] 0041eac4 mov ebx, PA.stream (0x823f30) 0041eac9 push ebx 0041eaca call dword near [Pa_OpenStream (0x823f18)] I noticed that those 0's were the values being fed in to the function. I remember converting c_ulong's to ulong's and that they were probably uint's in D. Converting those fixed the problem and the callback is now called! I converted all the ulongs to uint's but there were a few longs and I don't know if they are c_longs or d_longs... Anyways, At least I'm on the right track.For C/C++ interaction, always use c_... types if they are available. The idea is both that they will be correctly defined for the width, and also it will mangle correctly for C++ compilers (yes, long and int are mangled differently even when they are the same thing). -Steve
Aug 28 2017
On Monday, 28 August 2017 at 21:35:27 UTC, Steven Schveighoffer wrote:On 8/27/17 10:17 PM, Johnson Jones wrote:and where are these c_ types defined? The reason I replaced them was precisely because D was not finding them.[...]For C/C++ interaction, always use c_... types if they are available. The idea is both that they will be correctly defined for the width, and also it will mangle correctly for C++ compilers (yes, long and int are mangled differently even when they are the same thing). -Steve
Aug 28 2017
On Monday, 28 August 2017 at 22:21:18 UTC, Johnson Jones wrote:On Monday, 28 August 2017 at 21:35:27 UTC, Steven Schveighoffer wrote:core.stdc.config , which unfortunately doesn't appear in the online documentation AFAICT (something that ought to be fixed). A common workaround is to use pattern searching tools like grep if you know the phrase to look for: $ grep -Er c_long /path/to/imports , or in this case, since these things are usually done with aliases: $ grep -Er 'alias\s+\w*\s+c_long' /path/to/importsOn 8/27/17 10:17 PM, Johnson Jones wrote:and where are these c_ types defined? The reason I replaced them was precisely because D was not finding them.[...]For C/C++ interaction, always use c_... types if they are available. The idea is both that they will be correctly defined for the width, and also it will mangle correctly for C++ compilers (yes, long and int are mangled differently even when they are the same thing). -Steve
Aug 28 2017
On Monday, 28 August 2017 at 22:41:56 UTC, Moritz Maxeiner wrote:On Monday, 28 August 2017 at 22:21:18 UTC, Johnson Jones wrote:Thanks. I copied over stuff from the bindings and from the original header and I guess I missed the import.On Monday, 28 August 2017 at 21:35:27 UTC, Steven Schveighoffer wrote:core.stdc.config , which unfortunately doesn't appear in the online documentation AFAICT (something that ought to be fixed). A common workaround is to use pattern searching tools like grep if you know the phrase to look for: $ grep -Er c_long /path/to/imports , or in this case, since these things are usually done with aliases: $ grep -Er 'alias\s+\w*\s+c_long' /path/to/imports[...]and where are these c_ types defined? The reason I replaced them was precisely because D was not finding them.
Aug 28 2017
On Monday, 28 August 2017 at 21:35:27 UTC, Steven Schveighoffer wrote:On 8/27/17 10:17 PM, Johnson Jones wrote:In portaudio, this doesn't seem to be the case. I changed all the longs to ints and ran my code in x64 and it worked fine. It may just be that the stuff that uses long is not used in my code. In port audio I see it using unsigned long and so this should be 64-bits in x64. Surprised it worked. Maybe conversion is taking place or the high bits of the long are 0'ed and so there is not much difference. Anyways, I guess I'll deal with any of those bugs when I run in to them, if they exist.Looking at the assembly shows something like this: 0041ea98 push 0x0 0041ea9a push 0x0 0041ea9c push 0x0 0041ea9e push dword 0x100 0041eaa3 mov ecx, [typeid(PaStreamParameters)+0xe36fc (0x80d4cc)] 0041eaa9 mov eax, [fs:0x2c] 0041eaaf mov edx, [eax+ecx*4] 0041eab2 push dword [edx+0x1c] 0041eab8 push dword [edx+0x18] 0041eabe push dword [ebp-0x54] 0041eac1 push dword [ebp-0x5c] 0041eac4 mov ebx, PA.stream (0x823f30) 0041eac9 push ebx 0041eaca call dword near [Pa_OpenStream (0x823f18)] I noticed that those 0's were the values being fed in to the function. I remember converting c_ulong's to ulong's and that they were probably uint's in D. Converting those fixed the problem and the callback is now called! I converted all the ulongs to uint's but there were a few longs and I don't know if they are c_longs or d_longs... Anyways, At least I'm on the right track.For C/C++ interaction, always use c_... types if they are available. The idea is both that they will be correctly defined for the width, and also it will mangle correctly for C++ compilers (yes, long and int are mangled differently even when they are the same thing). -Steve
Aug 28 2017
On 8/28/17 7:47 PM, Johnson Jones wrote:On Monday, 28 August 2017 at 21:35:27 UTC, Steven Schveighoffer wrote:Then I think possibly the port audio bindings are not correct. It's also possible that long is not 64-bit even on the platform you are using. Finally, it's also possible that the D compiler is not generating the call correctly. When I test on my mac, c_long is 8-bytes. I'm not sure what it is in your case, try this code and see what happens: import core.stdc.config; pragma(msg, c_long.sizeof); // prints 8 on my box. And in c: #include <stdio.h> int main() { printf("%lu\n", sizeof(long)); // also prints 8 on my box return 0; } They should match. If they don't, that's a D bug (in either core.stdc.config or the compiler, I'm not sure which). If they do, then one of the other two possibilities is happening, and I would lean towards the bindings being incorrect. -SteveOn 8/27/17 10:17 PM, Johnson Jones wrote:In portaudio, this doesn't seem to be the case. I changed all the longs to ints and ran my code in x64 and it worked fine. It may just be that the stuff that uses long is not used in my code. In port audio I see it using unsigned long and so this should be 64-bits in x64. Surprised it worked. Maybe conversion is taking place or the high bits of the long are 0'ed and so there is not much difference.Looking at the assembly shows something like this: 0041ea98 push 0x0 0041ea9a push 0x0 0041ea9c push 0x0 0041ea9e push dword 0x100 0041eaa3 mov ecx, [typeid(PaStreamParameters)+0xe36fc (0x80d4cc)] 0041eaa9 mov eax, [fs:0x2c] 0041eaaf mov edx, [eax+ecx*4] 0041eab2 push dword [edx+0x1c] 0041eab8 push dword [edx+0x18] 0041eabe push dword [ebp-0x54] 0041eac1 push dword [ebp-0x5c] 0041eac4 mov ebx, PA.stream (0x823f30) 0041eac9 push ebx 0041eaca call dword near [Pa_OpenStream (0x823f18)] I noticed that those 0's were the values being fed in to the function. I remember converting c_ulong's to ulong's and that they were probably uint's in D. Converting those fixed the problem and the callback is now called! I converted all the ulongs to uint's but there were a few longs and I don't know if they are c_longs or d_longs... Anyways, At least I'm on the right track.For C/C++ interaction, always use c_... types if they are available. The idea is both that they will be correctly defined for the width, and also it will mangle correctly for C++ compilers (yes, long and int are mangled differently even when they are the same thing).
Aug 28 2017
On Tuesday, 29 August 2017 at 00:42:45 UTC, Steven Schveighoffer wrote:On 8/28/17 7:47 PM, Johnson Jones wrote:import core.stdc.config; pragma(msg, c_long.sizeof); prints 4UL both on x64 and x86 and and C: void foo() { int dummy; switch (dummy) { case sizeof(long) : case sizeof(long) : break; } } produces 4 on both x86 and x64. So, I'm not sure how you are getting 8.[...]Then I think possibly the port audio bindings are not correct. It's also possible that long is not 64-bit even on the platform you are using. Finally, it's also possible that the D compiler is not generating the call correctly. When I test on my mac, c_long is 8-bytes. I'm not sure what it is in your case, try this code and see what happens: import core.stdc.config; pragma(msg, c_long.sizeof); // prints 8 on my box. And in c: #include <stdio.h> int main() { printf("%lu\n", sizeof(long)); // also prints 8 on my box return 0; } They should match. If they don't, that's a D bug (in either core.stdc.config or the compiler, I'm not sure which). If they do, then one of the other two possibilities is happening, and I would lean towards the bindings being incorrect. -Steve
Aug 28 2017
On Tuesday, 29 August 2017 at 01:34:40 UTC, Johnson Jones wrote:[...] produces 4 on both x86 and x64. So, I'm not sure how you are getting 8.There are different 64bit data models [1] and it seems your platform uses LLP64, which uses 32bit longs. Am I correct in assuming you're on Windows (as they are the only major modern platform that I'm aware of that made this choice)? [1] https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
Aug 28 2017
On Tuesday, 29 August 2017 at 01:56:43 UTC, Moritz Maxeiner wrote:On Tuesday, 29 August 2017 at 01:34:40 UTC, Johnson Jones wrote:Yes. I found this, which gives a map for all the types: https://dlang.org/spec/interfaceToC.html Seems only long and ulong are issues.[...] produces 4 on both x86 and x64. So, I'm not sure how you are getting 8.There are different 64bit data models [1] and it seems your platform uses LLP64, which uses 32bit longs. Am I correct in assuming you're on Windows (as they are the only major modern platform that I'm aware of that made this choice)? [1] https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models
Aug 28 2017
On Tuesday, 29 August 2017 at 02:47:34 UTC, Johnson Jones wrote:[...] Seems only long and ulong are issues.With respect to the currently major platforms you can reasonable expect software to run on, yes. Just don't try to use D on something with e.g. 32 bit C shorts unless you bind to it via c_short.
Aug 28 2017
On Tuesday, 29 August 2017 at 01:34:40 UTC, Johnson Jones wrote:import core.stdc.config; pragma(msg, c_long.sizeof); prints 4UL both on x64 and x86 and and C: void foo() { int dummy; switch (dummy) { case sizeof(long) : case sizeof(long) : break; } } produces 4 on both x86 and x64. So, I'm not sure how you are getting 8.It's because you're on Windows. There, long/ulong are 4 bytes in both 32- and 64-bit. On Linux/Mac/*BSD, they're 4 in 32-bit and 8 in 64-bit. This is why we have c_long and c_ulong, to hide those differences.
Aug 28 2017
On 8/28/17 9:34 PM, Johnson Jones wrote:produces 4 on both x86 and x64. So, I'm not sure how you are getting 8.Yes, this is exactly why you should use c_long and c_ulong, because just using int makes it not portable to other systems. -Steve
Aug 29 2017