Igor Marnat wrote:
MG> It is, but I would not recommend to do so. Instead, I'd recommend to just MG> port the functions from pthread.h (i.e. give them an "external" declared MG> header in your unit) and handle the pthread_mutex_t just as a pointer. It is really the way to go, I'll use it. But just I'm curious: how can I export complex datatype from C units since it is possible?
There are several ways to do so.
If you want to port, for example, a C struct, the simplest (but also most error-prone) way is to re-model it 1:1 as a Pascal record and insert it in every function declaration o.s. where it appears. E.g. if there is a struct
typedef struct { int i; double x; char *name; } foo;
and a function
void bar(foo a_foo);
you can "translate" it in your own Pascal sources by defining something like
type tFoo = record i: integer; x: double; name: CString; end;
Procedure bar(aFoo: tFoo); attribute (name = 'bar'); external;
This is an easy way, but it relies on the Pascal record and its member variables having exactly the same size as the C struct and its members. So this way will work as long as 1.) the primitive data types like integer, double etc. are really equivalent to their C siblings and 2.) the definition of struct foo isn't changed at some point when the used C API changes. But if only they change the sequence of two member variables for whatever reason, you're in trouble.
To avoid this, I usually encapsulate the struct as an object instead of a record:
type tFoo = object public Constructor init; Destructor fini; virtual; Function i: integer; Function x: double; Function name: CString; private fNativeFoo: pointer; end;
...
Function cCreateFoo: pointer; attribute (name = 'c_create_foo'); external; Procedure cFreeFoo(aFoo: pointer); attribute (name = 'c_free_foo'); external; Function cFooGetI(aFoo: pointer); attribute (name = 'c_foo_get_i'); external; ...
Constructor tFoo.init; begin fNativeFoo:= cCreateFoo; end;
Destructor tFoo.fini; begin cFreeFoo(fNativeFoo); end;
Function tFoo.i = result: integer; begin result:= cFooGetI(fNativeFoo); end;
...
You'll need to write an additional C file then to define your external functions:
foo* c_create_foo () { return new foo; }
void c_free_foo(foo *a_foo) { free(a_foo); }
int c_foo_get_i(foo *a_foo) { return a_foo -> i; }
...
This is, at a first glance, a lot more work than just "translating" the struct, but you can always be sure that your object remains a valid port of the struct, no matter what member variables they add in the API afterwards and where.
So far for structs. ;-) As for enumeration types, I would always port them as their Pascal equivalent, i.e. if you've got something like
typedef enum {foo, bar, baz, ...} pruzzel;
just make it a
type pruzzel = (foo, bar, baz, ...);
Adapting this one to a possible API change is a lot less work than e.g. with structs. (And it usually occurs far less frequently.)
As for all other complex C data types (like, e.g. pthreads or mutexes), I'd always work with pointers to them, treat them as untypified pointers in the Pascal sources and implement the last access layer in C.
MG> However, I'm currently working on a (pthread-based) standard unit for GPC MG> multithreading, so if you aren't in a hurry, maybe I can offer you a MG> solution within a few weeks. :-) I'll be waiting for it and I'll be beta-tester surely. Shall we know about its readiness from mailing-list?
Yes, I'll announce when it's ready so far. But as I said, it'll certainly take a few weeks. The unit will be part of a larger GPC programming framework, and there's quite some foundation work to be done in some other units before I can go on to the thread unit.
Best,
Markus