# HG changeset patch # User Tassilo Philipp # Date 1662651380 -7200 # Node ID 1d4f0f5164835a52eece455ccaf676ac5c037707 # Parent 87b5f5d7af1fc79037cee591dea18e8d138a64df man pages: - dyncall(3): removal of unnecessary whitespace - dyncallback(3): added many more examples diff -r 87b5f5d7af1f -r 1d4f0f516483 dyncall/dyncall.3 --- a/dyncall/dyncall.3 Thu Sep 08 16:15:52 2022 +0200 +++ b/dyncall/dyncall.3 Thu Sep 08 17:36:20 2022 +0200 @@ -294,14 +294,14 @@ .Nm library, this function would be called as follows: .Bd -literal -offset indent - double r; - DCCallVM* vm = dcNewCallVM(4096); - dcMode(vm, DC_CALL_C_DEFAULT); - dcReset(vm); - /* call: double sqrt(double x); */ - dcArgDouble(vm, 4.2373); - r = dcCallDouble(vm, (DCpointer)&sqrt); - dcFree(vm); +double r; +DCCallVM* vm = dcNewCallVM(4096); +dcMode(vm, DC_CALL_C_DEFAULT); +dcReset(vm); +/* call: double sqrt(double x); */ +dcArgDouble(vm, 4.2373); +r = dcCallDouble(vm, (DCpointer)&sqrt); +dcFree(vm); .Ed .Pp Note that the @@ -317,18 +317,18 @@ which requires a different initial mode, as well as a mode switch for the varargs part: .Bd -literal -offset indent - int n_written_chars, r; - /* initial callconv mode */ - dcMode(vm, DC_CALL_C_ELLIPSIS); - dcReset(vm); - /* int printf(const char * restrict format, ...); */ - dcArgPointer(vm, "my printf(%d) %s string%n"); - /* switch mode for varargs part */ - dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS); - dcArgInt(vm, 3); - dcArgPointer(vm, "format"); - dcArgPointer(vm, &n_written_chars); - r = dcCallInt(vm, (DCpointer)&printf); +int n_written_chars, r; +/* initial callconv mode */ +dcMode(vm, DC_CALL_C_ELLIPSIS); +dcReset(vm); +/* int printf(const char * restrict format, ...); */ +dcArgPointer(vm, "my printf(%d) %s string%n"); +/* switch mode for varargs part */ +dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS); +dcArgInt(vm, 3); +dcArgPointer(vm, "format"); +dcArgPointer(vm, &n_written_chars); +r = dcCallInt(vm, (DCpointer)&printf); .Ed .Pp .Ss C/trivial aggregates by-value @@ -341,8 +341,8 @@ to .Fn f : .Bd -literal -offset indent - struct S { char x[3]; double y; }; - void f(int, struct S); +struct S { char x[3]; double y; }; +void f(int, struct S); .Ed .Pp requires a @@ -351,20 +351,20 @@ .Sy struct S , and is called as follows: .Bd -literal -offset indent - struct S s = { { 56, -23, 0 }, -6.28 }; +struct S s = { { 56, -23, 0 }, -6.28 }; - DCaggr *a = dcNewAggr(2, sizeof(struct S)); - dcAggrField(a, DC_SIGCHAR_CHAR, offsetof(struct S, x), 3); - dcAggrField(a, DC_SIGCHAR_DOUBLE, offsetof(struct S, y), 1); - dcCloseAggr(a); +DCaggr *a = dcNewAggr(2, sizeof(struct S)); +dcAggrField(a, DC_SIGCHAR_CHAR, offsetof(struct S, x), 3); +dcAggrField(a, DC_SIGCHAR_DOUBLE, offsetof(struct S, y), 1); +dcCloseAggr(a); - dcMode(vm, DC_CALL_C_DEFAULT); - dcArgInt(vm, 999); - dcArgAggr(vm, a, &s); +dcMode(vm, DC_CALL_C_DEFAULT); +dcArgInt(vm, 999); +dcArgAggr(vm, a, &s); - dcCallVoid(vm, (DCpointer)&f); +dcCallVoid(vm, (DCpointer)&f); - dcFreeAggr(a); +dcFreeAggr(a); .Ed .Pp Let's look at an example returning @@ -373,7 +373,7 @@ .Sy struct S from function: .Bd -literal -offset indent - struct S g(int, short); +struct S g(int, short); .Ed .Pp Omitting creation of the @@ -381,23 +381,23 @@ .Ar *a description, for simplicity: .Bd -literal -offset indent - struct S s; +struct S s; - dcMode(vm, DC_CALL_C_DEFAULT); +dcMode(vm, DC_CALL_C_DEFAULT); - /* needed when returning aggrs by value, *before* pushing args */ - dcBeginCallAggr(vm, a); +/* needed when returning aggrs by value, *before* pushing args */ +dcBeginCallAggr(vm, a); - dcArgInt(vm, 9); - dcArgShort(vm, 7); +dcArgInt(vm, 9); +dcArgShort(vm, 7); - dcCallAggr(vm, (DCpointer)&g, a, &s); +dcCallAggr(vm, (DCpointer)&g, a, &s); .Ed .Ss C++ In our next example, let's look at calling a simple C++ method, with the method declaration being: .Bd -literal -offset indent - virtual void Klass::Method(float, int); +virtual void Klass::Method(float, int); .Ed .Pp To keep the example simple, let's assume we have a pointer to this virtual @@ -406,39 +406,39 @@ (e.g. grabbed from the instance's vtable), and a pointer to the instance in var .Ar thisptr : .Bd -literal -offset indent - /* thiscall calling convention */ - dcMode(vm, DC_CALL_C_DEFAULT_THIS); - dcReset(vm); - /* C++ methods use this-ptr as first/hidden argument */ - dcArgPointer(vm, thisptr); - dcArgFloat(vm, 2.3f); - dcArgInt(vm, -19); - dcCallVoid(vm, (DCpointer)mptr); +/* thiscall calling convention */ +dcMode(vm, DC_CALL_C_DEFAULT_THIS); +dcReset(vm); +/* C++ methods use this-ptr as first/hidden argument */ +dcArgPointer(vm, thisptr); +dcArgFloat(vm, 2.3f); +dcArgInt(vm, -19); +dcCallVoid(vm, (DCpointer)mptr); .Ed .Pp Extending the last example to a vararg method would need some more .Xr dcMode 3 calls. E.g.: .Bd -literal -offset indent - virtual void Klass::Method(float, int, ...); +virtual void Klass::Method(float, int, ...); .Ed .Pp would be called as follows: .Bd -literal -offset indent - /* thiscall calling convention (to pass this-ptr) */ - dcMode(vm, DC_CALL_C_DEFAULT_THIS); - dcReset(vm); - /* C++ methods use this-ptr as first/hidden argument */ - dcArgPointer(vm, thisptr); - /* fixed part of arguments */ - dcMode(vm, DC_CALL_C_ELLIPSIS); - dcArgFloat(vm, 2.3f); - dcArgInt(vm, -19); - /* variable part of arguments */ - dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS); - dcArgInt(vm, 7); - dcArgDouble(vm, 7.99); - dcCallVoid(vm, (DCpointer)mptr); +/* thiscall calling convention (to pass this-ptr) */ +dcMode(vm, DC_CALL_C_DEFAULT_THIS); +dcReset(vm); +/* C++ methods use this-ptr as first/hidden argument */ +dcArgPointer(vm, thisptr); +/* fixed part of arguments */ +dcMode(vm, DC_CALL_C_ELLIPSIS); +dcArgFloat(vm, 2.3f); +dcArgInt(vm, -19); +/* variable part of arguments */ +dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS); +dcArgInt(vm, 7); +dcArgDouble(vm, 7.99); +dcCallVoid(vm, (DCpointer)mptr); .Ed .Pp .Sh CONFORMING TO diff -r 87b5f5d7af1f -r 1d4f0f516483 dyncallback/dyncallback.3 --- a/dyncallback/dyncallback.3 Thu Sep 08 16:15:52 2022 +0200 +++ b/dyncallback/dyncallback.3 Thu Sep 08 17:36:20 2022 +0200 @@ -103,7 +103,7 @@ parameter, meaning it can only be used for callbacks that do not use any aggregate by value. .Pp -.Sy NOTE: +.Em NOTE : C++ non-trivial aggregates (check with the std::is_trivial type trait) do not use aggregate descriptions, so the respective pointers in the provided array must be NULL. See @@ -159,7 +159,7 @@ where .Ar target must point to memory large enough for the aggregate to be copied to, -.Sy iff +.Em iff the aggregate is trivial (see below for non-trivial C++ aggregates), in which case .Ar target is returned. @@ -190,12 +190,19 @@ point to (implicit, caller-provided) memory where the aggregate should be copied to. -.Sh EXAMPLE -Let's say, we want to create a callback object and call it. For simplicity, this -example will omit passing it as a function pointer to a function (e.g. compar -in qsort(), etc.) and demonstrate calling it, directly. First, we need to define -our callback handler - the following handler illustrates how to access the passed- -in arguments: +.Sh EXAMPLES +.Em Note : +for simplicity, none of the examples below do any error checking. Also, none of +them pass the callback object pointer as an argument to a function doing the +respective callback (e.g. +.Ar compar +in +.Xr qsort 3 , +etc.), but demonstrate calling it, directly, for clarity. +.Pp +Let's say, we want to create a callback object and call it. First, we need to +define our callback handler - the following handler illustrates how to access +the passed-in arguments, optional userdata, and how to return values: .Bd -literal -offset indent DCsigchar cbHandler(DCCallback* cb, DCArgs* args, @@ -216,21 +223,185 @@ } .Ed .Pp -Note that the return value of the handler is a signature character, not the -actual return value, itself. -Now, let's call it through a DCCallback object: +Note that the return value of the handler is a signature character, and not the +actual return value, itself. Now, let's call it through a +.Sy DCCallback +object: +.Bd -literal -offset indent +DCCallback* cb; +short result = 0; +int userdata = 1337; +cb = dcbNewCallback("ifsdl)s", &cbHandler, &userdata); + +/* call the callback object */ +result = ((short(*)(int, float, short, double, long long))cb) + (123, 23.f, 3, 1.82, 9909ll); + +dcbFreeCallback(cb); +.Ed +.Ss C/trivial aggregates by-value +Onto an example calling back a function which takes an aggregate +.Em "by value" +(note that this is only available on platforms where macro +.Dv DC__Feature_AggrByVal +is defined). E.g. with the following function +.Fn f +and +.Sy struct S : +.Bd -literal -offset indent +struct S { char x[3]; double y; }; +int f(struct S, float); +.Ed +.Pp +the callback handler would look like: +.Bd -literal -offset indent +DCsigchar cbHandler(DCCallback* cb, + DCArgs* args, + DCValue* result, + void* userdata) +{ + struct S arg1; + float arg2; + dcbArgAggr(args, (DCpointer)&arg1); + arg2 = dcbArgFloat(args); + + /* ... */ + + result->i = 1; + return 'i'; +} +.Ed +.Pp +and the callback object as well as the aggregate field/layout description are +set up (and the former called back) as follows: +.Bd -literal -offset indent +struct S s = { { 56, -23, 0 }, -6.28 }; +int result; + +DCCallback* cb; + +DCaggr *a = dcNewAggr(2, sizeof(struct S)); +dcAggrField(a, DC_SIGCHAR_CHAR, offsetof(struct S, x), 3); +dcAggrField(a, DC_SIGCHAR_DOUBLE, offsetof(struct S, y), 1); +dcCloseAggr(a); + +/* an array of DCaggr* must be passed as last arg, with one + * entry per 'A' signature character; we got only one, here + */ +cb = dcbNewCallback2("Af)v", &cbHandler, NULL, &a); + +/* call the callback object */ +result = ((int(*)(struct S, float))cb)(s, 42.f); + +dcbFreeCallback(cb); +dcFreeAggr(a); +.Ed +.Pp +Let's extend the last example, so that the callback function also returns +.Sy struct S +.Em "by value" . +The struct definition, function declaration and handler definition would be: .Bd -literal -offset indent - DCCallback* cb; - short result = 0; - int userdata = 1337; - cb = dcbNewCallback("ifsdl)s", &cbHandler, &userdata); +/* callback function decl */ +struct S f(struct S, float); + +struct S { char x[3]; double y; }; + +DCsigchar cbHandler(DCCallback* cb, + DCArgs* args, + DCValue* result, + void* userdata) +{ + struct S arg1, r; + float arg2; + dcbArgAggr(args, (DCpointer)&arg1); + arg2 = dcbArgFloat(args); + + /* ... */ + + /* use helper to write aggregate return value to result */ + dcbReturnAggr(args, result, (DCpointer)&r); + return 'A'; +} +.Ed +.Pp +.Pp +and the callback object as well as the aggregate field/layout descriptions are +set up (and the former called back) as follows: +.Bd -literal -offset indent +struct S s = { { 33, 29, -1 }, 6.8 }; +struct S result; + +DCCallback* cb; + +DCaggr *a = { dcNewAggr(2, sizeof(struct S)) }; +dcAggrField(a, DC_SIGCHAR_CHAR, offsetof(struct S, x), 3); +dcAggrField(a, DC_SIGCHAR_DOUBLE, offsetof(struct S, y), 1); +dcCloseAggr(a); + +/* an array of DCaggr* must be passed as last arg, with one + * entry per 'A' signature character + */ +cb = dcbNewCallback2("Af)A", &cbHandler, NULL, (DCaggr*[2]){a,a}); + +/* call the callback object */ +result = ((struct S(*)(struct S, float))cb)(s, 42.f); - /* call the callback object */ - result = ((short(*)(int, float, short, double, long long))cb) - (123, 23.f, 3, 1.82, 9909ll); +dcbFreeCallback(cb); +dcFreeAggr(a); +.Ed +.Ss C++ +In our next example, let's look at setting up a +.Sy DCCallback +object to call back a simple C++ method (illustrating the need to specify the +thiscall calling convention). If the class and method is declared as: +.Bd -literal -offset indent +class Klass { +public: + virtual void Method(float, int); +}; +.Ed +.Pp +the respective callback handler would be something along the lines of: +.Bd -literal -offset indent +DCsigchar cbHandler(DCCallback* cb, + DCArgs* args, + DCValue* result, + void* userdata) +{ + Klass* thisptr = (Klass*)dcbArgPointer(args); + float arg1 = dcbArgFloat(args); + int arg2 = dcbArgInt(args); + + /* ... */ - dcbFreeCallback(cb); + return 'v'; +} .Ed +.Pp +and the callback object would be used as follows: +.Bd -literal -offset indent +DCCallback* cb; +cb = dcbNewCallback("_*pfi)v", &cbHandler, NULL); + +/* HACK: this is a hack just for this example to force the compiler + * generating a thiscall, below (creates a fake vtable mimicking + * Klass, setting all of its possible entries to our callback handler; + */ +DCpointer fakeClass[sizeof(Klass)/sizeof(DCpointer)]; +for(int j=0; jMethod(8, 23.f); + +dcbFreeCallback(cb); +.Ed +.Pp +.Em NOTE : +In a real world scenario one would figure out the precise location of the vtable entry of +.Fn Klass::Method , +of course; the above example omits this for simplicity. .Sh CONFORMING TO The dyncallback library needs at least a c99 compiler with additional support for anonymous structs/unions (which were introduced officially in c11). Given