From 722737d60c6de88643b721ee0de0fe7204fa6854 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sun, 5 Nov 2023 20:30:11 +0100 Subject: [PATCH 01/16] Add M_QUAD internal macros --- m-core.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/m-core.h b/m-core.h index 2b3b0423..ddad841d 100644 --- a/m-core.h +++ b/m-core.h @@ -1158,6 +1158,12 @@ M_BEGIN_PROTECTED_CODE #define M_TRIPLE_2(a,b,c) b #define M_TRIPLE_3(a,b,c) c +/* Same for quad */ +#define M_QUAD_1(a,b,c,d) a +#define M_QUAD_2(a,b,c,d) b +#define M_QUAD_3(a,b,c,d) c +#define M_QUAD_4(a,b,c,d) d + /* Skip the Nth first arguments of a VA_ARGS Generated by: for i in $(seq 0 52) ; do printf "#define M_SKIPI_%d(" $i ; for j in $(seq 1 $i) ; do printf "_%d, " $j ; done ; printf "...) __VA_ARGS__\n"; done From 53c35a22d700b03cc7a75cde6473796a48f354e5 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Wed, 1 Nov 2023 11:10:36 +0100 Subject: [PATCH 02/16] First version of Generic header --- Makefile | 2 +- m-core.h | 2 + m-generic.h | 254 ++++++++++++++++++++++++++++++++++++++++++ tests/test-mgeneric.c | 116 +++++++++++++++++++ 4 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 m-generic.h create mode 100644 tests/test-mgeneric.c diff --git a/Makefile b/Makefile index 821552f2..a8481a92 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ PACKAGE=m_lib-$(VERSION) VERSION=0.7.1 # Define the contain of the distribution tarball -HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h +HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-thread.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h DOC1=LICENSE README.md DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png doc/bench-array-log.png doc/bench-array.png doc/bench-list-log.png doc/bench-list.png doc/bench-oset-log.png doc/bench-oset.png doc/bench-umap-log.png doc/bench-umap.png doc/cc.sh EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex11-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c example/ex-no-stdio.c example/ex11-count-lines.c example/ex11-json-net.c example/ex11-rbtree02.c example/ex-curl.c example/ex-dict05.c example/ex-dict06.c example/exm-lib.c example/exm-main.c example/exn-lib.c example/exn-main.c example/exm-header.h example/exn-header.h diff --git a/m-core.h b/m-core.h index ddad841d..9fb291f0 100644 --- a/m-core.h +++ b/m-core.h @@ -1459,6 +1459,8 @@ M_BEGIN_PROTECTED_CODE #define M_PATTERN_TYPE_TYPE , #define M_PATTERN_SUBTYPE_SUBTYPE , #define M_PATTERN_IT_TYPE_IT_TYPE , +#define M_PATTERN_KEY_TYPE_KEY_TYPE , +#define M_PATTERN_VALUE_TYPE_VALUE_TYPE , #define M_PATTERN_M_UNDERFLOW_M_UNDERFLOW , #define M_PATTERN_M_OVERFLOW_M_OVERFLOW , #define M_PATTERN_M_OVERFLOW_M_OVERFLOW , diff --git a/m-generic.h b/m-generic.h new file mode 100644 index 00000000..802f8531 --- /dev/null +++ b/m-generic.h @@ -0,0 +1,254 @@ +/* + * M*LIB - Generic module + * + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef MSTARLIB_GENERIC_H +#define MSTARLIB_GENERIC_H + +#include "m-core.h" + +#ifdef _MSC_VER +#define m_typeof(x) decltype(x) +#elif defined(__GNUC__) +#define m_typeof(x) __typeof__(x) +#else +// Definition valid for C23, TCC +#define m_typeof(x) typeof(x) +#endif + + +// Instead of using expensive M_SEQ(1,50), precompute it: +#define M_G3N_SEQ_INT \ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \ + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, \ + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 + +/* 3 levels of indirection are neeeded: + * One level is the organisation. + * One level is the component of the organisation. + * One level is the oplist within the component of the organisation + Return the list of unevaled oplist that are registered accorss all organisations, all components. + MLIB Organisation is the default organisation and is always registered + Usage Domain: Nb organizations * Nb components * Nb oplists < M_MAX_NB_ARGUMENT +*/ +#define M_G3N_REGISTERED_ITEMS() \ + M_CROSS_MAP( M_G3N_IS_PRESENT3, ( comp M_CROSS_MAP(M_G3N_IS_PRESENT2, (MLIB M_MAP(M_G3N_IS_PRESENT, M_G3N_SEQ_INT)), ( M_G3N_SEQ_INT ) ) ), ( M_G3N_SEQ_INT ) ) + +#define M_G3N_IS_PRESENT(num) \ + M_IF(M_PARENTHESIS_P(M_C(M_GENERIC_ORG_, num)() ))(M_DEFERRED_COMMA M_ID M_C(M_GENERIC_ORG_, num)(), ) + +#define M_G3N_IS_PRESENT2(el1, num) \ + M_IF(M_PARENTHESIS_P(M_C4(M_GENERIC_ORG_, el1, _COMP_, num)()))(M_DEFERRED_COMMA M_APPLY(M_C3, el1, _COMP_, M_ID M_C4(M_GENERIC_ORG_, el1, _COMP_, num)()), ) + +#define M_G3N_IS_PRESENT3(el1, num) \ + M_IF(M_OPLIST_P(M_C4(M_GENERIC_ORG_, el1, _OPLIST_, num)()))(M_DEFERRED_COMMA M_C4(M_GENERIC_ORG_, el1, _OPLIST_, num), ) + +// Dummy unused structure. Just to fill in default case of the _Generic +struct m_g3neric_dummys; + +// Synonymous for some basic pointers doesn't work well with prepropressing code +typedef FILE *m_g3n_file; +typedef const char *m_g3n_cstring; +typedef const char **m_g3n_cstring_end; + +#define M_AS_LINKED_TYPE(typex, x, typey, y) _Generic(((void)0,(x)), typex: (y), default: (typey) {0}) + +#define M_G3N_IT_TYPE(oplist) m_typeof( ((void)0, (M_GET_IT_TYPE oplist()){0})) + +// Translate type' into the container type if type == TYPE, IT_TYPE into its iterator +// KEY_TYPE, VALUE_TYPE, SUBTYPE into the associated type in the oplist, +// or keep 'type' otherwise +#define M_G3N_TYPE(type, oplist) M_GET_METHOD(type, type, M_OPFLAT oplist ()) + +// Call the OPERATOR call of the oplist registered to the variable 'x' +// which takes one argument. +#define M_G3N_CALL_1(op, x) \ + _Generic( ((void)0, (x)), \ + M_MAP2(M_G3N_CALL_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void)0) +#define M_G3N_CALL_1_func(x, oplist) \ + M_G3N_CALL_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) +#define M_G3N_CALL_1_func_test(gentype, op, x, oplist) \ + M_IF_METHOD(op, oplist())(M_G3N_CALL_1_func_expand, M_EAT)(gentype, op, x, oplist) +#define M_G3N_CALL_1_func_expand(gentype, op, x, oplist) \ + gentype: M_C(M_CALL_, op)(oplist(), M_AS_TYPE(gentype, x)), \ + const gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(const gentype, x, gentype, x)), + +// Call the OPERATOR call of the oplist registered to the variable 'svar' +// which takes 2 arguments of the given type +#define M_G3N_CALL_2(op, svar, type1, var1, type2, var2) \ + _Generic( ((void)0, (svar)), \ + M_MAP2(M_G3N_CALL_2_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) +#define M_G3N_CALL_2_func(x, oplist) \ + M_APPLY(M_G3N_CALL_2_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) +#define M_G3N_CALL_2_func_test(gentype, op, svar, type1, var1, type2, var2, oplist) \ + M_IF_METHOD(op, oplist())(M_G3N_CALL_2_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, oplist) +#define M_G3N_CALL_2_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ + gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(gentype, svar, type1, var1), M_AS_LINKED_TYPE(gentype, svar, type2, var2) ), \ + const gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(const gentype, svar, type1, var1), M_AS_LINKED_TYPE(const gentype, svar, type2, var2) ), + +// Call the OPERATOR call of the oplist registered to the variable 'svar' +// which takes 3 arguments of the given type +#define M_G3N_CALL_3(op, svar, type1, var1, type2, var2, type3, var3) \ + _Generic( ((void)0, (svar)), \ + M_MAP2(M_G3N_CALL_3_func, (op, svar, type1, var1, type2, var2, type3, var3) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) +#define M_G3N_CALL_3_func(x, oplist) \ + M_APPLY(M_G3N_CALL_3_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) +#define M_G3N_CALL_3_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, oplist) \ + M_IF_METHOD(op, oplist())(M_G3N_CALL_3_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, M_G3N_TYPE(type3, oplist), var3, oplist) +#define M_G3N_CALL_3_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ + gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(gentype, svar, type1, var1), M_AS_LINKED_TYPE(gentype, svar, type2, var2) ), \ + const gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(const gentype, svar, type1, var1), M_AS_LINKED_TYPE(const gentype, svar, type2, var2) ), + +// Call the OPERATOR call of the oplist registered to the variable 'svar' +// which takes 4 arguments of the given type +#define M_G3N_CALL_4(op, svar, type1, var1, type2, var2, type3, var3, type4, var4) \ + _Generic( ((void)0, (svar)), \ + M_MAP2(M_G3N_CALL_4_func, (op, svar, type1, var1, type2, var2, type3, var3, type4, var4) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) +#define M_G3N_CALL_4_func(x, oplist) \ + M_APPLY(M_G3N_CALL_4_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) +#define M_G3N_CALL_4_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, type4, var4, oplist) \ + M_IF_METHOD(op, oplist())(M_G3N_CALL_4_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, M_G3N_TYPE(type3, oplist), var3, M_G3N_TYPE(type4, oplist), var4, oplist) +#define M_G3N_CALL_4_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ + gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(gentype, svar, type1, var1), M_AS_LINKED_TYPE(gentype, svar, type2, var2) ), \ + const gentype: M_C(M_CALL_, op)(oplist(), M_AS_LINKED_TYPE(const gentype, svar, type1, var1), M_AS_LINKED_TYPE(const gentype, svar, type2, var2) ), + +// Call the OPERATOR call of the oplist registered to the variable 'x' +// which takes one argument, it_type +#define M_G3N_CALL_1i(op, x) \ + _Generic( ((void)0, (x)), \ + M_MAP2(M_G3N_CALL_1i_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void)0) +#define M_G3N_CALL_1i_func(x, oplist) \ + M_G3N_CALL_1_func_test(M_G3N_IT_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) + +// Call the OPERATOR call of the oplist registered to the variable 'svar' +// which takes 2 arguments of the given it_type +#define M_G3N_CALL_2i(op, svar, type1, var1, type2, var2) \ + _Generic( ((void)0, (svar)), \ + M_MAP2(M_G3N_CALL_2i_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) +#define M_G3N_CALL_2i_func(x, oplist) \ + M_APPLY(M_G3N_CALL_2_func_test, M_G3N_IT_TYPE(oplist), M_ID x, oplist) + +// Create a variable of type OPERATOR call of the oplist registered to the variable 'x' +#define M_G3N_TYPE_1(op, x) \ + _Generic( ((void)0, (x)), \ + M_MAP2(M_G3N_TYPE_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void)0) +#define M_G3N_TYPE_1_func(x, oplist) \ + M_G3N_TYPE_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) +#define M_G3N_TYPE_1_func_test(gentype, op, x, oplist) \ + M_IF_METHOD(op, oplist())(M_G3N_TYPE_1_func_expand, M_EAT)(gentype, op, x, oplist) +#define M_G3N_TYPE_1_func_expand(gentype, op, x, oplist) \ + gentype: (M_G3N_TYPE(op, oplist)){0}, \ + const gentype: (M_G3N_TYPE(op, oplist)){0}, + + +// Define the generic macro-functions +// TODO: m_ prefix? +#define init(x) M_G3N_CALL_1(INIT, x) +#define init_set(x, y) M_G3N_CALL_2(INIT_SET, x, TYPE, x, TYPE, y) +#define init_move(x, y) M_G3N_CALL_2(INIT_MOVE, x, TYPE, x, TYPE, y) +#define move(x, y) M_G3N_CALL_2(MOVE, x, TYPE, x, TYPE, y) +#define set(x, y) M_G3N_CALL_2(SET, x, TYPE, x , TYPE, y) +#define clear(x) M_G3N_CALL_1(CLEAR, x) +#define swap(x, y) M_G3N_CALL_2(SWAP, x, TYPE, x , TYPE, y) +#define hash(x) M_G3N_CALL_1(HASH, x) +#define equal(x, y) M_G3N_CALL_2(EQUAL, x, TYPE, x , TYPE, y) +#define cmp(x, y) M_G3N_CALL_2(CMP, x, TYPE, x , TYPE, y) +#define sort(x) M_G3N_CALL_1(SORT, x) +#define splice_back(d, s, i) M_G3N_CALL_3(SPLICE_BACK, d, TYPE, d, TYPE, s, IT_TYPE, i) +#define splice_at(d, id, s, is) M_G3N_CALL_4(SPLICE_BACK, d, TYPE, d, IT_TYPE, id, TYPE, s, IT_TYPE, is) +#define it_type(x) m_typeof(M_G3N_TYPE_1(IT_TYPE, x)) +#define sub_type(x) m_typeof(M_G3N_TYPE_1(SUBTYPE, x)) +#define key_type(x) m_typeof(M_G3N_TYPE_1(KEY_TYPE, x)) +#define value_type(x) m_typeof(M_G3N_TYPE_1(VALUE_TYPE, x)) +#define it_first(x, y) M_G3N_CALL_2(IT_FIRST, y, IT_TYPE, x, TYPE, y) +#define it_last(x, y) M_G3N_CALL_i2(IT_LAST, y, IT_TYPE, x, TYPE, y) +#define it_end(x, y) M_G3N_CALL_i2(IT_END, y, IT_TYPE, x, TYPE, y) +#define it_set(d, s) M_G3N_CALL_2i(IT_SET, d, IT_TYPE, d, IT_TYPE, s) +#define it_end_p(i) M_G3N_CALL_1i(IT_END_P, i) +#define it_last_p(i) M_G3N_CALL_1i(IT_LAST_P, i) +#define it_next(i) M_G3N_CALL_1i(IT_NEXT, i) +#define it_previous(i) M_G3N_CALL_1i(IT_PREVIOUS, i) +#define it_ref(i) M_G3N_CALL_1i(IT_REF, i) +#define it_cref(i) M_G3N_CALL_1i(IT_CREF, i) +#define it_equal(x, y) M_G3N_CALL_2i(IT_EQUAL, x, IT_TYPE, x, IT_TYPE, y) +#define it_insert(c, i, o) M_G2N_CALL_3(IT_INSERT, c, TYPE, c, IT_TYPE, i, SUBTYPE, o) +#define it_remove(c, i) M_G3N_CALL_2(IT_REMOVE, c, TYPE, c, IT_TYPE, i) +#define empty_p(x) M_G3N_CALL_1(EMPTY_P, x) +#define add(x, y) M_G3N_CALL_2(ADD, x, TYPE, x, TYPE, y) +#define sub(x, y) M_G3N_CALL_2(SUB, x, TYPE, x, TYPE, y) +#define mul(x, y) M_G3N_CALL_2(MUL, x, TYPE, x, TYPE, y) +#define div(x, y) M_G3N_CALL_2(DIV, x, TYPE, x, TYPE, y) +#define reset(x) M_G3N_CALL_1(RESET, x) +#define get(x, y) M_G3N_CALL_2(GET_KEY, x, TYPE, x, KEY_TYPE, y) +#define set_at(x, y, z) M_G3N_CALL_3(SET_KEY, x, TYPE, x, KEY_TYPE, y, VALUE_TYPE, z) +#define safe_get(x, y) M_G3N_CALL_2(SAFE_GET_KEY, x, TYPE, x, KEY_TYPE, y) +#define erase(x, y) M_G3N_CALL_2(ERASE, x, TYPE, x, KEY_TYPE, y) +#define get_size(x) M_G3N_CALL_1(GET_SIZE, x) +#define push(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) +#define pop(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) +#define push_move(x, y) M_G3N_CALL_2(PUSH_MOVE, x, TYPE, x, SUBTYPE *, y) +#define pop_move(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE*, y) +#define reverse(x) M_G3N_CALL_1(REVERSE, x) +#define get_str(s, c, b) M_G3N_CALL_3(GET_STR, c, string_t, s, TYPE, c, bool, b) +#define parse_str(c, s, e) M_G3N_CALL_3(PARSE_STR, c, TYPE, c, m_g3n_cstring, s, m_g3n_cstring_end, e) +#define out_str(x, y) M_G3N_CALL_2(OUT_STR, y, m_g3n_file, x, TYPE, y) +#define in_str(x, y) M_G3N_CALL_2(IN_STR, x, TYPE, x, m_g3n_file, y) +#define out_serial(x, y) M_G3N_CALL_2(OUT_SERIAL, y, m_serial_write_t, x, TYPE, y) +#define in_serial(x, y) M_G3N_CALL_2(IN_SERIAL, x, TYPE, x, m_serial_read_t, y) + +// for each +#define each(item, container) \ + M_G3N_EACHI(item, container, M_C(m_local_it_, __LINE__), M_C(local_pass_, __LINE__)) + +#define M_G3N_EACHI(item, container, l_it, l_pass) \ + (bool l_pass = true; l_pass; l_pass = false) \ + for(sub_type(container) *item; l_pass ; l_pass = false) \ + for(it_type(container) l_it; l_pass ; l_pass = false) \ + for(it_first(l_it, container) ; \ + !it_end_p(l_it) \ + && (item = it_ref(l_it), true) ; \ + it_next(l_it)) + +// TODO: init_with ? How to handle the different type of parameters ? +// TODO: emplace ? +// TODO: Integrate in M_PRINT ? +// TODO: How to handle -Wunreachable-code-generic-assoc which is enabled automatically in CLANG 15? +// Generate warning: due to lvalue conversion of the controlling expression, association of type 'const float' will never be selected because it is qualified [-Wunreachable-code-generic-assoc] +// Pragma if CLANG ? + +/* User code has to register oplists : + +#define M_GENERIC_ORG_2() (USER) +#define M_GENERIC_ORG_USER_COMP_1() (CORE) +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_6() FLT1 +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 +*/ + +#endif diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c new file mode 100644 index 00000000..8dd98b42 --- /dev/null +++ b/tests/test-mgeneric.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017-2023, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include "coverage.h" + +#include "m-string.h" +#include "m-array.h" +#include "m-generic.h" + +// Generic is not supported if not C11 +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + +ARRAY_DEF(array_int, int) +#define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST) + +static void h(string_t x) +{ + string_set_str(x, "TEST STRING"); +} + +const string_t gx; + +#define STR1 M_OPEXTEND(STRING_OPLIST, GENTYPE(struct m_string_s *), PUSH(m_string_push_u)) +#define FLT1 (GENTYPE(float), TYPE(float), INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ + CLEAR(M_NOTHING_DEFAULT) ) +#define ARRAY1 M_OPEXTEND(M_OPL_array_int_t(), GENTYPE(struct array_int_s *)) + +//#define M_GENERIC_ORG_MLIB_COMP_1() (CORE) +//#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_1() STR1 +//#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_5() FLT1 + +#define M_GENERIC_ORG_2() (USER) +#define M_GENERIC_ORG_USER_COMP_1() (CORE) +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_6() FLT1 +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 + +#define M_GENERIC_ORG_3() (ARRAY) +#define M_GENERIC_ORG_ARRAY_COMP_5() (INT) +#define M_GENERIC_ORG_ARRAY_COMP_INT_OPLIST_6() ARRAY1 + +static bool test_empty(const string_t p) +{ + return empty_p(p); +} + +static void test_string(string_t p) +{ + string_t s , d; + + init(s); + h(s); + init_set(d, s); + h(d); + init_set(p, s); + bool b = test_empty(p); + assert(!b); + push(p, 'c'); + out_str(stdout, p); + clear(s); + clear(d); + + float f; + init(f); + clear(f); + + clear(p); +} + +static void test_array(void) +{ + M_LET(a, array_int_t) { + push(a, 14); + push(a, 15); + out_str(stdout, a); + for each(it, a) { + printf("=%d, ", *it); + } + } +} + +int main(void) +{ + string_t p; + test_string(p); + test_array(); + exit(0); +} + +#else +int main(void) +{ + exit(0); +} +#endif From 1d86e51f4ab850323973d893d35fe88649b13085 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 9 Dec 2023 11:31:31 +0100 Subject: [PATCH 03/16] Improve test suite --- tests/test-mgeneric.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index 8dd98b42..d96207b7 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -27,6 +27,7 @@ #include "m-string.h" #include "m-array.h" +#include "m-list.h" #include "m-generic.h" // Generic is not supported if not C11 @@ -35,6 +36,9 @@ ARRAY_DEF(array_int, int) #define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST) +LIST_DEF(list_int, int) +#define M_OPL_list_int_t() LIST_OPLIST(list_int, M_BASIC_OPLIST) + static void h(string_t x) { string_set_str(x, "TEST STRING"); @@ -46,6 +50,7 @@ const string_t gx; #define FLT1 (GENTYPE(float), TYPE(float), INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ CLEAR(M_NOTHING_DEFAULT) ) #define ARRAY1 M_OPEXTEND(M_OPL_array_int_t(), GENTYPE(struct array_int_s *)) +#define LIST1 M_OPEXTEND(M_OPL_list_int_t(), GENTYPE(struct list_int_s **)) //#define M_GENERIC_ORG_MLIB_COMP_1() (CORE) //#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_1() STR1 @@ -57,8 +62,10 @@ const string_t gx; #define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 #define M_GENERIC_ORG_3() (ARRAY) -#define M_GENERIC_ORG_ARRAY_COMP_5() (INT) -#define M_GENERIC_ORG_ARRAY_COMP_INT_OPLIST_6() ARRAY1 +#define M_GENERIC_ORG_ARRAY_COMP_5() (AINT) +#define M_GENERIC_ORG_ARRAY_COMP_AINT_OPLIST_6() ARRAY1 +#define M_GENERIC_ORG_ARRAY_COMP_10() (LINT) +#define M_GENERIC_ORG_ARRAY_COMP_LINT_OPLIST_30() LIST1 static bool test_empty(const string_t p) { @@ -98,6 +105,15 @@ static void test_array(void) printf("=%d, ", *it); } } + // Just change the type, same code! + M_LET(a, list_int_t) { + push(a, 14); + push(a, 15); + out_str(stdout, a); + for each(it, a) { + printf("=%d, ", *it); + } + } } int main(void) From fb8bf65233192c26dc377c336275b4c7f54568ee Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sun, 17 Dec 2023 00:40:13 +0100 Subject: [PATCH 04/16] Worlaround for warning with clang --- m-generic.h | 53 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/m-generic.h b/m-generic.h index 802f8531..bc1aa9be 100644 --- a/m-generic.h +++ b/m-generic.h @@ -36,6 +36,21 @@ #define m_typeof(x) typeof(x) #endif +#if defined(__clang__) && __clang_major__ >= 15 +/* Warnings disabled for CLANG in C mode */ +#define M_G3N_BEGIN_PROTECTED_CODE \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunreachable-code-generic-assoc\"") + +#define M_G3N_END_PROTECTED_CODE \ + _Pragma("clang diagnostic pop") + +#else + +#define M_G3N_BEGIN_PROTECTED_CODE +#define M_G3N_END_PROTECTED_CODE + +#endif // Instead of using expensive M_SEQ(1,50), precompute it: #define M_G3N_SEQ_INT \ @@ -83,9 +98,11 @@ typedef const char **m_g3n_cstring_end; // Call the OPERATOR call of the oplist registered to the variable 'x' // which takes one argument. #define M_G3N_CALL_1(op, x) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_CALL_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) + struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_1_func(x, oplist) \ M_G3N_CALL_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) #define M_G3N_CALL_1_func_test(gentype, op, x, oplist) \ @@ -97,9 +114,11 @@ typedef const char **m_g3n_cstring_end; // Call the OPERATOR call of the oplist registered to the variable 'svar' // which takes 2 arguments of the given type #define M_G3N_CALL_2(op, svar, type1, var1, type2, var2) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_2_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_2_func(x, oplist) \ M_APPLY(M_G3N_CALL_2_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) #define M_G3N_CALL_2_func_test(gentype, op, svar, type1, var1, type2, var2, oplist) \ @@ -111,9 +130,11 @@ typedef const char **m_g3n_cstring_end; // Call the OPERATOR call of the oplist registered to the variable 'svar' // which takes 3 arguments of the given type #define M_G3N_CALL_3(op, svar, type1, var1, type2, var2, type3, var3) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_3_func, (op, svar, type1, var1, type2, var2, type3, var3) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_3_func(x, oplist) \ M_APPLY(M_G3N_CALL_3_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) #define M_G3N_CALL_3_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, oplist) \ @@ -125,9 +146,11 @@ typedef const char **m_g3n_cstring_end; // Call the OPERATOR call of the oplist registered to the variable 'svar' // which takes 4 arguments of the given type #define M_G3N_CALL_4(op, svar, type1, var1, type2, var2, type3, var3, type4, var4) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_4_func, (op, svar, type1, var1, type2, var2, type3, var3, type4, var4) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_4_func(x, oplist) \ M_APPLY(M_G3N_CALL_4_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) #define M_G3N_CALL_4_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, type4, var4, oplist) \ @@ -139,32 +162,38 @@ typedef const char **m_g3n_cstring_end; // Call the OPERATOR call of the oplist registered to the variable 'x' // which takes one argument, it_type #define M_G3N_CALL_1i(op, x) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_CALL_1i_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) + struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_1i_func(x, oplist) \ M_G3N_CALL_1_func_test(M_G3N_IT_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) // Call the OPERATOR call of the oplist registered to the variable 'svar' // which takes 2 arguments of the given it_type #define M_G3N_CALL_2i(op, svar, type1, var1, type2, var2) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_2i_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_2i_func(x, oplist) \ M_APPLY(M_G3N_CALL_2_func_test, M_G3N_IT_TYPE(oplist), M_ID x, oplist) // Create a variable of type OPERATOR call of the oplist registered to the variable 'x' #define M_G3N_TYPE_1(op, x) \ + M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_TYPE_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) + struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + M_G3N_END_PROTECTED_CODE #define M_G3N_TYPE_1_func(x, oplist) \ M_G3N_TYPE_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) #define M_G3N_TYPE_1_func_test(gentype, op, x, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_TYPE_1_func_expand, M_EAT)(gentype, op, x, oplist) #define M_G3N_TYPE_1_func_expand(gentype, op, x, oplist) \ - gentype: (M_G3N_TYPE(op, oplist)){0}, \ + gentype: (M_G3N_TYPE(op, oplist)){0}, \ const gentype: (M_G3N_TYPE(op, oplist)){0}, @@ -230,18 +259,14 @@ typedef const char **m_g3n_cstring_end; #define M_G3N_EACHI(item, container, l_it, l_pass) \ (bool l_pass = true; l_pass; l_pass = false) \ for(sub_type(container) *item; l_pass ; l_pass = false) \ - for(it_type(container) l_it; l_pass ; l_pass = false) \ + for(it_type(container) l_it; l_pass ; l_pass = false) \ for(it_first(l_it, container) ; \ !it_end_p(l_it) \ && (item = it_ref(l_it), true) ; \ it_next(l_it)) -// TODO: init_with ? How to handle the different type of parameters ? -// TODO: emplace ? +// TODO: init_with ? How to handle the different type of parameters ? emplace ? // TODO: Integrate in M_PRINT ? -// TODO: How to handle -Wunreachable-code-generic-assoc which is enabled automatically in CLANG 15? -// Generate warning: due to lvalue conversion of the controlling expression, association of type 'const float' will never be selected because it is qualified [-Wunreachable-code-generic-assoc] -// Pragma if CLANG ? /* User code has to register oplists : From 42184f69db16c62a90ad976ff112bf1f2291c561 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sun, 17 Dec 2023 09:27:51 +0100 Subject: [PATCH 05/16] Disable generic tests for TCC --- tests/test-mgeneric.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index d96207b7..b08b38d9 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -31,7 +31,9 @@ #include "m-generic.h" // Generic is not supported if not C11 -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +// TCC has some issues in the preprocessing. +// See https://lists.nongnu.org/archive/html/tinycc-devel/2023-12/msg00004.html +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__TINYC__) ARRAY_DEF(array_int, int) #define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST) From 29c4ba3dcca97cab74625889b1104d0dfd3f9e69 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 23 Dec 2023 11:22:00 +0100 Subject: [PATCH 06/16] Add support for M_PRINT --- m-generic.h | 40 +++++++++++++++++++++++++++++++++++++++- tests/test-mgeneric.c | 14 ++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/m-generic.h b/m-generic.h index bc1aa9be..e209e81e 100644 --- a/m-generic.h +++ b/m-generic.h @@ -266,7 +266,45 @@ typedef const char **m_g3n_cstring_end; it_next(l_it)) // TODO: init_with ? How to handle the different type of parameters ? emplace ? -// TODO: Integrate in M_PRINT ? + +#define M_FPRINT_ARG_G3N(f, x) \ + M_G3N_BEGIN_PROTECTED_CODE \ + _Generic(((void)0,(x)), \ + char: fprintf(f, "%c", M_AS_TYPE(char,x)), \ + bool: fprintf(f, "%d", M_AS_TYPE(bool,x)), \ + signed char: fprintf(f, "%hhd", M_AS_TYPE(signed char,x)), \ + unsigned char: fprintf(f, "%hhu", M_AS_TYPE(unsigned char,x)), \ + signed short: fprintf(f, "%hd", M_AS_TYPE(signed short,x)), \ + unsigned short: fprintf(f, "%hu", M_AS_TYPE(unsigned short,x)), \ + signed int: fprintf(f, "%d", M_AS_TYPE(signed int,x)), \ + unsigned int: fprintf(f, "%u", M_AS_TYPE(unsigned int,x)), \ + long int: fprintf(f, "%ld", M_AS_TYPE(long int,x)), \ + unsigned long int: fprintf(f, "%lu", M_AS_TYPE(unsigned long int,x)), \ + long long int: fprintf(f, "%lld", M_AS_TYPE(long long int,x)), \ + unsigned long long int: fprintf(f, "%llu", M_AS_TYPE(unsigned long long int,x)), \ + float: fprintf(f, "%f", M_AS_TYPE(float,x)), \ + double: fprintf(f, "%f", M_AS_TYPE(double,x)), \ + long double: fprintf(f, "%Lf", M_AS_TYPE(long double,x)), \ + const char *: fprintf(f, "%s", M_AS_TYPE(const char*,x)), \ + char *: fprintf(f, "%s", M_AS_TYPE(char*,x)), \ + const void *: fprintf(f, "%p", M_AS_TYPE(const void *,x)), \ + void *: fprintf(f, "%p", M_AS_TYPE(void *,x)), \ + M_MAP2(M_G3N_CALL_2_func, (OUT_STR, x, m_g3n_file, f, TYPE, x) M_G3N_REGISTERED_ITEMS() ) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + M_G3N_END_PROTECTED_CODE + +#define M_G3N_FPRINT_ARG(f, x) \ + M_IF(M_PARENTHESIS_P(x)) \ + ( M_FPRINT_ARG_OUT_STR(f, M_PAIR_2 x, M_PAIR_1 x), \ + M_FPRINT_ARG_G3N(f, x) ) + +/* Ovverride M_PRINT */ +#undef M_PRINT +#define M_PRINT(...) do { M_REDUCE2(M_G3N_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, stdout, __VA_ARGS__); } while (0) + +#undef M_FPRINT +#define M_FPRINT(f,...) do { M_REDUCE2(M_G3N_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, f, __VA_ARGS__); } while (0) + /* User code has to register oplists : diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index b08b38d9..f52d144f 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -118,11 +118,25 @@ static void test_array(void) } } +static void test_print(void) +{ + int x = 0; + M_PRINT("X= ", x, "\n"); + M_LET( (s, "Hello"), string_t) { + M_PRINT("s=", s, "\n"); + } + M_LET( (a, 3,4,5), array_int_t) { + M_PRINT("a=", a, "\n"); + } +} + int main(void) { + freopen("atmp-generic.dat", "w", stdout); string_t p; test_string(p); test_array(); + test_print(); exit(0); } From 76ea989865281de6963ed22b069934ca2d8c4cdf Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 23 Dec 2023 13:15:44 +0100 Subject: [PATCH 07/16] Fix warnings --- m-generic.h | 44 +++++++++++++++++++++---------------------- tests/test-mgeneric.c | 3 ++- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/m-generic.h b/m-generic.h index e209e81e..24b7291f 100644 --- a/m-generic.h +++ b/m-generic.h @@ -267,33 +267,33 @@ typedef const char **m_g3n_cstring_end; // TODO: init_with ? How to handle the different type of parameters ? emplace ? -#define M_FPRINT_ARG_G3N(f, x) \ +#define M_FPRINT_ARG_G3N(f, x) \ M_G3N_BEGIN_PROTECTED_CODE \ _Generic(((void)0,(x)), \ - char: fprintf(f, "%c", M_AS_TYPE(char,x)), \ - bool: fprintf(f, "%d", M_AS_TYPE(bool,x)), \ - signed char: fprintf(f, "%hhd", M_AS_TYPE(signed char,x)), \ - unsigned char: fprintf(f, "%hhu", M_AS_TYPE(unsigned char,x)), \ - signed short: fprintf(f, "%hd", M_AS_TYPE(signed short,x)), \ - unsigned short: fprintf(f, "%hu", M_AS_TYPE(unsigned short,x)), \ - signed int: fprintf(f, "%d", M_AS_TYPE(signed int,x)), \ - unsigned int: fprintf(f, "%u", M_AS_TYPE(unsigned int,x)), \ - long int: fprintf(f, "%ld", M_AS_TYPE(long int,x)), \ - unsigned long int: fprintf(f, "%lu", M_AS_TYPE(unsigned long int,x)), \ - long long int: fprintf(f, "%lld", M_AS_TYPE(long long int,x)), \ - unsigned long long int: fprintf(f, "%llu", M_AS_TYPE(unsigned long long int,x)), \ - float: fprintf(f, "%f", M_AS_TYPE(float,x)), \ - double: fprintf(f, "%f", M_AS_TYPE(double,x)), \ - long double: fprintf(f, "%Lf", M_AS_TYPE(long double,x)), \ - const char *: fprintf(f, "%s", M_AS_TYPE(const char*,x)), \ - char *: fprintf(f, "%s", M_AS_TYPE(char*,x)), \ - const void *: fprintf(f, "%p", M_AS_TYPE(const void *,x)), \ - void *: fprintf(f, "%p", M_AS_TYPE(void *,x)), \ + char: fprintf(f, "%c", M_AS_TYPE(char,x)), \ + bool: fprintf(f, "%d", M_AS_TYPE(bool,x)), \ + signed char: fprintf(f, "%hhd", M_AS_TYPE(signed char,x)), \ + unsigned char: fprintf(f, "%hhu", M_AS_TYPE(unsigned char,x)), \ + signed short: fprintf(f, "%hd", M_AS_TYPE(signed short,x)), \ + unsigned short: fprintf(f, "%hu", M_AS_TYPE(unsigned short,x)), \ + signed int: fprintf(f, "%d", M_AS_TYPE(signed int,x)), \ + unsigned int: fprintf(f, "%u", M_AS_TYPE(unsigned int,x)), \ + long int: fprintf(f, "%ld", M_AS_TYPE(long int,x)), \ + unsigned long int: fprintf(f, "%lu", M_AS_TYPE(unsigned long int,x)), \ + long long int: fprintf(f, "%lld", M_AS_TYPE(long long int,x)), \ + unsigned long long int: fprintf(f, "%llu", M_AS_TYPE(unsigned long long int,x)), \ + float: fprintf(f, "%f", M_AS_TYPE(float,x)), \ + double: fprintf(f, "%f", M_AS_TYPE(double,x)), \ + long double: fprintf(f, "%Lf", M_AS_TYPE(long double,x)), \ + const char *: fprintf(f, "%s", M_AS_TYPE(const char*,x)), \ + char *: fprintf(f, "%s", M_AS_TYPE(char*,x)), \ + const void *: fprintf(f, "%p", M_AS_TYPE(const void *,x)), \ + void *: fprintf(f, "%p", M_AS_TYPE(void *,x)), \ M_MAP2(M_G3N_CALL_2_func, (OUT_STR, x, m_g3n_file, f, TYPE, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE -#define M_G3N_FPRINT_ARG(f, x) \ +#define M_G3N_FPRINT_ARG(f, x) \ M_IF(M_PARENTHESIS_P(x)) \ ( M_FPRINT_ARG_OUT_STR(f, M_PAIR_2 x, M_PAIR_1 x), \ M_FPRINT_ARG_G3N(f, x) ) diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index f52d144f..4630a535 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -132,7 +132,8 @@ static void test_print(void) int main(void) { - freopen("atmp-generic.dat", "w", stdout); + FILE *f = freopen("atmp-generic.dat", "w", stdout); + if (!f) abort(); string_t p; test_string(p); test_array(); From 5344423ad96d51e11d0c7a3bbe4a92374ff26641 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 23 Dec 2023 15:14:56 +0100 Subject: [PATCH 08/16] Reorder header / comment --- m-generic.h | 158 ++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 72 deletions(-) diff --git a/m-generic.h b/m-generic.h index 24b7291f..0fe226f9 100644 --- a/m-generic.h +++ b/m-generic.h @@ -27,17 +27,92 @@ #include "m-core.h" +/* Define the generic macro-functions */ +// TODO: m_ prefix? +#define init(x) M_G3N_CALL_1(INIT, x) +#define init_set(x, y) M_G3N_CALL_2(INIT_SET, x, TYPE, x, TYPE, y) +#define init_move(x, y) M_G3N_CALL_2(INIT_MOVE, x, TYPE, x, TYPE, y) +#define move(x, y) M_G3N_CALL_2(MOVE, x, TYPE, x, TYPE, y) +#define set(x, y) M_G3N_CALL_2(SET, x, TYPE, x , TYPE, y) +#define clear(x) M_G3N_CALL_1(CLEAR, x) +#define swap(x, y) M_G3N_CALL_2(SWAP, x, TYPE, x , TYPE, y) +#define hash(x) M_G3N_CALL_1(HASH, x) +#define equal(x, y) M_G3N_CALL_2(EQUAL, x, TYPE, x , TYPE, y) +#define cmp(x, y) M_G3N_CALL_2(CMP, x, TYPE, x , TYPE, y) +#define sort(x) M_G3N_CALL_1(SORT, x) +#define splice_back(d, s, i) M_G3N_CALL_3(SPLICE_BACK, d, TYPE, d, TYPE, s, IT_TYPE, i) +#define splice_at(d, id, s, is) M_G3N_CALL_4(SPLICE_BACK, d, TYPE, d, IT_TYPE, id, TYPE, s, IT_TYPE, is) +#define it_type(x) m_typeof(M_G3N_TYPE_1(IT_TYPE, x)) +#define sub_type(x) m_typeof(M_G3N_TYPE_1(SUBTYPE, x)) +#define key_type(x) m_typeof(M_G3N_TYPE_1(KEY_TYPE, x)) +#define value_type(x) m_typeof(M_G3N_TYPE_1(VALUE_TYPE, x)) +#define it_first(x, y) M_G3N_CALL_2(IT_FIRST, y, IT_TYPE, x, TYPE, y) +#define it_last(x, y) M_G3N_CALL_i2(IT_LAST, y, IT_TYPE, x, TYPE, y) +#define it_end(x, y) M_G3N_CALL_i2(IT_END, y, IT_TYPE, x, TYPE, y) +#define it_set(d, s) M_G3N_CALL_2i(IT_SET, d, IT_TYPE, d, IT_TYPE, s) +#define it_end_p(i) M_G3N_CALL_1i(IT_END_P, i) +#define it_last_p(i) M_G3N_CALL_1i(IT_LAST_P, i) +#define it_next(i) M_G3N_CALL_1i(IT_NEXT, i) +#define it_previous(i) M_G3N_CALL_1i(IT_PREVIOUS, i) +#define it_ref(i) M_G3N_CALL_1i(IT_REF, i) +#define it_cref(i) M_G3N_CALL_1i(IT_CREF, i) +#define it_equal(x, y) M_G3N_CALL_2i(IT_EQUAL, x, IT_TYPE, x, IT_TYPE, y) +#define it_insert(c, i, o) M_G2N_CALL_3(IT_INSERT, c, TYPE, c, IT_TYPE, i, SUBTYPE, o) +#define it_remove(c, i) M_G3N_CALL_2(IT_REMOVE, c, TYPE, c, IT_TYPE, i) +#define empty_p(x) M_G3N_CALL_1(EMPTY_P, x) +#define add(x, y) M_G3N_CALL_2(ADD, x, TYPE, x, TYPE, y) +#define sub(x, y) M_G3N_CALL_2(SUB, x, TYPE, x, TYPE, y) +#define mul(x, y) M_G3N_CALL_2(MUL, x, TYPE, x, TYPE, y) +#define div(x, y) M_G3N_CALL_2(DIV, x, TYPE, x, TYPE, y) +#define reset(x) M_G3N_CALL_1(RESET, x) +#define get(x, y) M_G3N_CALL_2(GET_KEY, x, TYPE, x, KEY_TYPE, y) +#define set_at(x, y, z) M_G3N_CALL_3(SET_KEY, x, TYPE, x, KEY_TYPE, y, VALUE_TYPE, z) +#define safe_get(x, y) M_G3N_CALL_2(SAFE_GET_KEY, x, TYPE, x, KEY_TYPE, y) +#define erase(x, y) M_G3N_CALL_2(ERASE, x, TYPE, x, KEY_TYPE, y) +#define get_size(x) M_G3N_CALL_1(GET_SIZE, x) +#define push(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) +#define pop(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) +#define push_move(x, y) M_G3N_CALL_2(PUSH_MOVE, x, TYPE, x, SUBTYPE *, y) +#define pop_move(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE*, y) +#define reverse(x) M_G3N_CALL_1(REVERSE, x) +#define get_str(s, c, b) M_G3N_CALL_3(GET_STR, c, string_t, s, TYPE, c, bool, b) +#define parse_str(c, s, e) M_G3N_CALL_3(PARSE_STR, c, TYPE, c, m_g3n_cstring, s, m_g3n_cstring_end, e) +#define out_str(x, y) M_G3N_CALL_2(OUT_STR, y, m_g3n_file, x, TYPE, y) +#define in_str(x, y) M_G3N_CALL_2(IN_STR, x, TYPE, x, m_g3n_file, y) +#define out_serial(x, y) M_G3N_CALL_2(OUT_SERIAL, y, m_serial_write_t, x, TYPE, y) +#define in_serial(x, y) M_G3N_CALL_2(IN_SERIAL, x, TYPE, x, m_serial_read_t, y) + +// for each(item, container) +#define each(item, container) \ + M_G3N_EACHI(item, container, M_C(m_local_it_, __LINE__), M_C(local_pass_, __LINE__)) + +/* User code has to register oplists like this: +#define M_GENERIC_ORG_2() (USER) +#define M_GENERIC_ORG_USER_COMP_1() (CORE) +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_6() FLT1 +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 +*/ + + +/*****************************************************************************/ +/********************************** INTERNAL *********************************/ +/*****************************************************************************/ + +/* Definition of the typeof keyword + In C23, we can use the standard keyword. + Otherwise we use some extensions of the compilers. */ #ifdef _MSC_VER #define m_typeof(x) decltype(x) #elif defined(__GNUC__) #define m_typeof(x) __typeof__(x) #else -// Definition valid for C23, TCC #define m_typeof(x) typeof(x) #endif +/* Warnings disabled for CLANG in C mode: + Due to the genericty of the _Generic generation, + we cannot avoid generating both T and const T in the generic association. */ #if defined(__clang__) && __clang_major__ >= 15 -/* Warnings disabled for CLANG in C mode */ #define M_G3N_BEGIN_PROTECTED_CODE \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wunreachable-code-generic-assoc\"") @@ -58,7 +133,9 @@ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, \ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 -/* 3 levels of indirection are neeeded: +/* Generate a sequence of the oplist that have been registered. + + 3 levels of indirection are neeeded: * One level is the organisation. * One level is the component of the organisation. * One level is the oplist within the component of the organisation @@ -86,8 +163,11 @@ typedef FILE *m_g3n_file; typedef const char *m_g3n_cstring; typedef const char **m_g3n_cstring_end; +// If x is of typex, return y else return (typey){O} #define M_AS_LINKED_TYPE(typex, x, typey, y) _Generic(((void)0,(x)), typex: (y), default: (typey) {0}) +// Return IT_TYPE of oplist promoted to a type suitable for a _Generic statement +// i.e. transform array type into corresponding pointer type #define M_G3N_IT_TYPE(oplist) m_typeof( ((void)0, (M_GET_IT_TYPE oplist()){0})) // Translate type' into the container type if type == TYPE, IT_TYPE into its iterator @@ -196,66 +276,8 @@ typedef const char **m_g3n_cstring_end; gentype: (M_G3N_TYPE(op, oplist)){0}, \ const gentype: (M_G3N_TYPE(op, oplist)){0}, - -// Define the generic macro-functions -// TODO: m_ prefix? -#define init(x) M_G3N_CALL_1(INIT, x) -#define init_set(x, y) M_G3N_CALL_2(INIT_SET, x, TYPE, x, TYPE, y) -#define init_move(x, y) M_G3N_CALL_2(INIT_MOVE, x, TYPE, x, TYPE, y) -#define move(x, y) M_G3N_CALL_2(MOVE, x, TYPE, x, TYPE, y) -#define set(x, y) M_G3N_CALL_2(SET, x, TYPE, x , TYPE, y) -#define clear(x) M_G3N_CALL_1(CLEAR, x) -#define swap(x, y) M_G3N_CALL_2(SWAP, x, TYPE, x , TYPE, y) -#define hash(x) M_G3N_CALL_1(HASH, x) -#define equal(x, y) M_G3N_CALL_2(EQUAL, x, TYPE, x , TYPE, y) -#define cmp(x, y) M_G3N_CALL_2(CMP, x, TYPE, x , TYPE, y) -#define sort(x) M_G3N_CALL_1(SORT, x) -#define splice_back(d, s, i) M_G3N_CALL_3(SPLICE_BACK, d, TYPE, d, TYPE, s, IT_TYPE, i) -#define splice_at(d, id, s, is) M_G3N_CALL_4(SPLICE_BACK, d, TYPE, d, IT_TYPE, id, TYPE, s, IT_TYPE, is) -#define it_type(x) m_typeof(M_G3N_TYPE_1(IT_TYPE, x)) -#define sub_type(x) m_typeof(M_G3N_TYPE_1(SUBTYPE, x)) -#define key_type(x) m_typeof(M_G3N_TYPE_1(KEY_TYPE, x)) -#define value_type(x) m_typeof(M_G3N_TYPE_1(VALUE_TYPE, x)) -#define it_first(x, y) M_G3N_CALL_2(IT_FIRST, y, IT_TYPE, x, TYPE, y) -#define it_last(x, y) M_G3N_CALL_i2(IT_LAST, y, IT_TYPE, x, TYPE, y) -#define it_end(x, y) M_G3N_CALL_i2(IT_END, y, IT_TYPE, x, TYPE, y) -#define it_set(d, s) M_G3N_CALL_2i(IT_SET, d, IT_TYPE, d, IT_TYPE, s) -#define it_end_p(i) M_G3N_CALL_1i(IT_END_P, i) -#define it_last_p(i) M_G3N_CALL_1i(IT_LAST_P, i) -#define it_next(i) M_G3N_CALL_1i(IT_NEXT, i) -#define it_previous(i) M_G3N_CALL_1i(IT_PREVIOUS, i) -#define it_ref(i) M_G3N_CALL_1i(IT_REF, i) -#define it_cref(i) M_G3N_CALL_1i(IT_CREF, i) -#define it_equal(x, y) M_G3N_CALL_2i(IT_EQUAL, x, IT_TYPE, x, IT_TYPE, y) -#define it_insert(c, i, o) M_G2N_CALL_3(IT_INSERT, c, TYPE, c, IT_TYPE, i, SUBTYPE, o) -#define it_remove(c, i) M_G3N_CALL_2(IT_REMOVE, c, TYPE, c, IT_TYPE, i) -#define empty_p(x) M_G3N_CALL_1(EMPTY_P, x) -#define add(x, y) M_G3N_CALL_2(ADD, x, TYPE, x, TYPE, y) -#define sub(x, y) M_G3N_CALL_2(SUB, x, TYPE, x, TYPE, y) -#define mul(x, y) M_G3N_CALL_2(MUL, x, TYPE, x, TYPE, y) -#define div(x, y) M_G3N_CALL_2(DIV, x, TYPE, x, TYPE, y) -#define reset(x) M_G3N_CALL_1(RESET, x) -#define get(x, y) M_G3N_CALL_2(GET_KEY, x, TYPE, x, KEY_TYPE, y) -#define set_at(x, y, z) M_G3N_CALL_3(SET_KEY, x, TYPE, x, KEY_TYPE, y, VALUE_TYPE, z) -#define safe_get(x, y) M_G3N_CALL_2(SAFE_GET_KEY, x, TYPE, x, KEY_TYPE, y) -#define erase(x, y) M_G3N_CALL_2(ERASE, x, TYPE, x, KEY_TYPE, y) -#define get_size(x) M_G3N_CALL_1(GET_SIZE, x) -#define push(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) -#define pop(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) -#define push_move(x, y) M_G3N_CALL_2(PUSH_MOVE, x, TYPE, x, SUBTYPE *, y) -#define pop_move(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE*, y) -#define reverse(x) M_G3N_CALL_1(REVERSE, x) -#define get_str(s, c, b) M_G3N_CALL_3(GET_STR, c, string_t, s, TYPE, c, bool, b) -#define parse_str(c, s, e) M_G3N_CALL_3(PARSE_STR, c, TYPE, c, m_g3n_cstring, s, m_g3n_cstring_end, e) -#define out_str(x, y) M_G3N_CALL_2(OUT_STR, y, m_g3n_file, x, TYPE, y) -#define in_str(x, y) M_G3N_CALL_2(IN_STR, x, TYPE, x, m_g3n_file, y) -#define out_serial(x, y) M_G3N_CALL_2(OUT_SERIAL, y, m_serial_write_t, x, TYPE, y) -#define in_serial(x, y) M_G3N_CALL_2(IN_SERIAL, x, TYPE, x, m_serial_read_t, y) - -// for each -#define each(item, container) \ - M_G3N_EACHI(item, container, M_C(m_local_it_, __LINE__), M_C(local_pass_, __LINE__)) - +// for each item in the container +// Same as M_EACH except it uses the generic macros #define M_G3N_EACHI(item, container, l_it, l_pass) \ (bool l_pass = true; l_pass; l_pass = false) \ for(sub_type(container) *item; l_pass ; l_pass = false) \ @@ -267,6 +289,7 @@ typedef const char **m_g3n_cstring_end; // TODO: init_with ? How to handle the different type of parameters ? emplace ? +// Overwrite M_FPRINT & M_PRINT to support global oplists registration #define M_FPRINT_ARG_G3N(f, x) \ M_G3N_BEGIN_PROTECTED_CODE \ _Generic(((void)0,(x)), \ @@ -305,13 +328,4 @@ typedef const char **m_g3n_cstring_end; #undef M_FPRINT #define M_FPRINT(f,...) do { M_REDUCE2(M_G3N_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, f, __VA_ARGS__); } while (0) - -/* User code has to register oplists : - -#define M_GENERIC_ORG_2() (USER) -#define M_GENERIC_ORG_USER_COMP_1() (CORE) -#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_6() FLT1 -#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 -*/ - #endif From 044a085fb676039d6da2f4a8b13c0c6c5a561444 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 23 Dec 2023 23:40:34 +0100 Subject: [PATCH 09/16] Fix support for pop generic macro function --- m-core.h | 4 ++++ m-generic.h | 10 ++++++---- tests/test-mgeneric.c | 7 +++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/m-core.h b/m-core.h index 9fb291f0..5e2862c4 100644 --- a/m-core.h +++ b/m-core.h @@ -1458,6 +1458,7 @@ M_BEGIN_PROTECTED_CODE #define M_PATTERN_type_type , #define M_PATTERN_TYPE_TYPE , #define M_PATTERN_SUBTYPE_SUBTYPE , +#define M_PATTERN_SUBTYPE_PTR_SUBTYPE_PTR , #define M_PATTERN_IT_TYPE_IT_TYPE , #define M_PATTERN_KEY_TYPE_KEY_TYPE , #define M_PATTERN_VALUE_TYPE_VALUE_TYPE , @@ -3369,6 +3370,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_TYPE_TYPE(a) ,a, #define M_SUBTYPE_SUBTYPE(a) ,a, #define M_GENTYPE_GENTYPE(a) ,a, +#define M_SUBTYPE_PTR_SUBTYPE_PTR(a) ,a, #define M_NAME_NAME(a) ,a, #define M_OPLIST_OPLIST(a) ,a, #define M_SORT_SORT(a) ,a, @@ -3458,6 +3460,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_GET_CMP(...) M_GET_METHOD(CMP, M_CMP_DEFAULT, __VA_ARGS__) #define M_GET_TYPE(...) M_GET_METHOD(TYPE, M_NO_DEF_TYPE, __VA_ARGS__) #define M_GET_SUBTYPE(...) M_GET_METHOD(SUBTYPE, M_NO_DEF_TYPE, __VA_ARGS__) +#define M_GET_SUBTYPE_PTR(...) M_GET_METHOD(SUBTYPE_PTR, M_NO_DEF_TYPE, __VA_ARGS__) #define M_GET_GENTYPE(...) M_GET_METHOD(GENTYPE, M_NO_DEF_TYPE, __VA_ARGS__) #define M_GET_NAME(...) M_GET_METHOD(NAME, M_NO_DEF_TYPE, __VA_ARGS__) #define M_GET_OPLIST(...) M_GET_METHOD(OPLIST, (), __VA_ARGS__) @@ -3539,6 +3542,7 @@ M_INLINE size_t m_core_cstr_hash(const char str[]) #define M_CALL_CMP(oplist, ...) M_APPLY_API(M_GET_CMP oplist, oplist, __VA_ARGS__) //#define M_CALL_TYPE(oplist, ...) M_APPLY_API(M_GET_TYPE oplist, oplist, __VA_ARGS__) //#define M_CALL_SUBTYPE(oplist, ...) M_APPLY_API(M_GET_SUBTYPE oplist, oplist, __VA_ARGS__) +//#define M_CALL_SUBTYPE_PTR(oplist, ...) M_APPLY_API(M_GET_SUBTYPE_PTR oplist, oplist, __VA_ARGS__) //#define M_CALL_GENTYPE(oplist, ...) M_APPLY_API(M_GET_GENTYPE oplist, oplist, __VA_ARGS__) //#define M_CALL_NAME(oplist, ...) M_APPLY_API(M_GET_NAME oplist, oplist, __VA_ARGS__) //#define M_CALL_OPLIST(oplist, ...) M_APPLY_API(M_GET_OPLIST oplist, oplist, __VA_ARGS__) diff --git a/m-generic.h b/m-generic.h index 0fe226f9..7c82da49 100644 --- a/m-generic.h +++ b/m-generic.h @@ -71,9 +71,9 @@ #define erase(x, y) M_G3N_CALL_2(ERASE, x, TYPE, x, KEY_TYPE, y) #define get_size(x) M_G3N_CALL_1(GET_SIZE, x) #define push(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) -#define pop(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE, y) -#define push_move(x, y) M_G3N_CALL_2(PUSH_MOVE, x, TYPE, x, SUBTYPE *, y) -#define pop_move(x, y) M_G3N_CALL_2(PUSH, x, TYPE, x, SUBTYPE*, y) +#define pop(x, y) M_G3N_CALL_2(POP, y, SUBTYPE_PTR, x, TYPE, y) +#define push_move(x, y) M_G3N_CALL_2(PUSH_MOVE, x, TYPE, x, SUBTYPE_PTR, y) +#define pop_move(x, y) M_G3N_CALL_2(POP_MOVE, y, SUBTYPE_PTR, x, TYPE, y) #define reverse(x) M_G3N_CALL_1(REVERSE, x) #define get_str(s, c, b) M_G3N_CALL_3(GET_STR, c, string_t, s, TYPE, c, bool, b) #define parse_str(c, s, e) M_G3N_CALL_3(PARSE_STR, c, TYPE, c, m_g3n_cstring, s, m_g3n_cstring_end, e) @@ -173,7 +173,9 @@ typedef const char **m_g3n_cstring_end; // Translate type' into the container type if type == TYPE, IT_TYPE into its iterator // KEY_TYPE, VALUE_TYPE, SUBTYPE into the associated type in the oplist, // or keep 'type' otherwise -#define M_G3N_TYPE(type, oplist) M_GET_METHOD(type, type, M_OPFLAT oplist ()) +// Handle also specially SUBTYPE_PTR +#define M_G3N_TYPE(type, oplist) M_G3N_TYPE_B(type, oplist()) +#define M_G3N_TYPE_B(type, oplist) M_GET_METHOD(type, type, SUBTYPE_PTR(M_GET_SUBTYPE oplist *), M_OPFLAT oplist) // Call the OPERATOR call of the oplist registered to the variable 'x' // which takes one argument. diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index 4630a535..01c149fc 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -100,12 +100,19 @@ static void test_string(string_t p) static void test_array(void) { M_LET(a, array_int_t) { + bool b = empty_p(a); + assert(b); push(a, 14); + b = empty_p(a); + assert(!b); push(a, 15); out_str(stdout, a); for each(it, a) { printf("=%d, ", *it); } + int z; + pop(&z, a); + assert(z == 15); } // Just change the type, same code! M_LET(a, list_int_t) { From 73e3d1d1bd31cb61dba097f7ea5c72b1381deff5 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Thu, 28 Dec 2023 20:05:43 +0100 Subject: [PATCH 10/16] Register string_t globally --- m-generic.h | 19 +++++++++++++------ m-string.h | 11 +++++++---- tests/test-mgeneric.c | 10 +++++----- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/m-generic.h b/m-generic.h index 7c82da49..36f93e05 100644 --- a/m-generic.h +++ b/m-generic.h @@ -110,7 +110,7 @@ #endif /* Warnings disabled for CLANG in C mode: - Due to the genericty of the _Generic generation, + Due to the genericity of the _Generic generation, we cannot avoid generating both T and const T in the generic association. */ #if defined(__clang__) && __clang_major__ >= 15 #define M_G3N_BEGIN_PROTECTED_CODE \ @@ -135,13 +135,13 @@ /* Generate a sequence of the oplist that have been registered. - 3 levels of indirection are neeeded: + 3 levels of indirection are needed: * One level is the organisation. * One level is the component of the organisation. * One level is the oplist within the component of the organisation - Return the list of unevaled oplist that are registered accorss all organisations, all components. - MLIB Organisation is the default organisation and is always registered - Usage Domain: Nb organizations * Nb components * Nb oplists < M_MAX_NB_ARGUMENT + Return the list of unevaluated oplist that are registered across all organizations, all components. + M*LIB Organisation (MLIB) is the default organisation and is always registered + Usage Domain: Nb organizations * Nb components * Nb oplist <= M_MAX_NB_ARGUMENT */ #define M_G3N_REGISTERED_ITEMS() \ M_CROSS_MAP( M_G3N_IS_PRESENT3, ( comp M_CROSS_MAP(M_G3N_IS_PRESENT2, (MLIB M_MAP(M_G3N_IS_PRESENT, M_G3N_SEQ_INT)), ( M_G3N_SEQ_INT ) ) ), ( M_G3N_SEQ_INT ) ) @@ -155,10 +155,11 @@ #define M_G3N_IS_PRESENT3(el1, num) \ M_IF(M_OPLIST_P(M_C4(M_GENERIC_ORG_, el1, _OPLIST_, num)()))(M_DEFERRED_COMMA M_C4(M_GENERIC_ORG_, el1, _OPLIST_, num), ) + // Dummy unused structure. Just to fill in default case of the _Generic struct m_g3neric_dummys; -// Synonymous for some basic pointers doesn't work well with prepropressing code +// Synonymous for some basic pointers doesn't work well with preprocessing code typedef FILE *m_g3n_file; typedef const char *m_g3n_cstring; typedef const char **m_g3n_cstring_end; @@ -278,6 +279,7 @@ typedef const char **m_g3n_cstring_end; gentype: (M_G3N_TYPE(op, oplist)){0}, \ const gentype: (M_G3N_TYPE(op, oplist)){0}, + // for each item in the container // Same as M_EACH except it uses the generic macros #define M_G3N_EACHI(item, container, l_it, l_pass) \ @@ -289,6 +291,7 @@ typedef const char **m_g3n_cstring_end; && (item = it_ref(l_it), true) ; \ it_next(l_it)) + // TODO: init_with ? How to handle the different type of parameters ? emplace ? // Overwrite M_FPRINT & M_PRINT to support global oplists registration @@ -330,4 +333,8 @@ typedef const char **m_g3n_cstring_end; #undef M_FPRINT #define M_FPRINT(f,...) do { M_REDUCE2(M_G3N_FPRINT_ARG, M_SEPARATE_PER_SEMICOLON, f, __VA_ARGS__); } while (0) +// Register CORE component of M*LIB +#define M_GENERIC_ORG_MLIB_COMP_1() (CORE) + + #endif diff --git a/m-string.h b/m-string.h index 501ac1bb..e10dc4b3 100644 --- a/m-string.h +++ b/m-string.h @@ -2023,14 +2023,14 @@ m_string_utf8_p(m_string_t str) /* Use of Compound Literals to init a constant string. NOTE: The use of the additional structure layer is to ensure that the pointer to char is properly aligned to an int (this - is a needed asumption by m_string_hash). + is a needed assumption by m_string_hash). Otherwise it could have been : #define M_STRING_CTE(s) \ ((const m_string_t){{.size = sizeof(s)-1, .alloc = sizeof(s), \ .ptr = s}}) which produces faster code. Note: This code doesn't work with C++ (use of c99 feature - of recursive struct definition and compound literral). + of recursive struct definition and compound literal). As such, there is a separate C++ definition. */ #ifndef __cplusplus @@ -2101,7 +2101,7 @@ namespace m_lib { SWAP(m_string_swap), RESET(m_string_reset), \ EMPTY_P(m_string_empty_p), \ CLEAR(m_string_clear), HASH(m_string_hash), EQUAL(m_string_equal_p), \ - CMP(m_string_cmp), TYPE(m_string_t), \ + CMP(m_string_cmp), TYPE(m_string_t), GENTYPE(struct m_string_s *), \ PARSE_STR(m_string_parse_str), GET_STR(m_string_get_str), \ OUT_STR(m_string_out_str), IN_STR(m_string_in_str), \ OUT_SERIAL(m_string_out_serial), IN_SERIAL(m_string_in_serial), \ @@ -2115,13 +2115,16 @@ namespace m_lib { ,IT_END_P(m_string_end_p) \ ,IT_EQUAL_P(m_string_it_equal_p) \ ,IT_NEXT(m_string_next) \ - ,IT_CREF(m_string_cref) \ + ,IT_CREF(m_string_cref), PUSH(m_string_push_u) \ ,EMPLACE_TYPE(const char*) \ ) /* Register the OPLIST as a global one */ #define M_OPL_m_string_t() M_STRING_OPLIST +/* Register the string_t oplist as a generic type */ +#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_1() M_STRING_OPLIST + /***********************************************************************/ /* */ diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index 01c149fc..d4678891 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -46,22 +46,22 @@ static void h(string_t x) string_set_str(x, "TEST STRING"); } +// Global variable to test genericity on a global variable const string_t gx; -#define STR1 M_OPEXTEND(STRING_OPLIST, GENTYPE(struct m_string_s *), PUSH(m_string_push_u)) #define FLT1 (GENTYPE(float), TYPE(float), INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ CLEAR(M_NOTHING_DEFAULT) ) +#define INT1 (GENTYPE(int), TYPE(int), INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ + CLEAR(M_NOTHING_DEFAULT) ) #define ARRAY1 M_OPEXTEND(M_OPL_array_int_t(), GENTYPE(struct array_int_s *)) #define LIST1 M_OPEXTEND(M_OPL_list_int_t(), GENTYPE(struct list_int_s **)) -//#define M_GENERIC_ORG_MLIB_COMP_1() (CORE) -//#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_1() STR1 -//#define M_GENERIC_ORG_MLIB_COMP_CORE_OPLIST_5() FLT1 +//NOTE: string_t is registered by the CORE component of the MLIB organization #define M_GENERIC_ORG_2() (USER) #define M_GENERIC_ORG_USER_COMP_1() (CORE) #define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_6() FLT1 -#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_7() STR1 +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() INT1 #define M_GENERIC_ORG_3() (ARRAY) #define M_GENERIC_ORG_ARRAY_COMP_5() (AINT) From 5ceb34d9801d22b00966e3cb0ae2c5100a7d97b1 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 12:24:54 +0100 Subject: [PATCH 11/16] Cosmetics changes --- m-generic.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/m-generic.h b/m-generic.h index 36f93e05..91e213ed 100644 --- a/m-generic.h +++ b/m-generic.h @@ -157,7 +157,7 @@ // Dummy unused structure. Just to fill in default case of the _Generic -struct m_g3neric_dummys; +struct m_g3neric_dummy_s; // Synonymous for some basic pointers doesn't work well with preprocessing code typedef FILE *m_g3n_file; @@ -169,7 +169,8 @@ typedef const char **m_g3n_cstring_end; // Return IT_TYPE of oplist promoted to a type suitable for a _Generic statement // i.e. transform array type into corresponding pointer type -#define M_G3N_IT_TYPE(oplist) m_typeof( ((void)0, (M_GET_IT_TYPE oplist()){0})) +#define M_G3N_GEN_IT_TYPE(oplist) m_typeof( ((void)0, (M_GET_IT_TYPE oplist()){0})) +#define M_G3N_GEN_TYPE(oplist) M_GET_GENTYPE oplist() // Translate type' into the container type if type == TYPE, IT_TYPE into its iterator // KEY_TYPE, VALUE_TYPE, SUBTYPE into the associated type in the oplist, @@ -184,10 +185,10 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_CALL_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_1_func(x, oplist) \ - M_G3N_CALL_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) + M_G3N_CALL_1_func_test(M_G3N_GEN_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) #define M_G3N_CALL_1_func_test(gentype, op, x, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_CALL_1_func_expand, M_EAT)(gentype, op, x, oplist) #define M_G3N_CALL_1_func_expand(gentype, op, x, oplist) \ @@ -200,10 +201,10 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_2_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_2_func(x, oplist) \ - M_APPLY(M_G3N_CALL_2_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) + M_APPLY(M_G3N_CALL_2_func_test, M_G3N_GEN_TYPE(oplist), M_ID x, oplist) #define M_G3N_CALL_2_func_test(gentype, op, svar, type1, var1, type2, var2, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_CALL_2_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, oplist) #define M_G3N_CALL_2_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ @@ -216,10 +217,10 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_3_func, (op, svar, type1, var1, type2, var2, type3, var3) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_3_func(x, oplist) \ - M_APPLY(M_G3N_CALL_3_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) + M_APPLY(M_G3N_CALL_3_func_test, M_G3N_GEN_TYPE(oplist), M_ID x, oplist) #define M_G3N_CALL_3_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_CALL_3_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, M_G3N_TYPE(type3, oplist), var3, oplist) #define M_G3N_CALL_3_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ @@ -232,10 +233,10 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_4_func, (op, svar, type1, var1, type2, var2, type3, var3, type4, var4) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_4_func(x, oplist) \ - M_APPLY(M_G3N_CALL_4_func_test, M_GET_GENTYPE oplist(), M_ID x, oplist) + M_APPLY(M_G3N_CALL_4_func_test, M_G3N_GEN_TYPE(oplist), M_ID x, oplist) #define M_G3N_CALL_4_func_test(gentype, op, svar, type1, var1, type2, var2, type3, var3, type4, var4, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_CALL_4_func_expand, M_EAT)(gentype, op, svar, M_G3N_TYPE(type1, oplist), var1, M_G3N_TYPE(type2, oplist), var2, M_G3N_TYPE(type3, oplist), var3, M_G3N_TYPE(type4, oplist), var4, oplist) #define M_G3N_CALL_4_func_expand(gentype, op, svar, type1, var1, type2, var2, oplist) \ @@ -248,10 +249,10 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_CALL_1i_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_1i_func(x, oplist) \ - M_G3N_CALL_1_func_test(M_G3N_IT_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) + M_G3N_CALL_1_func_test(M_G3N_GEN_IT_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) // Call the OPERATOR call of the oplist registered to the variable 'svar' // which takes 2 arguments of the given it_type @@ -259,20 +260,20 @@ typedef const char **m_g3n_cstring_end; M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (svar)), \ M_MAP2(M_G3N_CALL_2i_func, (op, svar, type1, var1, type2, var2) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_CALL_2i_func(x, oplist) \ - M_APPLY(M_G3N_CALL_2_func_test, M_G3N_IT_TYPE(oplist), M_ID x, oplist) + M_APPLY(M_G3N_CALL_2_func_test, M_G3N_GEN_IT_TYPE(oplist), M_ID x, oplist) // Create a variable of type OPERATOR call of the oplist registered to the variable 'x' #define M_G3N_TYPE_1(op, x) \ M_G3N_BEGIN_PROTECTED_CODE \ _Generic( ((void)0, (x)), \ M_MAP2(M_G3N_TYPE_1_func, (op, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void)0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void)0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_TYPE_1_func(x, oplist) \ - M_G3N_TYPE_1_func_test(M_GET_GENTYPE oplist(), M_PAIR_1 x, M_PAIR_2 x, oplist) + M_G3N_TYPE_1_func_test(M_G3N_GEN_TYPE(oplist), M_PAIR_1 x, M_PAIR_2 x, oplist) #define M_G3N_TYPE_1_func_test(gentype, op, x, oplist) \ M_IF_METHOD(op, oplist())(M_G3N_TYPE_1_func_expand, M_EAT)(gentype, op, x, oplist) #define M_G3N_TYPE_1_func_expand(gentype, op, x, oplist) \ @@ -318,7 +319,7 @@ typedef const char **m_g3n_cstring_end; const void *: fprintf(f, "%p", M_AS_TYPE(const void *,x)), \ void *: fprintf(f, "%p", M_AS_TYPE(void *,x)), \ M_MAP2(M_G3N_CALL_2_func, (OUT_STR, x, m_g3n_file, f, TYPE, x) M_G3N_REGISTERED_ITEMS() ) \ - struct m_g3neric_dummys *: /* cannot happen */ (void) 0) \ + struct m_g3neric_dummy_s *: /* cannot happen */ (void) 0) \ M_G3N_END_PROTECTED_CODE #define M_G3N_FPRINT_ARG(f, x) \ From 422f3c2c045014e2a294637c22404050da311f94 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 13:22:23 +0100 Subject: [PATCH 12/16] Add GENTYPE operator to all oplists --- m-array.h | 2 +- m-bptree.h | 4 ++-- m-buffer.h | 2 +- m-concurrent.h | 2 +- m-deque.h | 2 +- m-dict.h | 4 ++-- m-funcobj.h | 4 ++-- m-i-list.h | 2 +- m-i-shared.h | 2 +- m-list.h | 2 +- m-prioqueue.h | 2 +- m-rbtree.h | 2 +- m-shared.h | 2 +- m-snapshot.h | 2 +- m-string.h | 6 +++--- m-tuple.h | 2 +- m-variant.h | 2 +- tests/test-mgeneric.c | 6 ++---- 18 files changed, 24 insertions(+), 26 deletions(-) diff --git a/m-array.h b/m-array.h index 77bbc7f7..56a43d72 100644 --- a/m-array.h +++ b/m-array.h @@ -89,7 +89,7 @@ ,INIT_MOVE(M_F(name, _init_move)) \ ,MOVE(M_F(name, _move)) \ ,SWAP(M_F(name, _swap)) \ - ,TYPE(M_F(name,_ct)) \ + ,TYPE(M_F(name,_ct)) , GENTYPE(struct M_F(name,_s)*) \ ,NAME(name) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,EMPTY_P(M_F(name,_empty_p)) \ diff --git a/m-bptree.h b/m-bptree.h index bd823311..5ef60be6 100644 --- a/m-bptree.h +++ b/m-bptree.h @@ -166,7 +166,7 @@ MOVE(M_F(name, _move)), \ SWAP(M_F(name, _swap)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ SUBTYPE(M_F(name, _subtype_ct)), \ IT_TYPE(M_F(name, _it_ct)), \ IT_FIRST(M_F(name,_it)), \ @@ -215,7 +215,7 @@ MOVE(M_F(name, _move)), \ SWAP(M_F(name, _swap)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ SUBTYPE(M_F(name, _subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ IT_TYPE(M_F(name, _it_ct)), \ diff --git a/m-buffer.h b/m-buffer.h index a98630fa..a3ee02f3 100644 --- a/m-buffer.h +++ b/m-buffer.h @@ -1308,7 +1308,7 @@ M_F(name, _init)(buffer_t v, size_t size) \ ,SET(M_F(name, _set)) \ ,CLEAR(M_F(name, _clear)) \ ,NAME(name) \ - ,TYPE(M_F(name,_ct)) \ + ,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,RESET(M_F(name,_reset)) \ ,PUSH(M_F(name,_push)) \ diff --git a/m-concurrent.h b/m-concurrent.h index 0360c39f..4a940424 100644 --- a/m-concurrent.h +++ b/m-concurrent.h @@ -106,7 +106,7 @@ ,M_IF_METHOD(MOVE, oplist)(MOVE(M_F(name, _move)),) \ ,M_IF_METHOD(SWAP,oplist)(SWAP(M_F(name, _swap)),) \ ,NAME(name) \ - ,TYPE(M_F(name,_ct)) \ + ,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,OPLIST(oplist) \ ,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_F(name,_empty_p)),) \ diff --git a/m-deque.h b/m-deque.h index f8742e71..eecba7fd 100644 --- a/m-deque.h +++ b/m-deque.h @@ -1104,7 +1104,7 @@ ,MOVE(M_F(name, _move)) \ ,SWAP(M_F(name, _swap)) \ ,NAME(name) \ - ,TYPE(M_F(name,_ct)) \ + ,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,EMPTY_P(M_F(name,_empty_p)) \ ,IT_TYPE(M_F(name,_it_ct)) \ diff --git a/m-dict.h b/m-dict.h index 61f086b8..b0cacc21 100644 --- a/m-dict.h +++ b/m-dict.h @@ -1071,7 +1071,7 @@ SWAP(M_F(name, _swap)), \ RESET(M_F(name, _reset)), \ NAME(name), \ - TYPE(M_F(name, _ct)), \ + TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \ SUBTYPE(M_F(name, _subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ IT_TYPE(M_F(name, _it_ct)), \ @@ -1126,7 +1126,7 @@ SWAP(M_F(name, _swap)), \ RESET(M_F(name, _reset)), \ NAME(name), \ - TYPE(M_F(name, _ct)), \ + TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \ SUBTYPE(M_F(name, _subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ PUSH(M_F(name,_push)), \ diff --git a/m-funcobj.h b/m-funcobj.h index 7b5401e8..f4d4d9d4 100644 --- a/m-funcobj.h +++ b/m-funcobj.h @@ -111,7 +111,7 @@ /* Specialization of the OPLIST in function if there is at least one member or not */ #define M_FUNC0BJ_INS_NO_ATTR_OPLIST(name) ( \ NAME(name), \ - TYPE(M_F(name, _ct)), \ + TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \ CLEAR(M_F(name, _clear)), \ INIT(M_F(name,_init)) \ ) @@ -127,7 +127,7 @@ /* Define the oplist of the instance */ #define M_FUNC0BJ_INS_ATTR_OPLIST_P3(name, ...) ( \ NAME(name), \ - TYPE(M_F(name, _ct)), \ + TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \ INIT_WITH(M_F(name, _init_with)), \ CLEAR(M_F(name, _clear)), \ M_IF_METHOD_ALL(INIT, __VA_ARGS__)(INIT(M_F(name,_init)),), \ diff --git a/m-i-list.h b/m-i-list.h index 9ce955d6..f9a88ff2 100644 --- a/m-i-list.h +++ b/m-i-list.h @@ -100,7 +100,7 @@ typedef struct m_il1st_head_s { INIT_MOVE(M_F(name, _init_move)), \ MOVE(M_F(name, _move)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ RESET(M_F(name,_reset)), \ SUBTYPE(M_F(name,_subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ diff --git a/m-i-shared.h b/m-i-shared.h index f81fa555..9c6b1e48 100644 --- a/m-i-shared.h +++ b/m-i-shared.h @@ -95,7 +95,7 @@ M_BEGIN_PROTECTED_CODE CLEAR(M_F(name, _clear)), \ RESET(M_F(name, _reset) M_IPTR), \ NAME(name), \ - TYPE(M_F(name, _ct)), \ + TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*), \ OPLIST(oplist), \ SUBTYPE(M_F(name, _subtype_ct)) \ ) diff --git a/m-list.h b/m-list.h index 7a79b9a9..fc5951fb 100644 --- a/m-list.h +++ b/m-list.h @@ -109,7 +109,7 @@ INIT_MOVE(M_F(name, _init_move)), \ SWAP(M_F(name, _swap)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)**), \ SUBTYPE(M_F(name,_subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ IT_TYPE(M_F(name, _it_ct)), \ diff --git a/m-prioqueue.h b/m-prioqueue.h index 4c79c0a6..2a30a12e 100644 --- a/m-prioqueue.h +++ b/m-prioqueue.h @@ -82,7 +82,7 @@ ,MOVE(M_F(name, _move)) \ ,SWAP(M_F(name, _swap)) \ ,NAME(name) \ - ,TYPE(M_F(name,_ct)) \ + ,TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,RESET(M_F(name,_reset)) \ ,PUSH(M_F(name,_push)) \ diff --git a/m-rbtree.h b/m-rbtree.h index f5880f20..460d878a 100644 --- a/m-rbtree.h +++ b/m-rbtree.h @@ -81,7 +81,7 @@ MOVE(M_F(name, _move)), \ SWAP(M_F(name, _swap)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ SUBTYPE(M_F(name, _subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ GET_SIZE(M_F(name, _size)), \ diff --git a/m-shared.h b/m-shared.h index 8fba531d..17847af9 100644 --- a/m-shared.h +++ b/m-shared.h @@ -118,7 +118,7 @@ M_BEGIN_PROTECTED_CODE MOVE(M_F(name, _move)), \ SWAP(M_F(name, _swap)) \ ,NAME(name) \ - ,TYPE(M_F(name, _ct)) \ + ,TYPE(M_F(name, _ct)), GENTYPE(struct M_F(name,_s)*) \ ) // OPLIST to handle a counter of atomic type diff --git a/m-snapshot.h b/m-snapshot.h index 860ce57f..7b24480a 100644 --- a/m-snapshot.h +++ b/m-snapshot.h @@ -112,7 +112,7 @@ M_BEGIN_PROTECTED_CODE ,SET(M_F(name, _set)) \ ,CLEAR(M_F(name, _clear)) \ ,NAME(name) \ - ,TYPE(M_F(name, _ct)) \ + ,TYPE(M_F(name, _ct)) , GENTYPE(struct M_F(name,_s)*) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,OPLIST(oplist) \ ,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_F(name, _init_move)),) \ diff --git a/m-string.h b/m-string.h index e10dc4b3..039e4e7d 100644 --- a/m-string.h +++ b/m-string.h @@ -2101,7 +2101,7 @@ namespace m_lib { SWAP(m_string_swap), RESET(m_string_reset), \ EMPTY_P(m_string_empty_p), \ CLEAR(m_string_clear), HASH(m_string_hash), EQUAL(m_string_equal_p), \ - CMP(m_string_cmp), TYPE(m_string_t), GENTYPE(struct m_string_s *), \ + CMP(m_string_cmp), TYPE(m_string_t), GENTYPE(struct m_string_s*), \ PARSE_STR(m_string_parse_str), GET_STR(m_string_get_str), \ OUT_STR(m_string_out_str), IN_STR(m_string_in_str), \ OUT_SERIAL(m_string_out_serial), IN_SERIAL(m_string_in_serial), \ @@ -2197,7 +2197,7 @@ namespace m_lib { /* Set the string a to the string (or C string) b */ #define m_string_set(a,b) M_STR1NG_SELECT2(m_string_set, m_string_set_cstr, a, b) -/* Concatene the string (or C string) b to the string a */ +/* Concatenate the string (or C string) b to the string a */ #define m_string_cat(a,b) M_STR1NG_SELECT2(m_string_cat, m_string_cat_cstr, a, b) /* Compare the string a to the string (or C string) b and return the sort order */ @@ -2273,7 +2273,7 @@ namespace m_lib { HASH(M_F(name,_hash)), \ EQUAL(M_F(name,_equal_p)), \ CMP(M_F(name,_cmp)), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ OOR_EQUAL(M_F(name,_oor_equal_p)), \ OOR_SET(M_F(name, _oor_set)), \ PARSE_STR(M_F(name,_parse_str)), \ diff --git a/m-tuple.h b/m-tuple.h index 55edc1ae..5a0864bc 100644 --- a/m-tuple.h +++ b/m-tuple.h @@ -750,7 +750,7 @@ namespace m_lib { SET(M_F(name,_set)), \ CLEAR(M_F(name, _clear)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ OPLIST( (__VA_ARGS__) ), \ M_IF_METHOD_ALL(CMP, __VA_ARGS__)(CMP(M_F(name, _cmp)),), \ M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \ diff --git a/m-variant.h b/m-variant.h index 1644bc19..2aab7b28 100644 --- a/m-variant.h +++ b/m-variant.h @@ -774,7 +774,7 @@ M_INLINE void \ CLEAR(M_F(name, _clear)), \ RESET(M_F(name, _reset)), \ NAME(name), \ - TYPE(M_F(name,_ct)), \ + TYPE(M_F(name,_ct)), GENTYPE(struct M_F(name,_s)*), \ EMPTY_P(M_F(name,_empty_p)), \ M_IF_METHOD_ALL(HASH, __VA_ARGS__)(HASH(M_F(name, _hash)),), \ M_IF_METHOD_ALL(EQUAL, __VA_ARGS__)(EQUAL(M_F(name, _equal_p)),), \ diff --git a/tests/test-mgeneric.c b/tests/test-mgeneric.c index d4678891..eb4f23e2 100644 --- a/tests/test-mgeneric.c +++ b/tests/test-mgeneric.c @@ -53,8 +53,6 @@ const string_t gx; CLEAR(M_NOTHING_DEFAULT) ) #define INT1 (GENTYPE(int), TYPE(int), INIT(M_INIT_BASIC), INIT_SET(M_SET_BASIC), SET(M_SET_BASIC), \ CLEAR(M_NOTHING_DEFAULT) ) -#define ARRAY1 M_OPEXTEND(M_OPL_array_int_t(), GENTYPE(struct array_int_s *)) -#define LIST1 M_OPEXTEND(M_OPL_list_int_t(), GENTYPE(struct list_int_s **)) //NOTE: string_t is registered by the CORE component of the MLIB organization @@ -65,9 +63,9 @@ const string_t gx; #define M_GENERIC_ORG_3() (ARRAY) #define M_GENERIC_ORG_ARRAY_COMP_5() (AINT) -#define M_GENERIC_ORG_ARRAY_COMP_AINT_OPLIST_6() ARRAY1 +#define M_GENERIC_ORG_ARRAY_COMP_AINT_OPLIST_6() M_OPL_array_int_t() #define M_GENERIC_ORG_ARRAY_COMP_10() (LINT) -#define M_GENERIC_ORG_ARRAY_COMP_LINT_OPLIST_30() LIST1 +#define M_GENERIC_ORG_ARRAY_COMP_LINT_OPLIST_30() M_OPL_list_int_t() static bool test_empty(const string_t p) { From c98786a3b3b94348a034167e50a8bed6e88e9cff Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 16:22:20 +0100 Subject: [PATCH 13/16] Update TODO items --- m-generic.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/m-generic.h b/m-generic.h index 91e213ed..299111d6 100644 --- a/m-generic.h +++ b/m-generic.h @@ -293,7 +293,9 @@ typedef const char **m_g3n_cstring_end; it_next(l_it)) -// TODO: init_with ? How to handle the different type of parameters ? emplace ? +// TODO: init_with ? How to handle the different type of parameters ? Using EMPLACE_TYPE ? +// TODO: support of PUSH_EMPLACE ? +// TODO: support of "let" directly to avoid using the post-processing registration of M_LET? // Overwrite M_FPRINT & M_PRINT to support global oplists registration #define M_FPRINT_ARG_G3N(f, x) \ From 76ef061dec97bbf80c684f4494e7354b3730ef51 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 16:22:34 +0100 Subject: [PATCH 14/16] Add basic example of generic interface --- example/ex11-generic01.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 example/ex11-generic01.c diff --git a/example/ex11-generic01.c b/example/ex11-generic01.c new file mode 100644 index 00000000..36abfb1f --- /dev/null +++ b/example/ex11-generic01.c @@ -0,0 +1,25 @@ +#include "m-list.h" +#include "m-generic.h" + +LIST_DEF(list_uint, unsigned int) +// Register for M_LET & M_EACH +#define M_OPL_list_uint_t() LIST_OPLIST(list_uint, M_BASIC_OPLIST) +// Register for Generic: +#define M_GENERIC_ORG_1() (USER) +#define M_GENERIC_ORG_USER_COMP_1() (CORE) +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() M_OPL_list_uint_t() + +int main(void) +{ + M_LET(list, list_uint_t) { + // We can push in the list. It will call the registered method. + push(list, 42); + push(list, 17); + // We can iterate over the list + for each(item, list) { + M_PRINT("ITEM=", *item, "\n"); + } + // We can print directly the list. It will use the registered method + M_PRINT("List = ", list, "\n"); + } +} From ed0f538fb7e1d00de869c537c7000c64a165bdc4 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 16:22:46 +0100 Subject: [PATCH 15/16] Add documentation of generic interface --- README.md | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eebd71b8..6a62f7d1 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ M\*LIB: Generic type-safe Container Library for C language 27. [Memory pool](#m-mempool) 28. [JSON Serialization](#m-serial-json) 29. [Binary Serialization](#m-serial-bin) + 30. [Generic interface](#m-generic) 12. [Global User Customization](#global-user-customization) 13. [License](#license) @@ -179,6 +180,7 @@ Other headers offering other functionality are: * [m-worker.h](#m-worker): header for providing an easy pool of workers on separated threads to handle work orders (used for parallel tasks), * [m-serial-json.h](#m-serial-json): header for importing / exporting the containers in [JSON format](https://en.wikipedia.org/wiki/JSON), * [m-serial-bin.h](#m-serial-bin): header for importing / exporting the containers in an adhoc fast binary format, +* [m-generic.h](#m-generic): header for using a common interface for all registered types, * m-genint.h: internal header for generating unique integers in a concurrent context, * [m-core.h](#m-core): header for meta-programming with the C preprocessor (used by all other headers). @@ -492,15 +494,17 @@ of the library. The final goal of the library is to be able to write code like this in pure C while keeping type safety and compile time name resolution: ```C - let(list, list_uint_t) { + M_LET(list, list_uint_t) { push(list, 42); push(list, 17); for each (item, list) { - print(item, "\n"); + M_PRINT(*item, "\n"); } } ``` +See #M-GENERIC header for details. + ## Performance M\*LIB performance is compared to the one of GNU C++ STL (v10.2) in the following graphs. @@ -8411,6 +8415,171 @@ otherwise the behavior of the object is undefined. Clear the serialization object 'serial'. +### M-GENERIC + +This header is for registering and using a generic interface, regardless of the real type. +More precisely it provides way of registering the oplist of a type. Then a variable of this type can be used in macro-like functions (init, clear, push, ...) and the associated method of this oplist will be used to handle the variable. +If no type is associated to this variable, an error is reported by the compiler. + +This header needs a C23 compliant compiler or a C11 compiler providing the typeof extension (like GCC, CLANG, MSVC). +NOTE: TCC is not supported yet due to some bugs in the compiler. + +This provide the same level of flexibility of the C++. However there is some drawbacks of using this generic interface: + +* error messages can be more complex, +* compilation time increase a lot (on pair with C++) + +This header is still a WIP and is currently more a demo (Not ready for production). + +#### Example + +A concrete example of the generic header is the following. +It reuses the basic example of the introduction of the library. + +```C +#include "m-list.h" +#include "m-generic.h" + +LIST_DEF(list_uint, unsigned int) + +// Register oplist for M_LET & M_EACH : +#define M_OPL_list_uint_t() LIST_OPLIST(list_uint, M_BASIC_OPLIST) + +// Register for Generic (Organization, Component & Oplist) +#define M_GENERIC_ORG_1() (USER) +#define M_GENERIC_ORG_USER_COMP_1() (CORE) +#define M_GENERIC_ORG_USER_COMP_CORE_OPLIST_1() M_OPL_list_uint_t() + +int main(void) +{ + M_LET(list, list_uint_t) { + push(list, 42); + push(list, 17); + for each(item, list) { + M_PRINT("ITEM=", *item, "\n"); + } + M_PRINT("List = ", list, "\n"); + } +} +``` + + +#### Registration + +To register an oplist you need to register three things: + +* the *integration user* (user responsible for the integration of the whole program) registers all the *organizations* by mapping an integer from 1 to 50 to the short name of the organization. +* the *organization user* (user responsible for the integration produced by an organization, typically a library or the program except its libraries) chooses the short name of the organization and registers all the *components* of this organization by mapping an integer from 1 to 50 to the short name of the component. +* the *component user* (user responsible for a component of a library or a program) registers all the *oplists* of this component by mapping an integer from 1 to 50 to the oplist. + +This 3 levels registration is used to enable support for complex integration in real project. So, it maps the basic structure of a program so that each independent layer can register its own oplists independently of the others, and only the final integrator maps everything together. + +To register an organization the *integration user* shall define a macro like this for all organizations: + +```C +#define M_GENERIC_ORG_() () +``` +with + +* replaced by a unique integer in the range 1 to 50 +* replaced by the short name of the organization. + + +To register a component the *organization user* shall define a macro like this for all components: + +```C +#define M_GENERIC_ORG__COMP_() () +``` +with + +* replaced by a unique integer in the range 1 to 50 +* replaced by the short name of the organization. +* replaced by the short name of the component. + + +To register an oplist the *component user* shall define a macro like this for all oplists: + +```C +#define M_GENERIC_ORG__COMP__OPLIST_() +``` +with + +* replaced by a unique integer in the range 1 to 50 +* replaced by the short name of the organization. +* replaced by the short name of the component. +* replaced by the oplist (no need for extra parenthesis). + + +Theses macros should be defined in the header associated to their responsibility. +To register something, you don't need to include the m-register header file. + + +#### Usage + +The following macro functions are defined. They have the same behavior as their corresponding OPERATOR of the oplists: + +* init(x) +* init_set(x, y) +* init_move(x, y) +* move(x, y) +* set(x, y) +* clear(x) +* swap(x, y) +* hash(x) +* equal(x, y) +* cmp(x, y) +* sort(x) +* splice_back(d, s, i) +* splice_at(d, id, s, is) +* it_first(x, y) +* it_last(x, y) +* it_end(x, y) +* it_set(d, s) +* it_end_p(i) +* it_last_p(i) +* it_next(i) +* it_previous(i) +* it_ref(i) +* it_cref(i) +* it_equal(x, y) +* it_insert(c, i, o) +* it_remove(c, i) +* empty_p(x) +* add(x, y) +* sub(x, y) +* mul(x, y) +* div(x, y) +* reset(x) +* get(x, y) +* set_at(x, y, z) +* safe_get(x, y) +* erase(x, y) +* get_size(x) +* push(x, y) +* pop(x, y) +* push_move(x, y) +* pop_move(x, y) +* reverse(x) +* get_str(s, c, b) +* parse_str(c, s, e) +* out_str(x, y) +* in_str(x, y) +* out_serial(x, y) +* in_serial(x, y) + +The following macro are defined and represents a type associated to the variable. They have the same behavior as their corresponding OPERATOR of the oplists and use the typeof keyword: + +* it_type(x) +* sub_type(x) +* key_type(x) +* value_type(x) + +The following macro is defined and enabled to iterate over a container (See example): + +* each(item, container) + +Note that only short names are defined in this header which may be incompatible with your own namespace (This header is still a WIP) + ## Global User Customization From 127530abaaac3f1693e87325e86f94807d199d7c Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 30 Dec 2023 16:27:25 +0100 Subject: [PATCH 16/16] Fix format --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6a62f7d1..75b7b757 100644 --- a/README.md +++ b/README.md @@ -8469,7 +8469,7 @@ int main(void) To register an oplist you need to register three things: * the *integration user* (user responsible for the integration of the whole program) registers all the *organizations* by mapping an integer from 1 to 50 to the short name of the organization. -* the *organization user* (user responsible for the integration produced by an organization, typically a library or the program except its libraries) chooses the short name of the organization and registers all the *components* of this organization by mapping an integer from 1 to 50 to the short name of the component. +* the *organization user* (user responsible for the integration produced by an organization, typically a library or the program without its libraries) chooses the short name of the organization and registers all the *components* of this organization by mapping an integer from 1 to 50 to the short name of the component. * the *component user* (user responsible for a component of a library or a program) registers all the *oplists* of this component by mapping an integer from 1 to 50 to the oplist. This 3 levels registration is used to enable support for complex integration in real project. So, it maps the basic structure of a program so that each independent layer can register its own oplists independently of the others, and only the final integrator maps everything together. @@ -8477,22 +8477,22 @@ This 3 levels registration is used to enable support for complex integration in To register an organization the *integration user* shall define a macro like this for all organizations: ```C -#define M_GENERIC_ORG_() () +#define M_GENERIC_ORG_() () ``` with -* replaced by a unique integer in the range 1 to 50 +* replaced by a unique integer in the range 1 to 50 * replaced by the short name of the organization. To register a component the *organization user* shall define a macro like this for all components: ```C -#define M_GENERIC_ORG__COMP_() () +#define M_GENERIC_ORG__COMP_() () ``` with -* replaced by a unique integer in the range 1 to 50 +* replaced by a unique integer in the range 1 to 50 * replaced by the short name of the organization. * replaced by the short name of the component. @@ -8500,14 +8500,14 @@ with To register an oplist the *component user* shall define a macro like this for all oplists: ```C -#define M_GENERIC_ORG__COMP__OPLIST_() +#define M_GENERIC_ORG__COMP__OPLIST_() ``` with -* replaced by a unique integer in the range 1 to 50 +* replaced by a unique integer in the range 1 to 50 * replaced by the short name of the organization. * replaced by the short name of the component. -* replaced by the oplist (no need for extra parenthesis). +* replaced by the oplist (no need for extra parenthesis). Theses macros should be defined in the header associated to their responsibility.