What is the recommended way for a C basic type to be passed-by-reference to function wanting to use it for output.
For example 'width' & 'height' here in this library unction... int FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document, int page_index, double* width, double* height); void mytestGetPageSizeByIndex(doc) { double width, height; FPDF_GetPageSizeByIndex(doc, 0, &width, &height); printf("width=%f, height=%f\n", width, height); } gives... width=200.000000, height=200.000000 In Pharo I'm trying... FFIOpaqueObject subclass: #FPDF_DOCUMENT FPDF_GetPageSizeByIndex__document: document page_index: page_index width: width height: height ^self ffiCall: #(int FPDF_GetPageSizeByIndex( FPDF_DOCUMENT *document, int page_index, FFIFloat64 * width, FFIFloat64 * height)) testGetPageSizeByIndex | document page_index width height result| PDFium FPDF_InitLibrary. document := PDFium FPDF_LoadDocument__file_path: helloPdf password: ''. width := 0.0. height := 0.0. page_index := 0. "Its zero based" result := PDFium FPDF_GetPageSizeByIndex__document: document page_index: 0 width: width height: height. PDFium FPDF_CloseDocument__document: document. PDFium FPDF_DestroyLibrary. self assert: document isNull not. "Document opened okay, and btw this works for a different pageCount test" self assert: result > 0. "Non-zero for success. 0 for error (document or page not found)" self halt. and at the halt the Inspector shows... result = 1 width = 0.0 height = 0.0 cheers -ben |
> On 15 Nov 2017, at 02:05, Ben Coman <[hidden email]> wrote: > > What is the recommended way for a C basic type to be passed-by-reference to function wanting to use it for output. > For example 'width' & 'height' here in this library unction... > > int FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document, > int page_index, > double* width, > double* height); > > void mytestGetPageSizeByIndex(doc) { > double width, height; > FPDF_GetPageSizeByIndex(doc, 0, &width, &height); > printf("width=%f, height=%f\n", width, height); > } > > gives... > width=200.000000, height=200.000000 > > > > In Pharo I'm trying... > > FFIOpaqueObject subclass: #FPDF_DOCUMENT > > FPDF_GetPageSizeByIndex__document: document > page_index: page_index > width: width > height: height > ^self ffiCall: #(int FPDF_GetPageSizeByIndex( > FPDF_DOCUMENT *document, > int page_index, > FFIFloat64 * width, > FFIFloat64 * height)) > > > testGetPageSizeByIndex > | document page_index width height result| > PDFium FPDF_InitLibrary. > document := PDFium FPDF_LoadDocument__file_path: helloPdf password: ''. > width := 0.0. > height := 0.0. > page_index := 0. "Its zero based" > result := PDFium FPDF_GetPageSizeByIndex__document: document > page_index: 0 > width: width > height: height. > PDFium FPDF_CloseDocument__document: document. > PDFium FPDF_DestroyLibrary. > self assert: document isNull not. "Document opened okay, and btw this works for a different pageCount test" > self assert: result > 0. "Non-zero for success. 0 for error (document or page not found)" > self halt. > > > and at the halt the Inspector shows... > result = 1 > width = 0.0 > height = 0.0 no, that will not work :) what you need to do here is to pass a “buffer” to contain the width and height: testGetPageSizeByIndex | document page_index widthBuffer heightBuffer width height result| PDFium FPDF_InitLibrary. document := PDFium FPDF_LoadDocument__file_path: helloPdf password: ''. widthBuffer := ByteArray new: (FFIFloat64 typeSize). heightBuffer := ByteArray new: (FFIFloat64 typeSize). page_index := 0. "Its zero based" result := PDFium FPDF_GetPageSizeByIndex__document: document page_index: 0 width: widthBuffer height: heightBuffer. width := widthBuffer doubleAt: 1. height := heightBuffer doubleAt: 1. PDFium FPDF_CloseDocument__document: document. PDFium FPDF_DestroyLibrary. self assert: document isNull not. "Document opened okay, and btw this works for a different pageCount test" self assert: result > 0. "Non-zero for success. 0 for error (document or page not found)" self halt. side note: I do not recommend using the UFFI type in declarations. In the long way is worst for comprehension and maintainability. This thingy: ^self ffiCall: #(int FPDF_GetPageSizeByIndex( FPDF_DOCUMENT *document, int page_index, FFIFloat64 * width, FFIFloat64 * height)) would be better as in original version: ^self ffiCall: #(int FPDF_GetPageSizeByIndex( FPDF_DOCUMENT *document, int page_index, double * width, double * height)) Esteban > > cheers -ben > |
On 15 November 2017 at 17:27, Esteban Lorenzano <[hidden email]> wrote:
widthBuffer := ByteArray new: (FFIFloat64 new typeSize). or... widthBuffer := ByteArray new: (FFIFloat64 externalTypeSize). Now it would be nice to do... widthBuffer := FFIFloat64 newBuffer. which could be implemented... FFIExternalType class >> newBuffer. ^ByteArray new: (self externalTypeSize) testGetPageSizeByIndex | document page_index widthBuffer heightBuffer width height result| PDFium FPDF_InitLibrary. document := PDFium FPDF_LoadDocument__file_path: helloPdf password: ''. widthBuffer := FFIFloat64 buffer. heightBuffer := FFIFloat64 buffer. page_index := 0. "Its zero based" result := PDFium FPDF_GetPageSizeByIndex__ page_index: 0 height: heightBuffer. width := widthBuffer doubleAt: 1. height := heightBuffer doubleAt: 1. btw, a broader curiousity even though this is likely set in stone and not even your personal choice - why "FFIFloat64" and not "FFIDouble" ? widthBuffer := FFIDouble newBuffer. would be a nice match to the callout declaration. Would it be overkill to have in Pharo an empty FFIDouble subclassed from FFIFloat64 ? the superclass being explicit about the format, while the subclass provides a nice identifier for users? page_index := 0. "Its zero based" PDFium FPDF_CloseDocument__document: document. Thanks for the heads up. cheers -ben |
you know, I thought the same :) but since I’m officially on holiday I didn’t do it (yet). If you want to add an issue and send a PR, it would be perfect :)
because the ABI defines float64 as the right name, heh. and yes, it was not my personal choice ;)
maybe it is overkilling. Because also… where do you put the limit? (doubles, floats, longs, etc… ?) Esteban
|
On 15 November 2017 at 20:47, Esteban Lorenzano <[hidden email]> wrote:
Well, there are not so many... ?? cheers -ben |
In reply to this post by EstebanLM
On 15 November 2017 at 20:47, Esteban Lorenzano <[hidden email]> wrote:
> > which could be implemented... > FFIExternalType class >> newBuffer. > ^ByteArray new: (self externalTypeSize) > > testGetPageSizeByIndex > | document page_index widthBuffer heightBuffer width height result| > PDFium FPDF_InitLibrary. > document := PDFium FPDF_LoadDocument__file_path: helloPdf > password: ''. > widthBuffer := FFIFloat64 buffer. > heightBuffer := FFIFloat64 buffer. > page_index := 0. "Its zero based" > result := PDFium FPDF_GetPageSizeByIndex__document: document > page_index: 0 > width: widthBuffer > height: heightBuffer. > width := widthBuffer doubleAt: 1. > height := heightBuffer doubleAt: 1. I'm about to do something similar with for this prototype... void clang_getFileLocation( CXSourceLocation location, CXFile *file, unsigned *line, unsigned *column, unsigned *offset); Now 'uints' are good to work with since they are consistently 4 bytes on all modern platforms (Windows / Linux / Unix) x (32bit / 64bit) http://nickdesaulniers.github.io/blog/2016/05/30/data-models-and-word-size/ So I using this FFI definition... Libclang >> clang_getFileLocation__location: location file: file line: line column: column offset: offset self ffiCall: #(void clang_getFileLocation( CXSourceLocation location, CXFile *file, uint *line, uint *column, uint *offset)) with this application code... lineBuffer := FFIUInt32 newBuffer. columnBuffer := FFIUInt32 newBuffer. offsetBuffer := FFIUInt32 newBuffer. Libclang clang_getFileLocation__location: cxLocation file: cxFile line: lineBuffer column: columnBuffer offset: offsetBuffer. but to unwrap the buffer the following is not available... line := lineBuffer uint32At: 1. Other candidates are senders of #integerAt:size:signed: from which the only one dealing in 4 bytes is... ByteArray >> unsignedLongAt: byteOffset ^self integerAt: byteOffset size: 4 signed: false which (apart from 'ulong' being semantically different from the original 'uint' C definition) seems a poor cross-platform choice, since 'long' is the only integer type whose size that varies between 32-bit & 64-bit. * https://software.intel.com/en-us/articles/size-of-long-integer-type-on-different-architecture-and-os There is ByteArray >> platformUnsignedLongAt: but again the original C declaration was an uint, so it still feels wrong to use 'long' converter. The remaining option is directly using the lower level api, but its a bit ugly... lineBuffer := FFIUInt32 newBuffer. columnBuffer := FFIUInt32 newBuffer. offsetBuffer := FFIUInt32 newBuffer. Libclang clang_getFileLocation__location: cxLocation file: cxFile line: lineBuffer column: columnBuffer offset: offsetBuffer. line := lineBuffer integerAt: 1 size: 4 signed: false. column := columnBuffer integerAt: 1 size: 4 signed: false. offset := offsetBuffer integerAt: 1 size: 4 signed: false. ^{ line . column. offset } So are additional type converters required for ByteArray? Or alternatively, could FFIExternalType get an extra ByteArray instance variable that is usually nil, but is available when we want to deal concretely with an external type while keeping the type info associated, rather needing a raw ByteArray. For example... Object subclass: #FFIExternalType instanceVariableNames: 'pointerArity loader buffer' classVariableNames: '' package: 'UnifiedFFI-Types' FFIExternalType >> buffer ^ buffer ifNil: [ buffer := ByteArray new: (self externalTypeSize) ]. buffer pin. FFIExternalType >> dereference ^ self basicHandle: self buffer at: 1 then presumably I could do "something" like... line := FFIUInt32 new. column := FFIUInt32 new. offset := FFIUInt32 new. Libclang clang_getFileLocation__location: cxLocation file: cxFile line: line buffer column: column buffer offset: offset buffer. ^ { line value . column value . offset value} or maybe #bufferPointer would be better than #buffer I'm experimenting along this line, but I'm currently breaking my Image so I pause to seek feedback on the idea. cheers -ben |
Free forum by Nabble | Edit this page |