Skip to content

submit_event() always returns False on Windows (MSVC) — IS_ENABLED() macro mismatch #289

@Jeffeke

Description

@Jeffeke

Hi, I had an issue trying to make a test cardreader PD in python on Windows with submit_event(). After bashing my head into a wall with Claude for a while I managed to fix the issue.

Full transparency: I'm very much out of my depth here, so here is Claude's synopsis on what went wrong and how to fix it. Apologies for the AI slop.

Summary

osdp_pd_submit_event() silently fails on Windows builds because the IS_ENABLED() macro evaluates incorrectly under MSVC's legacy preprocessor, creating a code path mismatch between the Python C extension and the core library.

Root Cause

libosdp uses a Linux kernel-style IS_ENABLED() macro (from c-utils/include/utils/utils.h):

#define __IS_ENABLED1(x) __IS_ENABLED2(__XXXX ## x)
#define __XXXX1 __YYYY,
#define __IS_ENABLED2(y) __IS_ENABLED3(y 1, 0)
#define __IS_ENABLED3(_i, val, ...) val
#define IS_ENABLED(x) __IS_ENABLED1(x)

This relies on token-pasting:

When OPT_OSDP_APP_OWNED_QUEUE_DATA=1, __XXXX ## 1__XXXX1 → expands to __YYYY, which injects a comma and makes __IS_ENABLED3 return 1.

MSVC's legacy preprocessor does not expand macro arguments before ## token pasting like GCC/Clang do.
Result:
IS_ENABLED(OPT_OSDP_APP_OWNED_QUEUE_DATA) evaluates to 0 on MSVC even when the flag is defined.

This creates a mismatch:

File | Guard | Evaluates to | Behavior -- | -- | -- | -- python/osdp_sys/pd.c | #ifdef OPT_OSDP_APP_OWNED_QUEUE_DATA | true | Allocates event with calloc, calls osdp_pd_submit_event() src/osdp_pd.c | IS_ENABLED(OPT_OSDP_APP_OWNED_QUEUE_DATA) | false | Takes !IS_ENABLED branch → calls pd_event_alloc() src/osdp_pd.c | #ifndef OPT_OSDP_APP_OWNED_QUEUE_DATA | false | pd_event_alloc() compiled as no-op returning NULL

So:

osdp_pd_submit_event()
-> pd_event_alloc()
-> returns NULL
-> return -1

Python wrapper converts this to:

PeripheralDevice.submit_event(...) -> False

No events are queued or sent.

Impact

On Windows:

  • PeripheralDevice.submit_event() always returns False
  • No card reads / keypad / manufacturer events sent
  • PD replies to POLL with ACK (0x40) instead of REPLY_RAW (0x50)
  • Appears as if event submission is broken

Affected Versions

  • libosdp 3.2.0 (PyPI Windows wheel)
  • Any build using IS_ENABLED() with MSVC legacy preprocessor

Fix Options

Option A — Compiler flag (workaround)

Enable conformant MSVC preprocessor:

set CL=/Zc:preprocessor
pip install --no-binary :all: --no-cache-dir libosdp==3.2.0

This fixes IS_ENABLED() evaluation.


Option B — Code fix (preferred)

Make both files use identical guards.

In src/osdp_pd.c:

// Before
if (IS_ENABLED(OPT_OSDP_APP_OWNED_QUEUE_DATA)) {

// After
#ifdef OPT_OSDP_APP_OWNED_QUEUE_DATA

This matches python/osdp_sys/pd.c and avoids macro dependence.


Option C — Disable flag (alternate path)

Requires full Git source (PyPI tarball missing slab.c):

set OPT_OSDP_APP_OWNED_QUEUE_DATA=off
pip install "git+https://github.com/goToMain/libosdp@v3.2.0#egg=libosdp&subdirectory=python"

Additional Issue

PyPI source tarball for v3.2.0 is missing:

vendor/utils/src/slab.c

When building with:

OPT_OSDP_APP_OWNED_QUEUE_DATA=off

Build fails:

RuntimeError: Path 'vendor\utils/src/slab.c' does not exist

Full Git clone (with submodules) is required.

Suggested Resolution

Either:

  1. Replace IS_ENABLED() with #ifdef in src/osdp_pd.c
  2. Or force /Zc:preprocessor in Windows build flags
  3. Or remove mixed guard usage across compilation units

This restores correct event submission on Windows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions