2

How i should create a (const char **) to pass it to a C function ?

Let say my const char ** is named prompts then:

user := 'User:' copyToHeap: #malloc:.    
pwd := 'Password:' copyToHeap: #malloc:.
prompts := (ByteArray new: 64) copyToHeap: #malloc:.
prompts copyAt: 0 from: (user referentAddress asByteArraySize: 32) size: 4 startingAt: 1.
prompts copyAt: 31 from: (pwd referentAddress asByteArraySize: 32) size: 4 startingAt: 1.

So prompts is an array of 64bits where the first 32bits are a pointer to user and the secods 32bits are a pointer to pwd.

But the C function is not working. In GemStone is working ok with:

prompts := CByteArray gcMalloc: 16.
user := CByteArray withAll: 'User:'.
pwd := CByteArray withAll: 'Password:'.
prompts uint64At: 0 put: user memoryAddress.
prompts uint64At: 8 put: pwd memoryAddress.
bruno bb
  • 152
  • 9
  • ByteArray are bytes (8 bits). ByteArray new: 64 is 64×8 bits... That's not the object that you need... – aka.nice Feb 20 '21 at 08:05
  • Yes, it should be ByteArray new: 8. And some bits operations to set address on the ByteArray. My VW is 32bits – bruno bb Feb 22 '21 at 12:12

2 Answers2

2

DLLCC offers some API very close to C. You need an array of two char pointers.

prompts := CIntegerType char pointerType gcMalloc: 2.

Then you can populate this array like this:

prompts at: 0 put: user.
prompts at: 1 put: pwd.

Note that the indices mimic C like prompts[0]=user; prompts[1]=pwd;.

Last thing, everything you malloc, you must then free, otherwise you'll get memory leaks.

That means that you shall better protect all this code with some

["your protected code here"]
    ensure: [prompts free. user free. pwd free]`

...or worse...

["your protected code here"]
    ensure:
        [prompts isNil ifFalse: [prompts free].
        "etc..."]`.

In early development, I suggest that you shall better use gcMalloc and gcMalloc:.

AFTER THOUGHTS

gcMalloc is maybe not a such good idea for userand pwd.

This is because prompts will get a copy of the address of memory contained in user and pwd objects: it will point to same memory zone, but will not point to the Smalltalk objects...

gcMalloc only monitor the garbage collection of Smalltalk objects. Hence, if Smalltalk objects are not more used, C heap might get freed prematurely despite some other objects point to the same C heap...

Example:

fillPrompts
    | user pwd prompts |
    user := 'User:' copyToHeap: #gcMalloc:.    
    pwd := 'Password:' copyToHeap: #gcMalloc:.
    prompts := CIntegerType char pointerType gcMalloc: 2.
    prompts at: 0 put: user.
    prompts at: 1 put: pwd.
    ^prompts

copyToHeap: creates a CPointer object. As long as the method is active, its context point to those objects (thru slots on the stack).
But after return of this method, there is not any object pointing to the CPointer objects.
If some garbage collection occur, their associated pointer to C heap will be freed.

But prompts still contain reference to already freed memory (the so called dangling pointers).

DLLCC being very close to C, one must adopt the same care as when writing C code... And double pointers is a source of bugs for the vast majority of C programmers.

aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • To be on the safe side of using #gcCalloc you should only use it locally to pass data to a C function. Try to avoid returning allocated data from a method. You’ll have to start worrying about correctly freeing the data and that’s typically harder that initially thought. To copy a String to the heap, use `gcCopyUnicodeStringToHeap` or a similar method where you can specify the encoding. – Karsten Feb 20 '21 at 09:33
  • 1
    @Karsten yes isolating such allocation in a method is not a good idea. It's just to illustrate the limits of gcMalloc. If none of the objects nor C Heap pointers survive to method activation, it's still OK to use gcMalloc with double pointers. But it's even better to know why. As for the encoding, I usually send #asByteArrayEncoding: before copying to heap. This way we pass what C expects: bytes... If C use ASCII and we have dumb constant string like in this example, it's not necessary though. – aka.nice Feb 20 '21 at 11:42
2

You shouldn’t work directly on the bytes. That doesn’t even make sense in C.

  1. create a struct with the two char* members, that’s easier to declare, to create and to handle.
  2. use #gcCalloc or #gcCopyToHeap in order to allocate memory on the heap that’s still automatically freed. Typically it’s safe to use these methods because you only need that memory inside a single method to transfer it to C. The assumption is that the c-function copies this memory itself in case it needs it later.
  3. you can use #memberAt:put: to assign members to the struct.
Karsten
  • 2,772
  • 17
  • 22
  • Yes you are right. But we have only a few cases where we can use VW C structs. Maybe after having everything running i will switch to VW C structs. For now there are only 2 cases other cases can be managed easily with basic pointers. – bruno bb Feb 22 '21 at 12:19