diff --git a/mysql-test/suite/villagesql/std_data/bad_type_funcs.cc b/mysql-test/suite/villagesql/std_data/bad_type_funcs.cc index 434a75ed756e..a39a67883cee 100644 --- a/mysql-test/suite/villagesql/std_data/bad_type_funcs.cc +++ b/mysql-test/suite/villagesql/std_data/bad_type_funcs.cc @@ -34,23 +34,14 @@ void f1_impl(std::string_view from, vsql::CustomResult out) { } // Generic TO_STRING function -bool f2_impl(vsql::Span buf, - vsql::Span to, size_t *to_length) { - (void)buf; - - if (to.size() < 4) { - return true; - } - - strcpy(to.data(), "val"); - *to_length = 3; - return false; +void f2_impl(vsql::CustomArg in, vsql::StringResult out) { + (void)in; + out.set("val"); } // Generic COMPARE function -int f3_impl(vsql::Span a, - vsql::Span b) { - return memcmp(a.data(), b.data(), 16); +int f3_impl(vsql::CustomArg a, vsql::CustomArg b) { + return memcmp(a.value().data(), b.value().data(), 16); } static constexpr const char kTestBadTypeName[] = "TESTBADTYPE"; diff --git a/mysql-test/suite/villagesql/std_data/complex3_encode.cc b/mysql-test/suite/villagesql/std_data/complex3_encode.cc index 2608f0273699..00b114008f96 100644 --- a/mysql-test/suite/villagesql/std_data/complex3_encode.cc +++ b/mysql-test/suite/villagesql/std_data/complex3_encode.cc @@ -37,25 +37,13 @@ void complex3_from_string(std::string_view from, vsql::CustomResult out) { } // TO_STRING: always return "(0,0)" -bool complex3_to_string(vsql::Span buf, - vsql::Span to, size_t *to_length) { - (void)buf; // Unused - always return "(0,0)" - - const char *result = "(0,0)"; - size_t len = strlen(result); - - if (to.size() < len + 1) { - return true; // Error - } - - memcpy(to.data(), result, len); - *to_length = len; - return false; // Success +void complex3_to_string(vsql::CustomArg in, vsql::StringResult out) { + (void)in; // Unused - always return "(0,0)" + out.set("(0,0)"); } // COMPARE: always returns 0 (equal) -int complex3_compare(vsql::Span a, - vsql::Span b) { +int complex3_compare(vsql::CustomArg a, vsql::CustomArg b) { (void)a; (void)b; return 0; diff --git a/mysql-test/suite/villagesql/std_data/missing_params_fn.cc b/mysql-test/suite/villagesql/std_data/missing_params_fn.cc index c681ad14d9f3..dd5e67604185 100644 --- a/mysql-test/suite/villagesql/std_data/missing_params_fn.cc +++ b/mysql-test/suite/villagesql/std_data/missing_params_fn.cc @@ -43,20 +43,21 @@ void faketype_encode(vsql::MaybeParams &, std::string_view from, out.set_length(n); } -bool faketype_decode(vsql::Span data, - vsql::Span out, size_t *out_len) { - size_t n = data.size() < out.size() ? data.size() : out.size(); - memcpy(out.data(), data.data(), n); - *out_len = n; - return false; +void faketype_decode(vsql::CustomArg in, vsql::StringResult out) { + auto data = in.value(); + auto buf = out.buffer(); + size_t n = data.size() < buf.size() ? data.size() : buf.size(); + memcpy(buf.data(), data.data(), n); + out.set_length(n); } -int faketype_compare(vsql::Span a, - vsql::Span b) { - size_t n = a.size() < b.size() ? a.size() : b.size(); - int r = memcmp(a.data(), b.data(), n); +int faketype_compare(vsql::CustomArg a, vsql::CustomArg b) { + auto va = a.value(); + auto vb = b.value(); + size_t n = va.size() < vb.size() ? va.size() : vb.size(); + int r = memcmp(va.data(), vb.data(), n); if (r != 0) return r; - return static_cast(a.size()) - static_cast(b.size()); + return static_cast(va.size()) - static_cast(vb.size()); } static constexpr const char kFakeTypeName[] = "FAKETYPE"; diff --git a/mysql-test/suite/villagesql/std_data/no_default_type.cc b/mysql-test/suite/villagesql/std_data/no_default_type.cc index 67a7d5b5959c..ec4c51afde77 100644 --- a/mysql-test/suite/villagesql/std_data/no_default_type.cc +++ b/mysql-test/suite/villagesql/std_data/no_default_type.cc @@ -41,19 +41,16 @@ void no_default_encode(std::string_view from, vsql::CustomResult out) { out.set_length(static_cast(kLen)); } -bool no_default_decode(vsql::Span buffer, - vsql::Span out, size_t *out_len) { - if (buffer.size() < static_cast(kLen)) return true; - int written = snprintf(out.data(), out.size(), "(%u)", buffer[0]); - if (written < 0) return true; - *out_len = static_cast(written); - return false; +void no_default_decode(vsql::CustomArg in, vsql::StringResult out) { + auto buffer = in.value(); + if (buffer.size() < static_cast(kLen)) return; // default ERROR + auto buf = out.buffer(); + int written = snprintf(buf.data(), buf.size(), "(%u)", buffer[0]); + if (written < 0) return; + out.set_length(static_cast(written)); } -int no_default_compare(vsql::Span, - vsql::Span) { - return 0; -} +int no_default_compare(vsql::CustomArg, vsql::CustomArg) { return 0; } static constexpr const char kNoDefaultTypeName[] = "NO_DEFAULT_TYPE"; diff --git a/mysql-test/suite/villagesql/std_data/testtype_full.cc b/mysql-test/suite/villagesql/std_data/testtype_full.cc index 7aad525bf3cb..df254c2e36dd 100644 --- a/mysql-test/suite/villagesql/std_data/testtype_full.cc +++ b/mysql-test/suite/villagesql/std_data/testtype_full.cc @@ -39,18 +39,18 @@ void encode_testtype(std::string_view from, vsql::CustomResult out) { out.set_length(kTestTypeLen); } -bool decode_testtype_full(vsql::Span from, - vsql::Span to, size_t *to_length) { +void decode_testtype_full(vsql::CustomArg in, vsql::StringResult out) { + auto from = in.value(); double real, imag; memcpy(&real, from.data(), 8); memcpy(&imag, from.data() + 8, 8); - *to_length = snprintf(to.data(), to.size(), "(%.17g,%.17g)", real, imag); - return false; + auto to = out.buffer(); + size_t len = snprintf(to.data(), to.size(), "(%.17g,%.17g)", real, imag); + out.set_length(len); } -int cmp_testtype(vsql::Span l, - vsql::Span r) { - return memcmp(l.data(), r.data(), kTestTypeLen); +int cmp_testtype(vsql::CustomArg l, vsql::CustomArg r) { + return memcmp(l.value().data(), r.value().data(), kTestTypeLen); } static constexpr const char kTestTypeName[] = "TESTTYPE"; diff --git a/mysql-test/suite/villagesql/std_data/testtype_short.cc b/mysql-test/suite/villagesql/std_data/testtype_short.cc index 6f8aaf1e5599..46985a37c522 100644 --- a/mysql-test/suite/villagesql/std_data/testtype_short.cc +++ b/mysql-test/suite/villagesql/std_data/testtype_short.cc @@ -39,18 +39,18 @@ void encode_testtype(std::string_view from, vsql::CustomResult out) { out.set_length(kTestTypeLen); } -bool decode_testtype_short(vsql::Span from, - vsql::Span to, size_t *to_length) { +void decode_testtype_short(vsql::CustomArg in, vsql::StringResult out) { + auto from = in.value(); double real, imag; memcpy(&real, from.data(), 8); memcpy(&imag, from.data() + 8, 8); - *to_length = snprintf(to.data(), to.size(), "(%.6g,%.6g)", real, imag); - return false; + auto to = out.buffer(); + size_t len = snprintf(to.data(), to.size(), "(%.6g,%.6g)", real, imag); + out.set_length(len); } -int cmp_testtype(vsql::Span a, - vsql::Span b) { - return memcmp(a.data(), b.data(), kTestTypeLen); +int cmp_testtype(vsql::CustomArg a, vsql::CustomArg b) { + return memcmp(a.value().data(), b.value().data(), kTestTypeLen); } static constexpr const char kTestTypeName[] = "TESTTYPE"; diff --git a/villagesql/examples/vsql-complex/src/complex.cc b/villagesql/examples/vsql-complex/src/complex.cc index 97cfeaceb65c..04569b99e6e5 100644 --- a/villagesql/examples/vsql-complex/src/complex.cc +++ b/villagesql/examples/vsql-complex/src/complex.cc @@ -135,22 +135,23 @@ void complex2_from_string(std::string_view from, vsql::CustomResult out) { // Decode: 16 bytes -> "(real,imag)" string // COMPLEX -> STRING -bool complex_to_string(vsql::Span data, - vsql::Span out, size_t *out_len) { - if (data.size() != kComplexSize) return true; +void complex_to_string(CustomArg in, StringResult out) { + auto data = in.value(); + if (data.size() != kComplexSize) return; // wrapper default ERROR Complex cx = load_complex(data.data()); - int written = snprintf(out.data(), out.size(), "(%g,%g)", cx.re, cx.im); - if (written < 0 || static_cast(written) >= out.size()) return true; - *out_len = static_cast(written); - return false; + auto buf = out.buffer(); + int written = snprintf(buf.data(), buf.size(), "(%g,%g)", cx.re, cx.im); + if (written < 0 || static_cast(written) >= buf.size()) return; + out.set_length(static_cast(written)); } // Compare: (COMPLEX, COMPLEX) -> INT for ORDER BY, indexes -int complex_compare(vsql::Span a, - vsql::Span b) { - if (a.size() != kComplexSize || b.size() != kComplexSize) return 0; - Complex lhs = load_complex(a.data()); - Complex rhs = load_complex(b.data()); +int complex_compare(CustomArg a, CustomArg b) { + auto da = a.value(); + auto db = b.value(); + if (da.size() != kComplexSize || db.size() != kComplexSize) return 0; + Complex lhs = load_complex(da.data()); + Complex rhs = load_complex(db.data()); // Compare real parts first if (lhs.re < rhs.re) return -1; @@ -165,7 +166,8 @@ int complex_compare(vsql::Span a, // Canonicalizes -0 to +0 before hashing so that -0.0 and +0.0 hash to the // same bucket. This allows COMPLEX2 to preserve -0 in storage while still // working correctly with hash joins and EXCEPT operations. -size_t complex2_hash(vsql::Span data) { +size_t complex2_hash(CustomArg in) { + auto data = in.value(); if (data.size() != kComplexSize) return 0; Complex cx = load_complex(data.data()); cx.canonicalize(); diff --git a/villagesql/examples/vsql-simple/src/extension.cc b/villagesql/examples/vsql-simple/src/extension.cc index 80965591a6c5..db2c6b664ae2 100644 --- a/villagesql/examples/vsql-simple/src/extension.cc +++ b/villagesql/examples/vsql-simple/src/extension.cc @@ -42,18 +42,17 @@ void bytearray_from_string(std::string_view from, CustomResult out) { } // to_string: binary -> string (copy 8 bytes) -bool bytearray_to_string(Span data, Span out, - size_t *out_len) { - if (data.size() < kBytearrayLen || out.size() < kBytearrayLen) return true; - memcpy(out.data(), data.data(), kBytearrayLen); - *out_len = kBytearrayLen; - return false; // success +void bytearray_to_string(CustomArg in, StringResult out) { + auto data = in.value(); + auto buf = out.buffer(); + if (data.size() < kBytearrayLen || buf.size() < kBytearrayLen) return; + memcpy(buf.data(), data.data(), kBytearrayLen); + out.set_length(kBytearrayLen); } // Compare: lexicographic byte comparison -int bytearray_compare(Span a, - Span b) { - return memcmp(a.data(), b.data(), kBytearrayLen); +int bytearray_compare(CustomArg a, CustomArg b) { + return memcmp(a.value().data(), b.value().data(), kBytearrayLen); } // ROT13: apply ROT13 cipher to ASCII letters diff --git a/villagesql/examples/vsql-tvector/src/tvector.cc b/villagesql/examples/vsql-tvector/src/tvector.cc index 647690407429..9ad542264508 100644 --- a/villagesql/examples/vsql-tvector/src/tvector.cc +++ b/villagesql/examples/vsql-tvector/src/tvector.cc @@ -271,39 +271,39 @@ void tvector_from_string(vsql::MaybeParams &p, // Decode: N * bpe bytes binary -> "[v1,v2,...,vN]" string. // TVECTOR -> STRING // Dimension and element type are read from type parameters. -bool tvector_to_string(const TVectorParams &p, - vsql::Span data, - vsql::Span out, size_t *out_len) { +void tvector_to_string(vsql::CustomArgWith in, + vsql::StringResult out) { + const TVectorParams &p = in.params(); + auto data = in.value(); const size_t bpe = p.bytes_per_elem; - if (data.size() != static_cast(p.dimension) * bpe) return true; + if (data.size() != static_cast(p.dimension) * bpe) return; + auto buf = out.buffer(); size_t pos = 0; - if (pos >= out.size()) return true; - out[pos++] = '['; + if (pos >= buf.size()) return; + buf[pos++] = '['; for (size_t i = 0; i < static_cast(p.dimension); i++) { if (i > 0) { - if (pos >= out.size()) return true; - out[pos++] = ','; + if (pos >= buf.size()) return; + buf[pos++] = ','; } int written; if (bpe == 8) { double val = load_double(data.data() + i * bpe); - written = snprintf(out.data() + pos, out.size() - pos, "%.17g", val); + written = snprintf(buf.data() + pos, buf.size() - pos, "%.17g", val); } else { float val = load_float(data.data() + i * bpe); - written = snprintf(out.data() + pos, out.size() - pos, "%g", val); + written = snprintf(buf.data() + pos, buf.size() - pos, "%g", val); } - if (written < 0 || pos + static_cast(written) >= out.size()) - return true; + if (written < 0 || pos + static_cast(written) >= buf.size()) return; pos += static_cast(written); } - if (pos >= out.size()) return true; - out[pos++] = ']'; + if (pos >= buf.size()) return; + buf[pos++] = ']'; - *out_len = pos; - return false; + out.set_length(pos); } // Compare: (TVECTOR, TVECTOR) -> INT for ORDER BY, indexes. @@ -311,17 +311,20 @@ bool tvector_to_string(const TVectorParams &p, // TODO(villagesql-performance): we can also consider having templated versions // of these functions instead of using branches, then selecting the version to // use with one branch. -int tvector_compare(const TVectorParams &p, vsql::Span a, - vsql::Span b) { +int tvector_compare(vsql::CustomArgWith a, + vsql::CustomArgWith b) { + const TVectorParams &p = a.params(); + const unsigned char *da = a.value().data(); + const unsigned char *db = b.value().data(); for (int64_t i = 0; i < p.dimension; i++) { if (p.bytes_per_elem == 8) { - double v1 = load_double(a.data() + i * p.bytes_per_elem); - double v2 = load_double(b.data() + i * p.bytes_per_elem); + double v1 = load_double(da + i * p.bytes_per_elem); + double v2 = load_double(db + i * p.bytes_per_elem); if (v1 < v2) return -1; if (v1 > v2) return 1; } else { - float v1 = load_float(a.data() + i * p.bytes_per_elem); - float v2 = load_float(b.data() + i * p.bytes_per_elem); + float v1 = load_float(da + i * p.bytes_per_elem); + float v2 = load_float(db + i * p.bytes_per_elem); if (v1 < v2) return -1; if (v1 > v2) return 1; } diff --git a/villagesql/sdk/include/villagesql/vsql/func_builder.h b/villagesql/sdk/include/villagesql/vsql/func_builder.h index 925197932891..d90c06917b81 100644 --- a/villagesql/sdk/include/villagesql/vsql/func_builder.h +++ b/villagesql/sdk/include/villagesql/vsql/func_builder.h @@ -215,27 +215,52 @@ template using TypeEncodeWithParamsFunc = void (*)(MaybeParams

&, std::string_view from, CustomResult); -// Decode: binary -> string. false=success, true=error. -using TypeDecodeFunc = bool (*)(Span data, Span out, - size_t *out_len); +// Decode: custom -> string. The function reports its outcome by calling +// out.set_length(n), out.set(sv), out.set_null(), out.warning(msg), or +// out.error(msg). If none is called the wrapper falls back to a default +// "failed to decode value" ERROR. +using TypeDecodeFunc = void (*)(CustomArg in, StringResult out); +// Parameterized variant: params come from the CustomArgWith

input. template -using TypeDecodeWithParamsFunc = bool (*)(const P &, - Span data, - Span out, size_t *out_len); +using TypeDecodeWithParamsFunc = void (*)(CustomArgWith

in, + StringResult out); // Compare: returns <0, 0, or >0. -using TypeCompareFunc = int (*)(Span a, - Span b); +using TypeCompareFunc = int (*)(CustomArg a, CustomArg b); template -using TypeCompareWithParamsFunc = int (*)(const P &, - Span a, - Span b); +using TypeCompareWithParamsFunc = int (*)(CustomArgWith

a, + CustomArgWith

b); // Hash: returns hash code. -using TypeHashFunc = size_t (*)(Span data); +using TypeHashFunc = size_t (*)(CustomArg in); template -using TypeHashWithParamsFunc = size_t (*)(const P &, - Span data); +using TypeHashWithParamsFunc = size_t (*)(CustomArgWith

in); + +// Backward-compat (deprecated) signatures. New extensions should use the +// typed shapes above; these aliases let in-tree and external extensions +// migrate over multiple PRs. +// +// TODO(villagesql-beta): drop these once all bundled extensions and the +// vsql-cube / vsql-vector extensions migrate to the typed CustomArg / +// StringResult shapes. +using TypeDecodeFuncOld = bool (*)(Span data, + Span out, size_t *out_len); +template +using TypeDecodeWithParamsFuncOld = bool (*)(const P &, + Span data, + Span out, size_t *out_len); + +using TypeCompareFuncOld = int (*)(Span a, + Span b); +template +using TypeCompareWithParamsFuncOld = int (*)(const P &, + Span a, + Span b); + +using TypeHashFuncOld = size_t (*)(Span data); +template +using TypeHashWithParamsFuncOld = size_t (*)(const P &, + Span data); // intrinsic_default: returns string representation of the default value. using IntrinsicDefaultFunc = std::string (*)(char *error_msg); @@ -412,8 +437,30 @@ struct TypeEncodeVdfWrapper { } }; -// TypeDecodeVdfWrapper: wraps TypeDecodeFunc into a VDF. +// Pre-fills result->type / result->error_msg with a default "failed to +// decode value" ERROR. The decode wrappers call this before +// invoking the extension's to_string so that an extension that early-returns +// without setting an outcome still surfaces a useful error. +// +// TODO(villagesql-beta): tighten the contract so every to_string explicitly +// sets an outcome (set_length / set / set_null / warning / error) on every +// path, then drop this synthesis and treat the silent-return case as an SDK +// bug. Mirrors the same TODO on set_default_encode_failure. +inline void set_default_decode_failure(vef_vdf_result_t *result) { + result->type = VEF_RESULT_ERROR; + snprintf(result->error_msg, VEF_MAX_ERROR_LEN, "failed to decode value"); +} + +// TypeDecodeVdfWrapper: wraps TypeDecodeFunc / TypeDecodeFuncOld into a VDF. // VDF signature: (CUSTOM(type)) -> STRING. +// +// New signature (TypeDecodeFunc): pre-sets the result to VEF_RESULT_ERROR +// with a default "failed to decode value" message; the extension can override +// by calling out.set_length / out.set / out.set_null / out.warning / +// out.error. +// +// Old signature (TypeDecodeFuncOld): bool return is treated as true=error +// (synthesizes the same default error), false=success. template struct TypeDecodeVdfWrapper { static void invoke(vef_context_t *ctx, vef_vdf_args_t *args, @@ -423,21 +470,25 @@ struct TypeDecodeVdfWrapper { result->type = VEF_RESULT_NULL; return; } - size_t out_len; - bool failed = Func({arg.bin_value, arg.bin_len}, - {result->str_buf, result->max_str_len}, &out_len); - if (failed) { - result->type = VEF_RESULT_ERROR; - snprintf(result->error_msg, VEF_MAX_ERROR_LEN, "failed to decode value"); - return; + if constexpr (std::is_same_v) { + size_t out_len; + bool failed = Func({arg.bin_value, arg.bin_len}, + {result->str_buf, result->max_str_len}, &out_len); + if (failed) { + set_default_decode_failure(result); + return; + } + result->type = VEF_RESULT_VALUE; + result->actual_len = out_len; + } else { + set_default_decode_failure(result); + Func(CustomArg(&arg), StringResult(result)); } - result->type = VEF_RESULT_VALUE; - result->actual_len = out_len; } }; -// TypeCompareVdfWrapper: wraps TypeCompareFunc into a VDF. -// VDF signature: (CUSTOM(type), CUSTOM(type)) -> INT. +// TypeCompareVdfWrapper: wraps TypeCompareFunc / TypeCompareFuncOld into a +// VDF. VDF signature: (CUSTOM(type), CUSTOM(type)) -> INT. template struct TypeCompareVdfWrapper { static void invoke(vef_context_t *ctx, vef_vdf_args_t *args, @@ -448,13 +499,17 @@ struct TypeCompareVdfWrapper { result->type = VEF_RESULT_NULL; return; } - result->int_value = - Func({a.bin_value, a.bin_len}, {b.bin_value, b.bin_len}); + if constexpr (std::is_same_v) { + result->int_value = + Func({a.bin_value, a.bin_len}, {b.bin_value, b.bin_len}); + } else { + result->int_value = Func(CustomArg(&a), CustomArg(&b)); + } result->type = VEF_RESULT_VALUE; } }; -// TypeHashVdfWrapper: wraps TypeHashFunc into a VDF. +// TypeHashVdfWrapper: wraps TypeHashFunc / TypeHashFuncOld into a VDF. // VDF signature: (CUSTOM(type)) -> INT. template struct TypeHashVdfWrapper { @@ -465,8 +520,12 @@ struct TypeHashVdfWrapper { result->type = VEF_RESULT_NULL; return; } - result->int_value = - static_cast(Func({arg.bin_value, arg.bin_len})); + if constexpr (std::is_same_v) { + result->int_value = + static_cast(Func({arg.bin_value, arg.bin_len})); + } else { + result->int_value = static_cast(Func(CustomArg(&arg))); + } result->type = VEF_RESULT_VALUE; } }; @@ -559,10 +618,24 @@ struct TypeEncodeWithCacheVdfWrapper { } }; +// Recovers P from the first argument of a decode/compare/hash-with-params +// function. After cv/ref stripping the first arg is either: +// New: CustomArgWith

-> extracts P +// Old: P (from const P&) -> identity +template +struct ExtractDchParamsType { + using type = T; +}; +template +struct ExtractDchParamsType> { + using type = P; +}; + template struct TypeDecodeWithCacheVdfWrapper { - using P = std::remove_cv_t::type>>>; + using P = typename ExtractDchParamsType::type; static void invoke(vef_context_t *ctx, vef_vdf_args_t *args, vef_vdf_result_t *result) { @@ -571,24 +644,30 @@ struct TypeDecodeWithCacheVdfWrapper { result->type = VEF_RESULT_NULL; return; } - const P &p = type_params_cache_for

().get(arg.type_params); - size_t out_len; - bool failed = Func(p, {arg.bin_value, arg.bin_len}, - {result->str_buf, result->max_str_len}, &out_len); - if (failed) { - result->type = VEF_RESULT_ERROR; - snprintf(result->error_msg, VEF_MAX_ERROR_LEN, "failed to decode value"); - return; + if constexpr (std::is_same_v>) { + const P &p = type_params_cache_for

().get(arg.type_params); + size_t out_len; + bool failed = Func(p, {arg.bin_value, arg.bin_len}, + {result->str_buf, result->max_str_len}, &out_len); + if (failed) { + set_default_decode_failure(result); + return; + } + result->type = VEF_RESULT_VALUE; + result->actual_len = out_len; + } else { + set_default_decode_failure(result); + Func(CustomArgWith

(&arg), StringResult(result)); } - result->type = VEF_RESULT_VALUE; - result->actual_len = out_len; } }; template struct TypeCompareWithCacheVdfWrapper { - using P = std::remove_cv_t::type>>>; + using P = typename ExtractDchParamsType::type; static void invoke(vef_context_t *ctx, vef_vdf_args_t *args, vef_vdf_result_t *result) { @@ -598,17 +677,23 @@ struct TypeCompareWithCacheVdfWrapper { result->type = VEF_RESULT_NULL; return; } - const P &p = type_params_cache_for

().get(a.type_params); - result->int_value = - Func(p, {a.bin_value, a.bin_len}, {b.bin_value, b.bin_len}); + if constexpr (std::is_same_v>) { + const P &p = type_params_cache_for

().get(a.type_params); + result->int_value = + Func(p, {a.bin_value, a.bin_len}, {b.bin_value, b.bin_len}); + } else { + result->int_value = Func(CustomArgWith

(&a), CustomArgWith

(&b)); + } result->type = VEF_RESULT_VALUE; } }; template struct TypeHashWithCacheVdfWrapper { - using P = std::remove_cv_t::type>>>; + using P = typename ExtractDchParamsType::type; static void invoke(vef_context_t *ctx, vef_vdf_args_t *args, vef_vdf_result_t *result) { @@ -617,9 +702,14 @@ struct TypeHashWithCacheVdfWrapper { result->type = VEF_RESULT_NULL; return; } - const P &p = type_params_cache_for

().get(arg.type_params); - result->int_value = - static_cast(Func(p, {arg.bin_value, arg.bin_len})); + if constexpr (std::is_same_v>) { + const P &p = type_params_cache_for

().get(arg.type_params); + result->int_value = + static_cast(Func(p, {arg.bin_value, arg.bin_len})); + } else { + result->int_value = static_cast(Func(CustomArgWith

(&arg))); + } result->type = VEF_RESULT_VALUE; } }; @@ -1173,16 +1263,30 @@ constexpr StaticFuncDesc<1> make_type_encode(const char *name, } // make_type_decode<&fn>("name", TYPE) — (CUSTOM(type)) -> STRING. +// +// Accepts four signatures: +// TypeDecodeFunc (new, non-parameterized) +// TypeDecodeFuncOld (deprecated, non-parameterized) +// TypeDecodeWithParamsFunc

(new, parameterized) +// TypeDecodeWithParamsFuncOld

(deprecated, parameterized) template constexpr StaticFuncDesc<1> make_type_decode(const char *name, const char *type_name) { + using F = decltype(Func); FuncWithMetadata meta{}; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v || + std::is_same_v) { meta.f = &TypeDecodeVdfWrapper::invoke; } else { + using P = typename TypeDecodeWithCacheVdfWrapper::P; + static_assert(std::is_same_v> || + std::is_same_v>, + "make_type_decode: function must match one of " + "TypeDecodeFunc, TypeDecodeFuncOld, " + "TypeDecodeWithParamsFunc

, or " + "TypeDecodeWithParamsFuncOld

."); meta.f = &TypeDecodeWithCacheVdfWrapper::invoke; - meta.check_params_cache_bound = - &is_params_cache_bound::P>; + meta.check_params_cache_bound = &is_params_cache_bound

; } meta.return_type = to_vef_type(STRING); meta.param_types[0] = to_vef_type(type_name); @@ -1193,16 +1297,26 @@ constexpr StaticFuncDesc<1> make_type_decode(const char *name, } // make_type_compare<&fn>("name", TYPE) — (CUSTOM, CUSTOM) -> INT. +// +// Accepts four signatures, mirroring make_type_decode. template constexpr StaticFuncDesc<2> make_type_compare(const char *name, const char *type_name) { + using F = decltype(Func); FuncWithMetadata meta{}; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v || + std::is_same_v) { meta.f = &TypeCompareVdfWrapper::invoke; } else { + using P = typename TypeCompareWithCacheVdfWrapper::P; + static_assert(std::is_same_v> || + std::is_same_v>, + "make_type_compare: function must match one of " + "TypeCompareFunc, TypeCompareFuncOld, " + "TypeCompareWithParamsFunc

, or " + "TypeCompareWithParamsFuncOld

."); meta.f = &TypeCompareWithCacheVdfWrapper::invoke; - meta.check_params_cache_bound = &is_params_cache_bound< - typename TypeCompareWithCacheVdfWrapper::P>; + meta.check_params_cache_bound = &is_params_cache_bound

; } meta.return_type = to_vef_type(INT); meta.param_types[0] = to_vef_type(type_name); @@ -1214,16 +1328,26 @@ constexpr StaticFuncDesc<2> make_type_compare(const char *name, } // make_type_hash<&fn>("name", TYPE) — (CUSTOM(type)) -> INT. +// +// Accepts four signatures, mirroring make_type_decode. template constexpr StaticFuncDesc<1> make_type_hash(const char *name, const char *type_name) { + using F = decltype(Func); FuncWithMetadata meta{}; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v || + std::is_same_v) { meta.f = &TypeHashVdfWrapper::invoke; } else { + using P = typename TypeHashWithCacheVdfWrapper::P; + static_assert(std::is_same_v> || + std::is_same_v>, + "make_type_hash: function must match one of " + "TypeHashFunc, TypeHashFuncOld, " + "TypeHashWithParamsFunc

, or " + "TypeHashWithParamsFuncOld

."); meta.f = &TypeHashWithCacheVdfWrapper::invoke; - meta.check_params_cache_bound = - &is_params_cache_bound::P>; + meta.check_params_cache_bound = &is_params_cache_bound

; } meta.return_type = to_vef_type(INT); meta.param_types[0] = to_vef_type(type_name);