I am working with the bindings to MLT. You can create a number of producers: newProducer. Once used you would destroy them with: close. So the pattern needed would be something like:
p1 = newProducer()
p2 = newProducer()
...
...
p1.close()
p2.close()
I am not sure about the proper way to handle this. I'd like to do this automatically (I understand this is how GC work).
Any recomendation?
First thanks for the link. It is obviously what I need.
Regarding how to use it, it is a bit complex for me (occasional hobby developer). Do I need to implement the 4 hooks?
I was trying just to do simply:
proc `=destroy`[T:Producer](x: var T) =
x.close()
which seems to be wrong.=destroy is only for object types, so your Producer would need to be something like
type
ProducerS{.importc.} = object
Producer = object
p: ptr ProducerS
memory management is complex, as is interfacing with C! nil pointers are everywhere and they all hate you.
have a play with =destroy,`=copy` and =sink for a bit until you grok what's going on, but for what you're doing you probably only need to implement =destroy, the default =sink is probably fine.
if copying a producer is allowed, the default =copy would probably be fine, too but I think you'll want to forbid copying with {.error.}, just a hunch from seeing mlt_service_close( &self->parent ); in the destructor. idk.
have a play with this and let me know if anything makes more sense XD
{.emit:"""
#include <stdio.h>
#include <string.h>
#include <stdint.h>
struct producer_s{
int32_t value;
void (* close)(void*);
void *close_object;
};
typedef struct producer_s* producer;
producer producer_new(){
producer self = malloc(sizeof(struct producer_s));
printf(" c_new\n");
return self;
}
void producer_close(producer self){
if (self == NULL) {
printf("c_close NULL\n");
} else{
if (self->close != NULL){
self->close(self->close_object);
}
printf(" c_close %d\n",self->value);
free(self);
}
}
""".}
type
ProducerS{.importc:"struct producer_s".} = object
value:int32
close: proc(p:pointer){.nimcall.}
close_object: pointer
Producer = object
self: ptr ProducerS
proc `$`(p:Producer):string = (if p.self.isNil: "nil" else: $p.self.value)
proc newProducer(val:int32):Producer =
proc c_producer_new():ptr ProducerS {.importc:"producer_new",cdecl.}
result.self = c_producer_new()
result.self.value = val
proc `=destroy`(p:var Producer) =
proc c_producer_close(p:ptr ProducerS){.importc:"producer_close",cdecl.}
if not p.self.isNil:
p.self.close = nil
c_producer_close(p.self)
else:
echo "=destroy nil"
proc bar(x: sink Producer) =
echo "bar with producer ", x
import sugar
var three:Producer
block:
echo "creating producers"
var prods = collect(for i in 0'i32..5: newProducer(i))
echo "\nmoving into three"
three = move(prods[3])
echo '\n',prods,'\n'
bar move(prods[4])
echo '\n',prods,'\n'
bar prods[5]
echo '\n',prods,'\n'
echo "leaving block"
echo "\noutside block"
Easiest is usually to define =copy as .error, provide a destructor that doesn't forget to check for x.res != nil (the "was moved from?" check) and let the compiler use its own notion of =sink.
We might want to tweak the spec that if you only overwrite =destroy that then =copy is an error -- it almost never does the correct thing.