🌐 AI搜索 & 代理 主页
Skip to content

Conversation

@nilason
Copy link
Collaborator

@nilason nilason commented May 7, 2025

Add bool to ctypes type map. This change is needed for C23 support, where _Bool is not necessarily defined by default.

See e.g., GCC:

C23 added bool as the preferred name of the boolean type, but _Bool also remains a standard keyword in the language and is supported as such by GCC with -std=c23.

GCC 15 now defaults to C23.

Closes #173

echoix added a commit to echoix/grass that referenced this pull request May 7, 2025
This was referenced May 7, 2025
@nilason nilason merged commit a90952d into ctypesgen:master May 7, 2025
7 of 8 checks passed
@mara004
Copy link
Contributor

mara004 commented May 7, 2025

Thanks, I've also pulled this into pypdfium2-ctypesgen.

@nilason
Copy link
Collaborator Author

nilason commented May 7, 2025

Thanks, I've also pulled this into pypdfium2-ctypesgen.

Great!

(I don't dare make any explicit promises, due to time restraints, but I haven't given up on bringing back your work here!)

@mara004
Copy link
Contributor

mara004 commented May 7, 2025

Yes, I'd really appreciate my branch eventually being merged here!

But before that, I figured there would be a few features that I might have to add back (binding to multiple DLLs in the same file, and mixed calling convention (cdecl/stdcall)).
Unfortunately I also can't make any promises, for the same reason as you ;)
And I'd only want to merge it if it can be done cleanly.

@nilason
Copy link
Collaborator Author

nilason commented May 7, 2025

Perhaps the major break, that I noticed, was the drop of the "String" wrapper (part of the preamble).
The ctypesgen generated files of GRASS is also public Python API (interfacing its public C API), so it is not only internally used.

@mara004
Copy link
Contributor

mara004 commented May 7, 2025

Hmm, I can see the issue with existing public APIs, but I've been opposed to implicit string encoding/decoding, and the big wrapper classes in particular.

Technically, the expected encoding is specific to each C API, so one should really review the documentation in question and encode explicitly on the caller side IMHO. Also, an important note is that not all POINTER(c_char) in/out parameters are actually strings – in particular, generic output data may not be NUL-terminated.

Perhaps best to make a new major release and mandate that all callers encode/decode their strings explicitly.

@mara004
Copy link
Contributor

mara004 commented May 7, 2025

That said, maybe we could add an opt-in, leaner template based on c_char_p for backwards compatibility, to ease the transition to the new codebase.

I once made a draft here: pypdfium2-team#1 (comment)

@mara004
Copy link
Contributor

mara004 commented May 7, 2025

Re-reading the draft, I think the from_param encoding bit is OK, but the return string bit isn't, because we're always accessing the value property on _check_retval_, which is problematic if the data is not NUL-terminated.
Instead, .value should only be accessed when the caller actually tries to treat the parameter as string.

@mara004
Copy link
Contributor

mara004 commented May 7, 2025

How about something like this (disclaimer: untested):

import sys
import ctypes
import functools

if sys.version_info < (3, 8):
    # NOTE alternatively, we could write our own cached property backport with python's descriptor protocol
    def cached_property(func):
        return property( functools.lru_cache(maxsize=1)(func) )
else:
    cached_property = functools.cached_property


DEFAULT_ENCODING = "utf-8"

class _wraps_c_char_p:
    
    def __init__(self, ptr):
        self.ptr = ptr
    
    @cached_property
    def raw(self):
        return self.ptr.value
    
    @cached_property
    def decoded(self):
        if self.raw is None:
            raise RuntimeError("Null pointer cannot be decoded")
        return self.raw.decode(DEFAULT_ENCODING)
    
    def __str__(self):
        return self.decoded
    
    def __getattr__(self, attr):
        return getattr(self.decoded, attr)
    
    def __eq__(self, other):
        if type(self) is type(other):
            return self is other or self.raw == other.raw
        elif isinstance(other, str):
            return self.decoded == other
        else:
            return self.raw == other


class String (ctypes.c_char_p):
    
    @classmethod
    def _check_retval_(cls, result):
        return _wraps_c_char_p(result)
    
    @classmethod
    def from_param(cls, obj):
        if isinstance(obj, str):
            obj = obj.encode(DEFAULT_ENCODING)
        return super().from_param(obj)

Update: fixes, eq method added

@mara004
Copy link
Contributor

mara004 commented May 8, 2025

OK, I've basically implemented this in the fork/branch: pypdfium2-team@3bcd9e4

Testing/feedback is appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bool / C++ support

2 participants