Python has no way of doing that automatically for you:

You will have to build code to pick all the desired Data yourself, putting them in a suitable Python data structure (or just adding the data in a unique bytes-string where you will know where each element is by its offset) - and then save that object to disk.

This is not a "Python" problem - it is exactly a problem Python solves for you when you use Python objects and data. When coding in C or lower level, you are responsible to know not only where your data is, but also, the length of each chunk of data (and allocate memory for each chunk, and free it when done, and etc). And this is what you have to do in this case.

Your data structure should give you not only the pointers, but also the length of the data in each pointed location (in a way or the other - if the pointer is to another structure, "size_of" will work for you)

Answer from jsbueno on Stack Overflow
🌐
Ray
discuss.ray.io › ray tune
ValueError: ctypes objects containing pointers cannot be pickled - Ray Tune - Ray
August 5, 2022 - Hi! I’m trying to use Tune to search the optimal combination of parameters in a model. When I run Tune the following message appear in the terminal: 2022-08-04 14:11:20,580 INFO services.py:1470 -- View the Ray dashboa…
🌐
Numba Discussion
numba.discourse.group › community support
Looking for advice on "ValueError: ctypes objects containing pointers cannot be pickled - Community Support - Numba Discussion
November 19, 2023 - Hoping someone has an idea of what can be done, even if its not a numba-specific issue. I am looking for advice on the error-message ctypes objects containing pointers cannot be pickled, with the goal of running njitted scipy.special.cython_special-functions on remote workers.
🌐
Stack Overflow
stackoverflow.com › questions › 59124654 › valueerror-ctypes-objects-containing-pointers-cannot-be-pickled
python - ValueError: ctypes objects containing pointers cannot be pickled - Stack Overflow
December 1, 2019 - I am new to Python (2 months of programming/learning exp in total). In this code, all I do is get some data from MSSQL database and transfer it to DynamDB. I just want to know why I'm getting this error: ValueError: ctypes objects containing pointers cannot be pickled.
🌐
GitHub
github.com › pytorch › pytorch › issues › 87276
torch.save throws ValueError: ctypes objects containing pointers cannot be pickled · Issue #87276 · pytorch/pytorch
October 19, 2022 - 🐛 Describe the bug I have tried saving my dataloader object, but it throws me ValueError: ctypes objects containing pointers cannot be pickled Here is the code: batch_size = 16 do_flip = True flip_vert = True max_rotate = 90 max_zoom = 1...
Author: pytorch
🌐
Narkive
cocos-discuss.narkive.com › Bkzis6tb › valueerror-ctype-objects-cannot-be-pickled
valueerror ctype objects cannot be pickled
Here is the error traceback Thanks ... memo) File "/usr/lib/python3.5/copy.py", line 174, in deepcopy rv = reductor(4) ValueError: ctypes objects containing pointers cannot be pickled -- You received this message because you are subscribed to the Google Groups "cocos2d discuss" ...
🌐
GitHub
github.com › junzis › pyModeS › issues › 127
Modeslive throws ValueError: ctypes objects containing pointers cannot be pickled · Issue #127 · junzis/pyModeS
July 11, 2022 - Mac OS 12.4 Python 3.10.5 In a conda environment: pip list Package Version ---------- ------- numpy 1.23.0 pip 22.1.2 pyModeS 2.11 pyrtlsdr 0.2.92 pyzmq 23.2.0 setuptools 63.1.0 wheel 0.37.1 Hello, When trying to get modeslive running, I...
Author: junzis
🌐
Lightrun
lightrun.com › answers › delgan-loguru-ctypes-pickling-error
ctypes pickling error
Traceback (most recent call last): File "C:\Users\jules\.conda\envs\app\lib\site-packages\loguru\_handler.py", line 175, in emit self._queue.put(str_record) File "C:\Users\jules\.conda\envs\app\lib\multiprocessing\queues.py", line 362, in put obj = _ForkingPickler.dumps(obj) File "C:\Users\jules\.conda\envs\app\lib\multiprocessing\reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) ValueError: ctypes objects containing pointers cannot be pickled
Find elsewhere
🌐
GitHub
github.com › mahmoodlab › CLAM › issues › 64
Error while running create_heatmaps.py --> ValueError: ctypes objects containing pointers cannot be pickled · Issue #64 · mahmoodlab/CLAM
July 2, 2021 - Traceback (most recent call last): ...ultiprocessing\reduction.py", line 60, in dump ForkingPickler(file, protocol).dump(obj) ValueError: ctypes objects containing pointers cannot be pickled...
Author: mahmoodlab
🌐
GitHub
github.com › Delgan › loguru › issues › 342
ctypes pickling error · Issue #342 · Delgan/loguru
October 1, 2020 - Traceback (most recent call last): File "C:\Users\jules\.conda\envs\app\lib\site-packages\loguru\_handler.py", line 175, in emit self._queue.put(str_record) File "C:\Users\jules\.conda\envs\app\lib\multiprocessing\queues.py", line 362, in put obj = _ForkingPickler.dumps(obj) File "C:\Users\jules\.conda\envs\app\lib\multiprocessing\reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) ValueError: ctypes objects containing pointers cannot be pickled
Author: Delgan
🌐
Fast.ai
forums.fast.ai › part 1 (2019)
How to save databunch object (databunch from Itemlists) - Part 1 (2019) - fast.ai Course Forums
September 24, 2022 - Hey everyone, I was trying to save my databunch object using the data.save(“PATH/data.pkl”), when I encountered this error: ValueError: ctypes objects containing pointers cannot be pickled This is my code for creating…
🌐
GitHub
github.com › hyperopt › hyperopt › issues › 857
Hyperopt with XGBoost ValueError on Databricks when using SparkTrials · Issue #857 · hyperopt/hyperopt
May 16, 2022 - ValueError: ctypes objects containing pointers cannot be pickled --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <command-1847760743331734> in <module> 5 6 with mlflow.start_run(): ----> 7 argmin = fmin( 8 fn=objective, 9 space=search_space, /databricks/.python_edge_libs/hyperopt/fmin.py in fmin(fn, space, algo, max_evals, timeout, loss_threshold, trials, rstate, allow_trials_fmin, pass_expr_memo_ctrl, catch_eval_exceptions, verbose, return_argmin, points_to_evaluate, max_queue_len, show_progressbar, early_stop_fn, trials
Author: hyperopt
🌐
Google Groups
groups.google.com › d › topic › pyglet-users › Qn7wpIbA2fs
pickling
For example: >>> import pyglet ... "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/ lib/python2.7/pickle.py", line 306, in save rv = reduce(self.proto) ValueError: ctypes objects containing pointers cannot be pickled I have worked around this by manually excluding/rec...
Top answer
1 of 1
2

Necroposting.

I ran into the same situation investigating another issue (using Python 3).

Listing:

  • [Python.Docs]: ctypes - A foreign function library for Python

  • [Python.Docs]: pickle - Python object serialization

A pointer is a (starting) memory address where data (a number of bytes) may (or may not) be stored. Typically, when used in structure members, a pointer is used to store an array of elements (element represents the pointed type) just like in your example because:

  1. Avoids array limitations

  2. Flexibility: possibility to use the same structure (instance) for multiple numbers of elements (partially overlaps with #1.)

But, a pointer doesn't hold / have information about how much memory the stored data occupies (some could argue it's the pointed type's sizeof, but that is more like a hint).
So, in order to be able to get the right amount of bytes (in order to pickle (or do whatever with) it) from the pointer address, the data size must also be retrieved from an external (current pointer wise) source (in our case from another structure member).
If the size is not available, the fallback would be (as stated above) the pointed type's sizeof, but there might be cases when that wouldn't be correct. Things get even more complicated when structures are nested (which is pretty often). Also, data size might be expressed in bytes or elements.

So, there is no generic way of knowing how many bytes are stored at one address, and that's precisely why (CTypes) pointers can't be pickled by default (otherwise there might be cases of accessing "forbidden" memory which is Undefined Behavior and might trigger SegFault (Access Violation)).
As a consequence, structures (unions, or any other container types) containing pointers should define their own pickling strategy (depending on how they store / interpret data).

I created a small example.

code00.py:

#!/usr/bin/env python

import ctypes as ct
import pickle
import sys


def __reduce__(self):
    #print("__reduce__")
    state = []
    ptr_size = None
    for name, typ in self._fields_:
        val = getattr(self, name)
        if issubclass(typ, ct._Pointer):
            state.append(val[:ptr_size])
            ptr_size = None
        else:
            state.append(val)
            ptr_size = val
    #print("pickle state:", state)
    return (self.__class__, (), state)


def __setstate__(self, state):
    #print("__setstate__")
    for idx, (name, typ) in enumerate(self._fields_):
        if issubclass(typ, ct._Pointer):
            val = state[idx]
            setattr(self, name, (typ._type_ * len(val))(*val))
        else:
            setattr(self, name, state[idx])


def to_string(self, indent=0, head=True, tail=True, indent_text="  "):
    l = [""] if head else []
    l.append("{:s}{:s}".format(indent_text * indent, str(self)))
    i1 = indent_text * (indent + 1)
    ptr_size = None
    for name, typ in self._fields_:
        val = getattr(self, name)
        inner = getattr(val, to_string.__name__, None)
        if callable(inner):
            l.append("{:s}{:s}: {:}".format(i1, name, inner(indent=indent + 1, head=False, tail=False, indent_text=indent_text)))
        elif issubclass(typ, ct._Pointer):
            l.append("{:s}{:s} ({:}): ({:s})".format(i1, name, val, ", ".join(str(val[e]) for e in range(ptr_size))))
            ptr_size = None
        else:
            l.append("{:s}{:s}: {:}".format(i1, name, val))
            ptr_size = val
    if tail:
        l.append("")
    return "\n".join(l)


FloatPtr = ct.POINTER(ct.c_float)
StrPtr = ct.POINTER(ct.c_char_p)


class Struct0(ct.Structure):
    _fields_ = (
        ("float_size", ct.c_uint),
        ("float_data", FloatPtr),
        ("str_size", ct.c_uint),
        ("str_data", StrPtr),
    )

    '''
    def __getstate__(self):
        print("__getstate__")
        return ""
    '''

Struct0.__reduce__ = __reduce__
Struct0.__setstate__ = __setstate__
Struct0.to_string = to_string


class Struct1(ct.Structure):
    _fields_ = (
        ("struct0", Struct0),
        ("i", ct.c_int),
    )

Struct1.__reduce__ = __reduce__
Struct1.__setstate__ = __setstate__
Struct1.to_string = to_string


def main(*argv):
    floats = (
        3.141593,
        2.718282,
        1.618,
        -1,
    )
    strs = (
        "dummy",
        "",
        "stupid",
        "text",
        "",
    )

    s00 = Struct0()
    s00.float_size = len(floats)
    s00.float_data = (ct.c_float * s00.float_size)(*floats)
    s00.str_size = len(strs)
    s00.str_data = (ct.c_char_p * s00.str_size)(*(ct.c_char_p(e.encode()) for e in strs))

    print("\nORIGINAL:", s00.to_string())
    s00p = pickle.dumps(s00)
    print("PICKLED:\n", s00p)
    s01 = pickle.loads(s00p)
    print("\nUNPICKLED:", s01.to_string())

    #print(dir(s00) == dir(s01), s00.__dict__ == s01.__dict__, s00 == s01, Struct0() == Struct0())

    s10 = Struct1()
    s10.struct0 = s00
    s10.i = -69
    print("\nORIGINAL:", s10.to_string())
    s10p = pickle.dumps(s10)
    print("PICKLED:\n", s10p)
    s11 = pickle.loads(s10p)
    print("\nUNPICKLED:", s11.to_string())


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Notes:

  • I created the required methods (__reduce__ and __setstate__ - as pickling makes no sense without unpickling), and an additional one (to_string - to display the structure nicely). They use the same fields "browsing" mechanism

  • The code might look a bit too complicated, but I wanted to avoid hardocding member names or types, so that if those change, the methods still work

    • But I did hardcoded (although it's not a hardcoding per se) the structure's state: the code heavily relies on the fact that the member holding the size (in elements) comes right before the pointer one, so if the structure's structure changes (I assumed that would be less likely), it will no longer work
  • Since I used the same method implementations in both my structures, I defined them as functions, and "transformed" them to methods after each structure definition

    • Conversely, they only handle structure types like these 2, meaning that there are functionalities (e.g.: arrays, pointers to structures, or others that I didn't think about) which are currently not supported. It shouldn't be very hard to add them, but I didn't want to complicate the code even more
  • Bear in mind that converting the data back and forth is costly, so if done often, any speed improvement gained by CTypes usage (which is its main advantage), will be seriously affected

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q049694832]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" ./code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32


ORIGINAL:
<__main__.Struct0 object at 0x00000191331E40C0>
  float_size: 4
  float_data (<__main__.LP_c_float object at 0x00000191349C5140>): (3.1415929794311523, 2.7182819843292236, 1.6180000305175781, -1.0)
  str_size: 5
  str_data (<__main__.LP_c_char_p object at 0x00000191349C5140>): (b'dummy', b'', b'stupid', b'text', b'')

PICKLED:
 b'\x80\x04\x95m\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x07Struct0\x94\x93\x94)R\x94]\x94(K\x04]\x94(G@\t!\xfb\x80\x00\x00\x00G@\x05\xbf\n\xa0\x00\x00\x00G?\xf9\xe3T\x00\x00\x00\x00G\xbf\xf0\x00\x00\x00\x00\x00\x00eK\x05]\x94(C\x05dummy\x94C\x00\x94C\x06stupid\x94C\x04text\x94h\x08eeb.'

UNPICKLED:
<__main__.Struct0 object at 0x00000191349C5140>
  float_size: 4
  float_data (<__main__.LP_c_float object at 0x00000191349C5240>): (3.1415929794311523, 2.7182819843292236, 1.6180000305175781, -1.0)
  str_size: 5
  str_data (<__main__.LP_c_char_p object at 0x00000191349C5240>): (b'dummy', b'', b'stupid', b'text', b'')


ORIGINAL:
<__main__.Struct1 object at 0x00000191349C5240>
  struct0:   <__main__.Struct0 object at 0x00000191349C53C0>
    float_size: 4
    float_data (<__main__.LP_c_float object at 0x00000191349C5440>): (3.1415929794311523, 2.7182819843292236, 1.6180000305175781, -1.0)
    str_size: 5
    str_data (<__main__.LP_c_char_p object at 0x00000191349C5440>): (b'dummy', b'', b'stupid', b'text', b'')
  i: -69

PICKLED:
 b'\x80\x04\x95\x88\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x07Struct1\x94\x93\x94)R\x94]\x94(h\x00\x8c\x07Struct0\x94\x93\x94)R\x94]\x94(K\x04]\x94(G@\t!\xfb\x80\x00\x00\x00G@\x05\xbf\n\xa0\x00\x00\x00G?\xf9\xe3T\x00\x00\x00\x00G\xbf\xf0\x00\x00\x00\x00\x00\x00eK\x05]\x94(C\x05dummy\x94C\x00\x94C\x06stupid\x94C\x04text\x94h\x0ceebJ\xbb\xff\xff\xffeb.'

UNPICKLED:
<__main__.Struct1 object at 0x00000191349C5440>
  struct0:   <__main__.Struct0 object at 0x00000191349C53C0>
    float_size: 4
    float_data (<__main__.LP_c_float object at 0x00000191349C54C0>): (3.1415929794311523, 2.7182819843292236, 1.6180000305175781, -1.0)
    str_size: 5
    str_data (<__main__.LP_c_char_p object at 0x00000191349C54C0>): (b'dummy', b'', b'stupid', b'text', b'')
  i: -69


Done.
🌐
GitHub
github.com › IRNAS › ppk2-api-python › issues › 18
MP Example ctypes objects containing pointers cannot be pickled · Issue #18 · IRNAS/ppk2-api-python
October 17, 2022 - Has anyone experienced a ValueError: ctypes objects containing pointers cannot be pickled when start_measuring() is called when using multiprocessing? Issue experienced using python 3.7. 3.8 and 3.9. Found PPK2 at COM23 Traceback (most r...
Author: IRNAS