UFFI C basic types passed-by-reference for output

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

UFFI C basic types passed-by-reference for output

Ben Coman
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

Reply | Threaded
Open this post in threaded view
|

Re: UFFI C basic types passed-by-reference for output

EstebanLM


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


Reply | Threaded
Open this post in threaded view
|

Re: UFFI C basic types passed-by-reference for output

Ben Coman



On 15 November 2017 at 17:27, Esteban Lorenzano <[hidden email]> wrote:


> 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).

Thanks Esteban. That worked. However I needed a minor tweak...
   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__document: document
                         page_index: 0
                         width: widthBuffer
                         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"
        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))

Thanks for the heads up.

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: UFFI C basic types passed-by-reference for output

EstebanLM


On 15 Nov 2017, at 09:36, Ben Coman <[hidden email]> wrote:




On 15 November 2017 at 17:27, Esteban Lorenzano <[hidden email]> wrote:


> 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).

Thanks Esteban. That worked. However I needed a minor tweak...
   widthBuffer := ByteArray new: (FFIFloat64 new typeSize).
or...
   widthBuffer := ByteArray new: (FFIFloat64 externalTypeSize).


Now it would be nice to do...
   widthBuffer := FFIFloat64 newBuffer.

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 :)


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.



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.

because the ABI defines float64 as the right name, heh.
and yes, it was not my personal choice ;)


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?

maybe it is overkilling. Because also… where do you put the limit? (doubles, floats, longs, etc… ?) 

Esteban


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

Thanks for the heads up.

cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: UFFI C basic types passed-by-reference for output

Ben Coman


On 15 November 2017 at 20:47, Esteban Lorenzano <[hidden email]> wrote:


On 15 Nov 2017, at 09:36, Ben Coman <[hidden email]> wrote:




On 15 November 2017 at 17:27, Esteban Lorenzano <[hidden email]> wrote:


> 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).

Thanks Esteban. That worked. However I needed a minor tweak...
   widthBuffer := ByteArray new: (FFIFloat64 new typeSize).
or...
   widthBuffer := ByteArray new: (FFIFloat64 externalTypeSize).


Now it would be nice to do...
   widthBuffer := FFIFloat64 newBuffer.

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 :)


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.



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.

because the ABI defines float64 as the right name, heh.
and yes, it was not my personal choice ;)


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?

maybe it is overkilling. Because also… where do you put the limit? (doubles, floats, longs, etc… ?) 


Well, there are not so many... ??

cheers -ben 
Reply | Threaded
Open this post in threaded view
|

Re: UFFI C basic types passed-by-reference for output

Ben Coman
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