packages feed

bindings-DSL 1.0.25 → 1.1.0

raw patch · 6 files changed

+366/−74 lines, 6 filesdep ~basenew-uploaderPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

Files

Bindings/Utilities.hs view
@@ -1,6 +1,6 @@-module Bindings.Utilities (-	storableCast,-	storableCastArray,+module Bindings.Utilities+  ( storableCast+  , storableCastArray   ) where  import Foreign.C
+ CHANGELOG.md view
@@ -0,0 +1,152 @@+# Changelog++This repository releases eleven packages, each versioned independently under the+PVP and tagged as `<pkg>-<version>` (see [RELEASE.md](RELEASE.md)). Each entry+below names the package and version it shipped in.++## bindings-DSL 1.1.0++- Add `#array2d_field` for members like `char x[8][255]`, which `#array_field`+  miscompiled: it divided the member size by `sizeOf (Ptr CChar)` and produced+  255 rows instead of 8. Declare the type as the row pointer (`Ptr CChar`);+  `peek` returns pointers into the struct, `poke` memmoves each row back.++- `#array_field` and `#union_array_field` element counts are now baked in from C+  (`sizeof member / sizeof member[0]`) instead of dividing by the declared+  Haskell type's `sizeOf` at runtime. Beware: if your declared type's `sizeOf`+  does not match the C element size, the count changes. The old code never read+  past the member; the new code reads `count * sizeOf` bytes and will overrun it+  if the declared type is too big. Correctly declared bindings generate identical+  code.++- The code generated by `#stoptype` now carries `{-# LINE #-}` pragmas pointing+  back into the `.hsc` file, so GHC reports an error in a struct field at that+  field rather than at an unrelated line before the `#starttype` block.++## bindings-gpgme 0.2.0++- Hide the trust item API when building against gpgme 2.0, which removed it+  (deprecated since 1.14), so the binding compiles against both the 1.x and 2.x+  series.++- Add the ECC, ECDSA, ECDH and EDDSA public key algorithm constants, and+  `gpgme_op_delete_ext`.++- Catch up with the API added between gpgme 1.6 and 2.1, which the binding had+  never covered. Key creation and editing (`createkey`, `adduid`, `keysign`,+  `setexpire`, `interact` and friends), context flags (offline, sender, status+  callback, `set_ctx_flag`), the encrypt, decrypt, export and keylist mode flags,+  importing and exporting by key rather than by pattern, TOFU, and the various+  new struct members. Each is guarded on the gpgme version that introduced it, so+  the binding still builds against older releases.++- Expose the key capability and status bits (revoked, expired, can_encrypt,+  secret, ...) as `c'gpgme_key_*`, `c'gpgme_subkey_*` and related accessors. They+  are C bitfields, which have no `offsetof` and so could not be bound as struct+  fields; they now go through `inlines.c`. Previously there was no way to tell+  whether a listed key was revoked or able to encrypt.++- Bind `gpgme_error_from_syserror` through `inlines.c`. It is a static inline in+  `gpgme.h` rather than a symbol in libgpgme, so a `#ccall` linked but failed to+  resolve at load time.++- Add `gpgme_invalid_key_next`. The `next` member of `_gpgme_invalid_key` is+  bound as an embedded struct instead of a pointer, so `invalid_recipients` and+  `invalid_signers` cannot be walked. Fixing the field would change the type of a+  generated accessor and break its callers, so it is left as it is and this is+  added alongside.++## bindings-hdf5 0.1.3++- Hide `H5D_MPIO_FILTERS` on HDF5 1.10 and later, which removed it.++## bindings-libcddb 0.3.1++- Declare `CDDB_CATEGORY` as `const char*`, matching libcddb. The accessor was+  instantiated with `char*`, so the generated return type dropped the const on+  the pointed-to char. GCC warned about that before 14 and rejects it from 14 on.++## All packages++- Every `bindings-*` package accepts `bindings-DSL < 1.2`, and is released so+  that the new bound reaches Hackage. Releases are now tagged per package+  (`bindings-DSL-1.1.0`, `bindings-hdf5-0.1.3`, ...) rather than with a bare+  version, which was ambiguous once more than one package was being released.++- Replace Travis with GitHub Actions, building `bindings-DSL` and+  `bindings-posix` across GHC 9.6 to 9.12, `bindings-gpgme` against the latest+  gpgme 1.x and 2.x, `bindings-libcddb` under GCC 14, and `bindings-hdf5`.++## bindings-DSL 1.0.25++- No changes to the DSL itself. `bindings-posix` stopped assuming that OS X+  provides everything POSIX asks for.++## bindings-DSL 1.0.24++- Add `#num_pattern` and `#fractional_pattern`, for matching numeric constants in+  patterns.++- Support interruptible calls with `#ccall_interruptible`.++- Silence GHC warnings for `Storable` instances of field-less types, printf+  format warnings, and shadowed temporary variables.++## bindings-DSL 1.0.23++- No changes to the DSL itself. `bindings-posix` gained bindings for message+  queues.++## bindings-DSL 1.0.22++- No changes to the DSL itself. `bindings-gpgme` gained pinentry modes, I/O+  wrappers and the subkey curve field; `bindings-hdf5` gained its low level+  interface.++## bindings-DSL 1.0.21++- No changes to the DSL itself. Maintainership passed to John Wiegley.++## bindings-DSL 1.0.20++- Include `<cinttypes>` instead of `<inttypes.h>` under a C++ compiler, which+  otherwise lacks `PRIuMAX` and `PRIdMAX` and fails to expand the macros. Note+  that this requires building the user library with `-std=c++11`.++## bindings-DSL 1.0.19++- Add a change log.++- Support unsafe calls with experimental `#ccall_unsafe`.++## bindings-DSL 1.0.18++This version has a bug. Marked as such in Hackage.++- Introduce a silly bug to solve a problem that does not exist.++## bindings-DSL 1.0.17++- Add `Bindings.Utilities` module for general utilities.++- Change repository to git.++- Don't underestimate size of field arrays with dimension >= 2.++- Change `#callback` to `#callback_t`.++- Keep source of `bindings-*` libraries with `bindings-DSL`.++- Add examples used in tutorial.++## bindings-DSL 1.0.16++- Use correct value for `Storable` alignment instead of copying `sizeOf`.++## bindings-DSL 1.0.15++- New hsc2hs template doesn't include some headers, so include it ourselves.++## bindings-DSL 1.0.14++- Add functions to get pointers to fields from pointers to structures.
− ChangeLog
@@ -1,35 +0,0 @@-Changes in 1.0.19--* Add a change log.--* Support unsafe calls with experimental #ccall_unsafe.--Changes in 1.0.18 (This version has a bug. Marked as such in Hackage)--* Introduce a silly bug to solve a problem that does not exist.--Changes in 1.0.17--* Add Bindings.Utilities module for general utilities.--* Change repository to git.--* Don't underestimate size of field arrays with dimension >= 2.--* Change #callback to #callback_t.--* Keep source of bindings-* libraries with bindings-DSL.--* Add examples used in tutorial.--Changes in 1.0.16--* Use correct value for Storable alignment instead of copying sizeOf.--Changes in 1.0.15--* New hsc2hs template doesn't include some headers, so include it ourselves.--Changes in 1.0.14--* Add functions to get pointers to fields from pointers to structures.
+ README.md view
@@ -0,0 +1,134 @@+# bindings-dsl++[![Hackage](https://img.shields.io/hackage/v/bindings-DSL.svg)](https://hackage.haskell.org/package/bindings-DSL)+[![CI](https://github.com/rethab/bindings-dsl/actions/workflows/ci.yml/badge.svg)](https://github.com/rethab/bindings-dsl/actions/workflows/ci.yml)+![BSD3 License](https://img.shields.io/github/license/rethab/bindings-dsl?label=license)++`bindings-DSL` is a domain specific language for writing Haskell FFI bindings, on+top of [hsc2hs](https://downloads.haskell.org/ghc/latest/docs/users_guide/utils.html#writing-haskell-interfaces-to-c-code-hsc2hs).+It is a set of C macros that describe a C interface, and from which hsc2hs+extracts the Haskell code that mimics it: `#ccall` for a function, `#starttype`+and `#field` for a struct, `#num` for a constant, and so on.++```haskell+#include <bindings.dsl.h>+#include <gpgme.h>++module Bindings.Gpgme where+#strict_import++#num GPGME_PK_RSA++#starttype struct _gpgme_key+#field revoked , CUInt+#field fpr     , CString+#stoptype++#ccall gpgme_get_key , <gpgme_ctx_t> -> CString -> Ptr <gpgme_key_t> -> CInt -> IO <gpgme_error_t>+```++Haskell names are derived from the C names automatically, so there is no naming+scheme to invent and no chance of the two drifting apart: a constant becomes+`c'GPGME_PK_RSA`, a struct becomes a `Storable` instance with `p'_gpgme_key'fpr`+accessors, and a function becomes `c'gpgme_get_key`. Structs are laid out by the+C compiler rather than by hand, which is what makes the bindings portable across+platforms where sizes and padding differ. There are also macros for binding+inline functions and macro functions, which have no symbol to call.++Full documentation, including a tutorial, is on the+[wiki](https://github.com/rethab/bindings-dsl/wiki). Release notes are in+[CHANGELOG.md](CHANGELOG.md).++## Packages++`bindings-DSL` is the DSL itself. The bindings built on top of it are released as+separate packages from this repository:++| Package | Hackage |+| --- | --- |+| [bindings-directfb](bindings-directfb) | [![Hackage](https://img.shields.io/hackage/v/bindings-directfb.svg?label=)](https://hackage.haskell.org/package/bindings-directfb) |+| [bindings-fann](bindings-fann) | [![Hackage](https://img.shields.io/hackage/v/bindings-fann.svg?label=)](https://hackage.haskell.org/package/bindings-fann) |+| [bindings-glib](bindings-glib) | [![Hackage](https://img.shields.io/hackage/v/bindings-glib.svg?label=)](https://hackage.haskell.org/package/bindings-glib) |+| [bindings-gpgme](bindings-gpgme) | [![Hackage](https://img.shields.io/hackage/v/bindings-gpgme.svg?label=)](https://hackage.haskell.org/package/bindings-gpgme) |+| [bindings-gsl](bindings-gsl) | [![Hackage](https://img.shields.io/hackage/v/bindings-gsl.svg?label=)](https://hackage.haskell.org/package/bindings-gsl) |+| [bindings-hdf5](bindings-hdf5) | [![Hackage](https://img.shields.io/hackage/v/bindings-hdf5.svg?label=)](https://hackage.haskell.org/package/bindings-hdf5) |+| [bindings-libcddb](bindings-libcddb) | [![Hackage](https://img.shields.io/hackage/v/bindings-libcddb.svg?label=)](https://hackage.haskell.org/package/bindings-libcddb) |+| [bindings-libffi](bindings-libffi) | [![Hackage](https://img.shields.io/hackage/v/bindings-libffi.svg?label=)](https://hackage.haskell.org/package/bindings-libffi) |+| [bindings-posix](bindings-posix) | [![Hackage](https://img.shields.io/hackage/v/bindings-posix.svg?label=)](https://hackage.haskell.org/package/bindings-posix) |+| [bindings-sqlite3](bindings-sqlite3) | [![Hackage](https://img.shields.io/hackage/v/bindings-sqlite3.svg?label=)](https://hackage.haskell.org/package/bindings-sqlite3) |++## Contributing++Contributions are welcome, whether to the DSL itself or to any of the bindings.+Bugs, missing coverage of a C API, and build problems can be raised in the+[issue tracker](https://github.com/rethab/bindings-dsl/issues); pull requests are+just as welcome. CI builds the DSL and the bindings across several GHC versions+and library releases, so open a pull request and let it run.++## Building bindings-gpgme on macOS with Homebrew++`gpgme.h` includes `<gpg-error.h>`, and Homebrew ships `libgpg-error` as a keg of+its own, so both prefixes have to be on the include and library paths. Passing+only gpgme's prefix leaves the nested include unresolved and the build fails with+a misleading complaint about a missing `gpgme.h`.++```sh+brew install gpgme libgpg-error++cabal build bindings-gpgme \+  --extra-include-dirs="$(brew --prefix gpgme)/include" \+  --extra-include-dirs="$(brew --prefix libgpg-error)/include" \+  --extra-lib-dirs="$(brew --prefix gpgme)/lib" \+  --extra-lib-dirs="$(brew --prefix libgpg-error)/lib"+```++With stack, list both prefixes in `stack.yaml` (`brew --prefix` is+`/opt/homebrew` on Apple silicon and `/usr/local` on Intel):++```yaml+extra-include-dirs:+  - /opt/homebrew/opt/gpgme/include+  - /opt/homebrew/opt/libgpg-error/include+extra-lib-dirs:+  - /opt/homebrew/opt/gpgme/lib+  - /opt/homebrew/opt/libgpg-error/lib+```++## Cross compilation++bindings-DSL does not work with `hsc2hs --cross-compile` (see+[#38](https://github.com/rethab/bindings-dsl/issues/38)). Every construct+(`#strict_import`, `#starttype`, `#ccall`, ...) is a custom hsc2hs construct that+emits Haskell while the generated C program runs. Cross mode never runs that+program: it recognises only a fixed set of built-in directives and rejects+everything else with++```+directive strict_import cannot be handled in cross-compilation mode+```++`--via-asm` does not help, it only changes how cross mode extracts constants. No+change to `bindings.dsl.h` can lift this; it needs hsc2hs to support custom+constructs when cross compiling.++Cross compiling still works if you can execute target binaries, e.g. with+qemu-user plus `binfmt_misc`, or wine for Windows targets. Cabal gets in the way+here: it passes `-x` to hsc2hs whenever the host platform differs from the build+platform, and hsc2hs has no flag to undo that. Point Cabal at a wrapper that+drops the flag and lets the normal compile-and-run path proceed under emulation:++```sh+#!/bin/sh+# hsc2hs-no-cross, used via --with-hsc2hs=/path/to/hsc2hs-no-cross+for a in "$@"; do+  shift+  case "$a" in+    -x|--cross-compile) ;;+    *) set -- "$@" "$a" ;;+  esac+done+exec hsc2hs "$@"+```++Otherwise, write plain `.hsc` without bindings-DSL for the modules you need to+cross compile.
bindings-DSL.cabal view
@@ -1,6 +1,6 @@-cabal-version: >= 1.8+cabal-version: >= 1.10 name: bindings-DSL-homepage: https://github.com/jwiegley/bindings-dsl/wiki+homepage: https://github.com/rethab/bindings-dsl/wiki synopsis:   FFI domain specific language, on top of hsc2hs. description: @@ -13,29 +13,32 @@   to help write bindings to inline functions or macro functions.   Documentation is available at package homepage:   .-  <https://github.com/jwiegley/bindings-dsl/wiki>+  <https://github.com/rethab/bindings-dsl/wiki>   .   The extra module Bindings.Utilities will contain tools that may   be convenient when working with FFI.-version: 1.0.25+version: 1.1.0 license: BSD3 license-file: LICENSE-maintainer: John Wiegley <johnw@newartisans.com>+maintainer: Reto <rethab@protonmail.ch> author: Maurício C. Antunes stability: Stable API, well tested, portable, used in commercial code. build-type: Simple-bug-reports: https://github.com/jwiegley/bindings-dsl/issues+bug-reports: https://github.com/rethab/bindings-dsl/issues category: FFI-extra-source-files: ChangeLog+extra-source-files: README.md , CHANGELOG.md library+  default-language:+    Haskell2010+  include-dirs: .   install-includes: bindings.dsl.h , bindings.cmacros.h-  build-depends: base >= 0 && < 1000+  build-depends: base >= 4 && < 5   exposed-modules: Bindings.Utilities source-repository head   type: git-  location: git://github.com/jwiegley/bindings-dsl+  location: https://github.com/rethab/bindings-dsl   branch: master source-repository this   type: git-  location: git://github.com/jwiegley/bindings-dsl-  tag: 1.0.25+  location: https://github.com/rethab/bindings-dsl+  tag: bindings-DSL-1.1.0
bindings.dsl.h view
@@ -18,6 +18,25 @@ #include <inttypes.h> #endif +static char bc_srcfile[1000] = "";++/* hsc2hs calls hsc_line before every chunk of Haskell output. We keep the+ * original behaviour and additionally remember the file name, so that the+ * code generated by hsc_stoptype can be attributed to the .hsc source. */+#undef hsc_line+#define hsc_line(line,file) \+    { \+     strncpy(bc_srcfile,file,sizeof bc_srcfile - 1); \+     bc_srcfile[sizeof bc_srcfile - 1] = '\0'; \+     printf("{-# LINE %d \"%s\" #-}\n",line,file); \+    } \++#define bc_lineprag(l) \+    { \+     if (bc_srcfile[0]) \+        printf("{-# LINE %d \"%s\" #-}\n",(int)(l),bc_srcfile); \+    } \+ #define hsc_strict_import(dummy) printf( \     "import Foreign.Ptr (Ptr,FunPtr,plusPtr)\n" \     "import Foreign.Ptr (wordPtrToPtr,castPtrToFunPtr)\n" \@@ -26,6 +45,7 @@     "import Foreign.C.String (CString,CStringLen,CWString,CWStringLen)\n" \     "import Foreign.Marshal.Alloc (alloca)\n" \     "import Foreign.Marshal.Array (peekArray,pokeArray)\n" \+    "import Foreign.Marshal.Utils (moveBytes)\n" \     "import Data.Int\n" \     "import Data.Word\n" \     ); \@@ -222,8 +242,8 @@     printf(" -> (");bc_typemarkup(# type);printf(")\n"); \  static struct {-	int n, is_union[500], is_fam[500];-	uintmax_t array_size[500], offset[500];+	int n, is_union[500], is_fam[500], is_2d[500], line[500];+	uintmax_t array_size[500], elem_size[500], offset[500]; 	char fname[500][1000], ftype[500][1000]; } bc_fielddata; @@ -240,6 +260,7 @@      ptrdiff_t typealign = (char*)&bc_refdata.v - (char*)&bc_refdata; \      bc_fielddata.n = 0; \      char typename[] = # name; \+     int typeline = __LINE__; \      int index; \      int standalone_deriving = 0; \ @@ -248,8 +269,11 @@      bc_fielddata.offset[index] = (uintmax_t) \          ((char*)&bc_refdata.v.name - (char*)&bc_refdata.v); \      bc_fielddata.array_size[index] = 0; \+     bc_fielddata.elem_size[index] = 0; \      bc_fielddata.is_union[index] = u; \      bc_fielddata.is_fam[index] = f; \+     bc_fielddata.is_2d[index] = 0; \+     bc_fielddata.line[index] = __LINE__; \      strcpy(bc_fielddata.fname[index],# name); \      strcpy(bc_fielddata.ftype[index],type); \ @@ -264,13 +288,22 @@  #define hsc_array_field(name,type) \      bc_basicfield(name,# type,0,0); \-     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name \+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0] \  #define hsc_union_array_field(name,type) \      bc_basicfield(name,# type,1,0); \-     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name \+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0] \ +#define hsc_array2d_field(name,type) \+     bc_basicfield(name,# type,0,0); \+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0]; \+     bc_fielddata.is_2d[index] = 1 \+ #define hsc_stoptype(dummy) \+     bc_lineprag(typeline); \      printf("data ");bc_conid(typename);printf(" = "); \      bc_conid(typename);printf("{\n"); \      int i; \@@ -297,14 +330,17 @@         } \      for (i=0; i < bc_fielddata.n; i++) \         { \+         bc_lineprag(bc_fielddata.line[i]); \          bc_fieldoffset(typename,bc_fielddata.fname[i]); \          printf(" p = plusPtr p %" PRIuMAX "\n",bc_fielddata.offset[i]); \+         bc_lineprag(bc_fielddata.line[i]); \          bc_fieldoffset(typename,bc_fielddata.fname[i]); \          printf(" :: Ptr (");bc_conid(typename);printf(") -> "); \          printf("Ptr (");bc_typemarkup(bc_fielddata.ftype[i]);printf(")\n"); \         } \      for (i=0; i < bc_fielddata.n; i++) if (bc_fielddata.is_union[i]) \         { \+         bc_lineprag(bc_fielddata.line[i]); \          bc_unionupdate(typename,bc_fielddata.fname[i]); \          printf(" :: ");bc_conid(typename);printf(" -> "); \          if (bc_fielddata.array_size[i] > 0) printf("["); \@@ -316,13 +352,9 @@          printf(" v vf = alloca $ \\p -> do\n"); \          printf("  poke p v\n"); \          if (bc_fielddata.array_size[i] > 0) \-            { \-             printf("  let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \-               i, bc_fielddata.array_size[i]); \-             bc_typemarkup(bc_fielddata.ftype[i]); \-             printf(")\n  pokeArray (plusPtr p %" PRIuMAX ") $ take s%d vf", \-               bc_fielddata.offset[i], i); \-            } \+             printf("  pokeArray (plusPtr p %" PRIuMAX ") $ take %" PRIuMAX " vf", \+               bc_fielddata.offset[i], \+               bc_fielddata.array_size[i] / bc_fielddata.elem_size[i]); \          else \            printf("  pokeByteOff p %" PRIuMAX " vf", \                bc_fielddata.offset[i]); \@@ -337,6 +369,7 @@              printf(" vu}\n"); \             } \         } \+     bc_lineprag(typeline); \      printf("instance Storable "); \      bc_conid(typename);printf(" where\n"); \      printf("  sizeOf _ = %" PRIuMAX "\n  alignment _ = %" PRIuMAX "\n", \@@ -347,14 +380,15 @@          printf("    v%d <- ",i); \          if (bc_fielddata.is_fam[i]) \             printf("return []"); \+         else if (bc_fielddata.is_2d[i]) \+            printf("return [plusPtr _p (%" PRIuMAX " + %" PRIuMAX " * k) " \+                   "| k <- [0 .. %" PRIuMAX "]]", \+              bc_fielddata.offset[i], bc_fielddata.elem_size[i], \+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i] - 1); \          else if (bc_fielddata.array_size[i] > 0) \-           { \-            printf ("let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \-              i, bc_fielddata.array_size[i]); \-            bc_typemarkup(bc_fielddata.ftype[i]); \-            printf(") in peekArray s%d (plusPtr _p %" PRIuMAX ")", \-              i, bc_fielddata.offset[i]); \-           } \+            printf("peekArray %" PRIuMAX " (plusPtr _p %" PRIuMAX ")", \+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i], \+              bc_fielddata.offset[i]); \          else \             printf("peekByteOff _p %" PRIuMAX "", bc_fielddata.offset[i]); \          printf("\n"); \@@ -370,14 +404,18 @@          if (bc_fielddata.is_fam[i]) \             printf("    pokeArray (plusPtr _p %" PRIuMAX ") v%d", \               bc_fielddata.offset[i],i); \+         else if (bc_fielddata.is_2d[i]) \+            printf("    sequence_ [moveBytes (plusPtr _p (%" PRIuMAX \+                   " + %" PRIuMAX " * k)) r %" PRIuMAX \+                   " | (k,r) <- zip [0 .. %" PRIuMAX "] v%d]", \+              bc_fielddata.offset[i], bc_fielddata.elem_size[i], \+              bc_fielddata.elem_size[i], \+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i] - 1, i); \          else if (bc_fielddata.array_size[i] > 0) \-           { \-            printf("    let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \-              i, bc_fielddata.array_size[i]); \-            bc_typemarkup(bc_fielddata.ftype[i]); \-            printf(")\n    pokeArray (plusPtr _p %" PRIuMAX ") (take s%d v%d)", \-              bc_fielddata.offset[i], i, i); \-           } \+            printf("    pokeArray (plusPtr _p %" PRIuMAX ") (take %" PRIuMAX \+                   " v%d)", \+              bc_fielddata.offset[i], \+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i], i); \          else \             printf("    pokeByteOff _p %" PRIuMAX " v%d", \               bc_fielddata.offset[i],i); \