Repository: celix Updated Branches: refs/heads/feature/CELIX-237_rsa-ffi 387f7792d -> 7f3383247
CELIX-237: Added dyn_function.c for dynamically calling function and dynamically creating closure using a schema Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/7f338324 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/7f338324 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/7f338324 Branch: refs/heads/feature/CELIX-237_rsa-ffi Commit: 7f338324707f1100f25f7b1f3ca82f01e56663fc Parents: 387f779 Author: Pepijn Noltes <pepijnnol...@gmail.com> Authored: Wed Jun 17 13:46:18 2015 +0200 Committer: Pepijn Noltes <pepijnnol...@gmail.com> Committed: Wed Jun 17 13:46:18 2015 +0200 ---------------------------------------------------------------------- remote_services/dyn_type/CMakeLists.txt | 14 ++ remote_services/dyn_type/closure_tests.c | 98 ++++++++++++ remote_services/dyn_type/dyn_function.c | 209 ++++++++++++++++++++++++++ remote_services/dyn_type/dyn_function.h | 24 +++ remote_services/dyn_type/dyn_type.c | 26 +--- remote_services/dyn_type/dyn_type.h | 28 +++- remote_services/dyn_type/func_tests.c | 66 ++++++++ 7 files changed, 443 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/CMakeLists.txt b/remote_services/dyn_type/CMakeLists.txt index 8b5b78f..351bb7c 100644 --- a/remote_services/dyn_type/CMakeLists.txt +++ b/remote_services/dyn_type/CMakeLists.txt @@ -6,4 +6,18 @@ add_executable(struct_tests json_serializer.c ) +add_executable(func_tests + func_tests.c + dyn_type.c + dyn_function.c +) + +add_executable(closure_tests + closure_tests.c + dyn_type.c + dyn_function.c +) + target_link_libraries(struct_tests ${JANSSON_LIBRARIES} /lib64/libffi.so ) +target_link_libraries(func_tests ${JANSSON_LIBRARIES} /lib64/libffi.so ) +target_link_libraries(closure_tests ${JANSSON_LIBRARIES} /lib64/libffi.so ) http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/closure_tests.c ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/closure_tests.c b/remote_services/dyn_type/closure_tests.c new file mode 100644 index 0000000..39e4ca5 --- /dev/null +++ b/remote_services/dyn_type/closure_tests.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "dyn_function.h" + +#define EXAMPLE1_SCHEMA "example(III)I" +void example1_binding(void *userData, void* args[], void *out) { + printf("example1 closure called\n"); + int32_t a = *((int32_t *)args[0]); + int32_t b = *((int32_t *)args[1]); + int32_t c = *((int32_t *)args[2]); + int32_t *ret = out; + *ret = a + b + c; +} + +#define EXAMPLE2_SCHEMA "example(I{DDD val1 val2 val3}I)D" +struct example2_arg2 { + double val1; + double val2; + double val3; +}; +void example2_binding(void *userData, void* args[], void *out) { + printf("example2 closure called\n"); + int32_t a = *((int32_t *)args[0]); + struct example2_arg2 *b = *((struct example2_arg2 **)args[1]); + int32_t c = *((int32_t *)args[2]); + int32_t *ret = out; + *ret = a + b->val1 + b->val2 + b->val3 + c; +} + + +#define EXAMPLE3_SCHEMA "example(III){III sum max min}" +struct example3_ret { + int32_t sum; + int32_t max; + int32_t min; +}; + +void example3_binding(void *userData, void* args[], void *out) { + printf("exampleelosure called\n"); + int32_t a = *((int32_t *)args[0]); + int32_t b = *((int32_t *)args[1]); + int32_t c = *((int32_t *)args[2]); + struct example3_ret *result = calloc(1,sizeof(*result)); + result->sum = a + b + c; + result->min = a <= b ? a : b; + result->max = a >= b ? a : b; + result->min = result->min <= c ? result->min : c; + result->max = result->max >= c ? result->max : c; + + struct example3_ret **ret = out; + (*ret) = result; +} + +int main() { + dyn_closure_type *dynClosure = NULL; + int rc; + + rc = dynClosure_create(EXAMPLE1_SCHEMA, example1_binding, NULL, &dynClosure); + if (rc == 0) { + int32_t (*func)(int32_t a, int32_t b, int32_t c) = NULL; + dynClosure_getFnPointer(dynClosure, (void *)&func); + int32_t ret = func(2,3,4); + printf("Return value for example1 is %i\n", ret); + dynClosure_destroy(dynClosure); + } else { + printf("example1 failed\n"); + } + + dynClosure = NULL; + rc = dynClosure_create(EXAMPLE2_SCHEMA, example2_binding, NULL, &dynClosure); + if (rc == 0) { + double (*func)(int32_t a, struct example2_arg2 *b, int32_t c) = NULL; + dynClosure_getFnPointer(dynClosure, (void *)&func); + struct example2_arg2 b = { .val1 = 1.0, .val2 = 1.5, .val3 = 2.0 }; + double ret = func(2,&b,4); + printf("Return value for example2 is %f\n", ret); + dynClosure_destroy(dynClosure); + } else { + printf("example2 failed\n"); + } + + dynClosure = NULL; + rc = dynClosure_create(EXAMPLE3_SCHEMA, example3_binding, NULL, &dynClosure); + if (rc == 0) { + struct example3_ret * (*func)(int32_t a, int32_t b, int32_t c) = NULL; + dynClosure_getFnPointer(dynClosure, (void *)&func); + struct example3_ret *ret = func(2,8,4); + printf("Return value for example3 is {sum:%i, max:%i, min:%i}\n", ret->sum, ret->max, ret->min); + dynClosure_destroy(dynClosure); + free(ret); + } else { + printf("example2 failed\n"); + } +} http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/dyn_function.c ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/dyn_function.c b/remote_services/dyn_type/dyn_function.c new file mode 100644 index 0000000..a90bc64 --- /dev/null +++ b/remote_services/dyn_type/dyn_function.c @@ -0,0 +1,209 @@ +#include "dyn_function.h" + +#include <stdio.h> +#include <stdarg.h> +#include <strings.h> +#include <string.h> +#include <stdlib.h> + +#include <ffi.h> + +#include "dyn_type.h" + +struct _dyn_function_type { + dyn_type *arguments; + dyn_type *funcReturn; + void (*fn)(void); + ffi_cif cif; +}; + +struct _dyn_closure_type { + dyn_type *arguments; + dyn_type *funcReturn; + ffi_cif cif; + ffi_closure *ffiClosure; + void (*fn)(void); + void (*bind)(void *userData, void *args[], void *ret); + void *userData; +}; + + +static int dynFunction_initCif(ffi_cif *cif, dyn_type *arguments, dyn_type *funcReturn); +static int dynFunction_parseSchema(const char *schema, dyn_type **arguments, dyn_type **funcReturn); +static void dynClosure_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData); + +int dynFunction_create(const char *schema, void (*fn)(void), dyn_function_type **out) { + int status = 0; + dyn_function_type *dynFunc = NULL; + + dynFunc = calloc(1, sizeof(*dynFunc)); + + if (dynFunc != NULL) { + dynFunc->fn = fn; + status = dynFunction_parseSchema(schema, &dynFunc->arguments, &dynFunc->funcReturn); + if (status == 0) { + status = dynFunction_initCif(&dynFunc->cif, dynFunc->arguments, dynFunc->funcReturn); + } + } else { + status = 2; + } + + if (status == 0) { + *out = dynFunc; + } else { + if (status == 1) { + printf("Cannot parse func schema '%s'\n", schema); + } else { + printf("Cannot allocate memory for dyn function\n"); + } + } + return status; +} + +static int dynFunction_parseSchema(const char *schema, dyn_type **arguments, dyn_type **funcReturn) { + int status = 0; + //FIXME white space parsing is missing. Note move parsing to lex/bison? + //TODO move to parse schema function + char *startPos = index(schema, '('); + char *endPos = index(schema, ')'); + + if (startPos != NULL && endPos != NULL) { + int nrOfArgs = 0; + int len = endPos - startPos - 1; + + char *pos = startPos + 1; + while (*pos != ')') { + nrOfArgs += 1; + if (*pos == '{') { //embedded struct + while (*pos != '}' && *pos != '\0') { + pos += 1; + } + } + pos += 1; + } + + printf("nrOfAgrs is %i\n", nrOfArgs); + + //length needed is len args +2 -> {DD} + //+ 7 per arg e.g. {DD arg001 arg002} + //+ 1 x \0 + //--> len args + 3 + 7 * nrArgs + int argLength = 3 + len + 7 * nrOfArgs; + + char argSchema[argLength]; + argSchema[0] = '{'; + memcpy(argSchema + 1, startPos + 1, len); + pos += len + 1; + int i; + for (i = 0 ; i < nrOfArgs; i += 1) { + sprintf(argSchema + 1 + len + 7 * i, " arg%03d", i); + pos += 7; + } + argSchema[argLength -2] = '}'; + argSchema[argLength -1] = '\0'; + printf("arg schema is '%s'\n", argSchema); + + size_t returnLen = strlen(endPos + 1); + char returnSchema[returnLen + 1]; + memcpy(returnSchema, endPos +1, returnLen); + returnSchema[returnLen] = '\0'; + printf("return schema is '%s'\n", returnSchema); + + status = dynType_create(argSchema, arguments); + if (status == 0) { + status = dynType_create(returnSchema, funcReturn); + } + } else { + status = 1; + } + + return status; +} + +static int dynFunction_initCif(ffi_cif *cif, dyn_type *arguments, dyn_type *returnValue) { + int result = 0; + + int count = 0; + int i; + for (i = 0; arguments->complex.ffiType.elements[i] != NULL; i += 1) { + count += 1; + } + + ffi_type **args = arguments->complex.ffiType.elements; + ffi_type *returnType = &ffi_type_pointer; + if (returnType->type == DYN_TYPE_SIMPLE) { + returnType = returnValue->simple.ffiType; + } + + + int ffiResult = ffi_prep_cif(cif, FFI_DEFAULT_ABI, count, returnType, args); + if (ffiResult != FFI_OK) { + result = 1; + } + + return result; +} + +int dynFunction_destroy(dyn_function_type *dynFunc) { + int result = 0; + printf("TODO destroy dyn dync\n"); + return result; +} + +int dynFunction_call(dyn_function_type *dynFunc, void *returnValue, void **argValues) { + ffi_call(&dynFunc->cif, dynFunc->fn, returnValue, argValues); + return 0; +} + +static void dynClosure_ffiBind(ffi_cif *cif, void *ret, void *args[], void *userData) { + dyn_closure_type *dynClosure = userData; + dynClosure->bind(dynClosure->userData, args, ret); +} + +int dynClosure_create(const char *schema, void (*bind)(void *, void **, void*), void *userData, dyn_closure_type **out) { + int status = 0; + dyn_closure_type *dynClosure = calloc(1, sizeof(*dynClosure)); + if (dynClosure != NULL) { + dynClosure->bind = bind; + dynClosure->userData = userData; + status = dynFunction_parseSchema(schema, &dynClosure->arguments, &dynClosure->funcReturn); + if (status == 0) { + status = dynFunction_initCif(&dynClosure->cif, dynClosure->arguments, dynClosure->funcReturn); + if (status == 0) { + dynClosure->ffiClosure = ffi_closure_alloc(sizeof(ffi_closure), (void **)&dynClosure->fn); + if (dynClosure->ffiClosure != NULL) { + int rc = ffi_prep_closure_loc(dynClosure->ffiClosure, &dynClosure->cif, dynClosure_ffiBind, dynClosure, dynClosure->fn); + if (rc != FFI_OK) { + status = 1; + } + } else { + status = 2; + } + } + } + } else { + status = 2; + } + + if (status == 0) { + *out = dynClosure; + } + return status; +} + +int dynClosure_getFnPointer(dyn_closure_type *dynClosure, void (**fn)(void)) { + int status = 0; + if (dynClosure != NULL) { + (*fn) = dynClosure->fn; + } else { + status = 1; + } + return status; +} + + +int dynClosure_destroy(dyn_closure_type *dynClosure) { + int result = 0; + printf("TODO destroy closure\n"); + return result; +} http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/dyn_function.h ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/dyn_function.h b/remote_services/dyn_type/dyn_function.h new file mode 100644 index 0000000..21a5e0d --- /dev/null +++ b/remote_services/dyn_type/dyn_function.h @@ -0,0 +1,24 @@ +#ifndef __DYN_FUNCTION_H_ +#define __DYN_FUNCTION_H_ + +#include <ffi.h> +#include "dyn_type.h" + +/** + * Uses the following schema + * (Name)([Type]*)Type + * e.g add(DD)D or sum({[D[D setA setB})D + */ + +typedef struct _dyn_function_type dyn_function_type; +typedef struct _dyn_closure_type dyn_closure_type; + +int dynFunction_create(const char *schema, void (*fn)(void), dyn_function_type **dynFunc); +int dynFunction_destroy(dyn_function_type *dynFunc); +int dynFunction_call(dyn_function_type *dynFunc, void *returnValue, void **argValues); + +int dynClosure_create(const char *schema, void (*bind)(void *, void **, void*), void *userData, dyn_closure_type **out); +int dynClosure_getFnPointer(dyn_closure_type *dynClosure, void(**fn)(void)); +int dynClosure_destroy(dyn_closure_type *dynClosure); + +#endif http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/dyn_type.c ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/dyn_type.c b/remote_services/dyn_type/dyn_type.c index fe4c9bc..e7a2645 100644 --- a/remote_services/dyn_type/dyn_type.c +++ b/remote_services/dyn_type/dyn_type.c @@ -16,26 +16,6 @@ struct generic_sequence { void *buf; }; -struct _dyn_type { - int type; - union { - struct { - ffi_type *ffiType; - } simple; - - struct { - ffi_type ffiType; - char **names; - dyn_type **nested_types; - } complex; - - struct { - ffi_type seqStruct; - dyn_type *dynType; //set if sequence is of a complex type - ffi_type *simpleType; //set if sequence is of a simple type - } sequence; - }; -}; static char dynType_schemaType(dyn_type *dynType, ffi_type *ffiType); @@ -105,12 +85,15 @@ static int dynType_simple_create(char t, dyn_type **result) { ffi_type *ffiType = dynType_ffiType_for(t); dyn_type *simple = NULL; if (ffiType != NULL) { - dyn_type *simple = calloc(1,sizeof(*simple)); + simple = calloc(1,sizeof(*simple)); simple->type = DYN_TYPE_SIMPLE; simple->simple.ffiType = ffiType; } if (ffiType != NULL && simple != NULL) { *result = simple; + } else { + printf("Error creating simple dyn type for '%c'\n", t); + status = 1; } return status; } @@ -203,6 +186,7 @@ static int dynType_complex_create(char *schema, dyn_type **result) { memcpy(nested, schema + start + 1, len); nested[len] = '\0'; dyn_type *nestedType = NULL; + //TODO catch nested problem dynType_complex_create(nested, &nestedType); //TODO use status result type->complex.nested_types[index] = nestedType; type->complex.ffiType.elements[index] = &ffi_type_pointer; http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/dyn_type.h ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/dyn_type.h b/remote_services/dyn_type/dyn_type.h index 5877fdf..c7fabc5 100644 --- a/remote_services/dyn_type/dyn_type.h +++ b/remote_services/dyn_type/dyn_type.h @@ -31,7 +31,10 @@ * * * ComplexTypes - * {[Type]+} [(Name)(SPACE)]+ + * {[Type]+ [(Name)(SPACE)]+} + * + * NOTE MAYBE SUPPORT STRUCT BY VALUE -> + * <[Type]+ [(Name)(SPACE)]+> * * SequenceType * [(Type) @@ -49,6 +52,29 @@ typedef struct _dyn_type dyn_type; +/* TODO MOVE TO PRIVATE HEADER */ +struct _dyn_type { + int type; + union { + struct { + ffi_type *ffiType; + } simple; + + struct { + ffi_type ffiType; + char **names; + dyn_type **nested_types; + } complex; + + struct { + ffi_type seqStruct; + dyn_type *dynType; //set if sequence is of a complex type + ffi_type *simpleType; //set if sequence is of a simple type + } sequence; + }; +}; + + int dynType_create(const char *schema, dyn_type **type); int dynType_destroy(dyn_type *type); http://git-wip-us.apache.org/repos/asf/celix/blob/7f338324/remote_services/dyn_type/func_tests.c ---------------------------------------------------------------------- diff --git a/remote_services/dyn_type/func_tests.c b/remote_services/dyn_type/func_tests.c new file mode 100644 index 0000000..95811d7 --- /dev/null +++ b/remote_services/dyn_type/func_tests.c @@ -0,0 +1,66 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "dyn_function.h" + +#define EXAMPLE1_SCHEMA "example(III)I" +int32_t example1(int32_t a, int32_t b, int32_t c) { + printf("example1 called with a:%i, b:%i, c:%i\n", a, b, c); + return 1; +} + +#define EXAMPLE2_SCHEMA "example(I{IID val1 val2 val3}D)D" +struct example2_arg { + int32_t val1; + int32_t val2; + double val3; +}; + +double example2(int32_t arg1, struct example2_arg *arg2, double arg3) { + printf("example2 called with arg1:%i, arg2:{val1:%i, val2:%i, val3:%f}, arg3:%f\n", arg1, arg2->val1, arg2->val2, arg2->val3, arg3); + return 2.2; +} + + +int main() { + dyn_function_type *dynFunc = NULL; + int rc; + + rc = dynFunction_create(EXAMPLE1_SCHEMA, (void *)example1, &dynFunc); + if (rc == 0 ) { + int32_t a = 2; + int32_t b = 4; + int32_t c = 8; + void *values[3]; + int32_t rVal = 0; + values[0] = &a; + values[1] = &b; + values[2] = &c; + dynFunction_call(dynFunc, &rVal, values); + dynFunction_destroy(dynFunc); + dynFunc = NULL; + } else { + printf("Example 1 failed\n"); + } + + rc = dynFunction_create(EXAMPLE2_SCHEMA, (void *)example2, &dynFunc); + if (rc == 0 ) { + int32_t arg1 = 2; + struct example2_arg complexVal = { .val1 = 2, .val2 = 3, .val3 = 4.1 }; + struct example2_arg *arg2 = &complexVal; + double arg3 = 8.1; + double returnVal = 0; + void *values[3]; + values[0] = &arg1; + values[1] = &arg2; + values[2] = &arg3; + dynFunction_call(dynFunc, &returnVal, values); + dynFunction_destroy(dynFunc); + dynFunc = NULL; + } else { + printf("Example 3 failed\n"); + } +}