Title
al_plugin_begin_timerange_action is unreachable from the public C ABI due to a declaration/definition signature mismatch
Body
Summary
al_plugin_begin_timerange_action is declared in include/al_lowlevel.h with extern "C" linkage, but its definition in src/al_lowlevel.cpp uses a different parameter type for dtime_shape. Because the two signatures don't match, the compiler treats them as two unrelated overloads: the declared (extern "C") version is never defined, and the defined version gets ordinary C++ linkage instead. The net effect is that the function documented in the public header does not exist in the compiled library under a callable C symbol.
Evidence
Header declaration (include/al_lowlevel.h:345-353, on develop @ ad0f2e2):
IMAS_CORE_LIBRARY_API al_status_t al_plugin_begin_timerange_action(int ctx,
const char *dataobjectname,
int rwmode,
double tmin,
double tmax,
const double* dtime,
const double* dtime_shape, // <-- double*
int interpmode,
int *opctx);
Definition (src/al_lowlevel.cpp:1045-1046):
al_status_t al_plugin_begin_timerange_action(int pctxID, const char* dataobjectname, int rwmode,
double tmin, double tmax, const double* dtime_buffer, const int* dtime_shape, int interpmode, int *octxID)
// <-- int*
Both lines were changed in the same commit (1917f25c, 2024-07-03), which is presumably where the type diverged.
Confirmed via the compiled library — nm -gU on libal.dylib shows no plain-C symbol _al_plugin_begin_timerange_action. Instead there's a C++-mangled symbol:
__Z32al_plugin_begin_timerange_actioniPKciddPKdPKiiPi
i.e. the function that actually got compiled has C++ linkage, not the extern "C" linkage the header promises.
dtime_shape is clearly meant to be an element count, not a data value — inside al_begin_timerange_action (the sibling, correctly-working, publicly-callable function, src/al_lowlevel.cpp:1502-1510), the equivalent parameter is dereferenced as a count:
al_status_t al_begin_timerange_action(int pctxID, const char* dataobjectname, int rwmode,
double tmin, double tmax, const double* dtime_buffer, const int* dtime_shape, int interpmode, int *octxID)
{
...
std::vector<double> dtime;
if (*dtime_shape > 0)
dtime = std::vector<double> (dtime_buffer, dtime_buffer + *dtime_shape);
al_begin_timerange_action itself already declares dtime_shape as const int* in both its header declaration (include/al_lowlevel.h:481) and its definition — consistent with the .cpp definition of al_plugin_begin_timerange_action, and inconsistent only with that one header declaration.
What this does not break
To be precise about scope: this does not break time-range reads/writes for ordinary callers, and it does not break plugin dispatch during a time-range operation. al_begin_timerange_action (the plain, User-facing function) calls al_plugin_begin_timerange_action internally — as an ordinary same-translation-unit C++ call, which resolves correctly regardless of the extern "C" mismatch — and separately dispatches to any bound plugin's begin_timerange_action via LLplugin::beginTimeRangeActionPlugin (al_lowlevel.cpp:471). Both of those paths work fine today.
Impact
al_plugin_begin_timerange_action mirrors a family of functions (al_plugin_begin_global_action, al_plugin_begin_slice_action, al_plugin_read_data, al_plugin_write_data, …) that exist so external code linked against this library — e.g. a plugin author's own compiled plugin — can re-enter the low-level layer (per docs/source/developers/plugins_architecture.rst, "Calling low level data access API functions from plugin code": these avoid infinite recursion when a plugin needs to fetch/write the underlying data from within its own read_data()/write_data()/begin_*_action() implementation). The sibling functions in this family are genuinely used this way today — e.g. access_layer_plugin_manager.cpp calls al_plugin_read_data/al_plugin_write_data externally across translation units for provenance metadata handling.
al_plugin_begin_timerange_action is not currently called this way by anything in this repository (no caller outside al_lowlevel.cpp itself, not wrapped in the Python bindings, no test exercises it), so today this bug has no observable effect — it's latent, not active. But it means the declared public capability is currently non-functional: if any external caller (an HLI, or a plugin author needing reentrant time-range access, matching the existing pattern for global/slice/read/write) ever links against al_plugin_begin_timerange_action as documented, they will get an undefined-symbol error at link time.
The three sibling al_plugin_begin_*_action functions (global, slice, arraystruct) are unaffected — their header/definition signatures match and they're confirmed present as plain C symbols in the compiled library.
Suggested fix
In include/al_lowlevel.h:351, change:
const double* dtime_shape,
to:
matching the .cpp definition and the sibling al_begin_timerange_action declaration. This is a minimal, low-risk fix since it only affects a currently-unused declared symbol.
Alternatively, since nothing currently uses this reentry path, maintainers may prefer to confirm whether it's still intended to be supported at all before fixing vs. removing it — raising as an issue rather than a PR so that decision can be made first.
Environment
- Found on
develop @ ad0f2e2910dac8dfd552e1f8ec67e8b4ac70162c
- macOS, Clang (Apple's
nm/dlyib toolchain), but this is a source-level signature mismatch, not platform-specific
Title
al_plugin_begin_timerange_actionis unreachable from the public C ABI due to a declaration/definition signature mismatchBody
Summary
al_plugin_begin_timerange_actionis declared ininclude/al_lowlevel.hwithextern "C"linkage, but its definition insrc/al_lowlevel.cppuses a different parameter type fordtime_shape. Because the two signatures don't match, the compiler treats them as two unrelated overloads: the declared (extern "C") version is never defined, and the defined version gets ordinary C++ linkage instead. The net effect is that the function documented in the public header does not exist in the compiled library under a callable C symbol.Evidence
Header declaration (
include/al_lowlevel.h:345-353, ondevelop@ad0f2e2):Definition (
src/al_lowlevel.cpp:1045-1046):Both lines were changed in the same commit (
1917f25c, 2024-07-03), which is presumably where the type diverged.Confirmed via the compiled library —
nm -gUonlibal.dylibshows no plain-C symbol_al_plugin_begin_timerange_action. Instead there's a C++-mangled symbol:i.e. the function that actually got compiled has C++ linkage, not the
extern "C"linkage the header promises.dtime_shapeis clearly meant to be an element count, not a data value — insideal_begin_timerange_action(the sibling, correctly-working, publicly-callable function,src/al_lowlevel.cpp:1502-1510), the equivalent parameter is dereferenced as a count:al_begin_timerange_actionitself already declaresdtime_shapeasconst int*in both its header declaration (include/al_lowlevel.h:481) and its definition — consistent with the.cppdefinition ofal_plugin_begin_timerange_action, and inconsistent only with that one header declaration.What this does not break
To be precise about scope: this does not break time-range reads/writes for ordinary callers, and it does not break plugin dispatch during a time-range operation.
al_begin_timerange_action(the plain, User-facing function) callsal_plugin_begin_timerange_actioninternally — as an ordinary same-translation-unit C++ call, which resolves correctly regardless of theextern "C"mismatch — and separately dispatches to any bound plugin'sbegin_timerange_actionviaLLplugin::beginTimeRangeActionPlugin(al_lowlevel.cpp:471). Both of those paths work fine today.Impact
al_plugin_begin_timerange_actionmirrors a family of functions (al_plugin_begin_global_action,al_plugin_begin_slice_action,al_plugin_read_data,al_plugin_write_data, …) that exist so external code linked against this library — e.g. a plugin author's own compiled plugin — can re-enter the low-level layer (perdocs/source/developers/plugins_architecture.rst, "Calling low level data access API functions from plugin code": these avoid infinite recursion when a plugin needs to fetch/write the underlying data from within its ownread_data()/write_data()/begin_*_action()implementation). The sibling functions in this family are genuinely used this way today — e.g.access_layer_plugin_manager.cppcallsal_plugin_read_data/al_plugin_write_dataexternally across translation units for provenance metadata handling.al_plugin_begin_timerange_actionis not currently called this way by anything in this repository (no caller outsideal_lowlevel.cppitself, not wrapped in the Python bindings, no test exercises it), so today this bug has no observable effect — it's latent, not active. But it means the declared public capability is currently non-functional: if any external caller (an HLI, or a plugin author needing reentrant time-range access, matching the existing pattern for global/slice/read/write) ever links againstal_plugin_begin_timerange_actionas documented, they will get an undefined-symbol error at link time.The three sibling
al_plugin_begin_*_actionfunctions (global,slice,arraystruct) are unaffected — their header/definition signatures match and they're confirmed present as plain C symbols in the compiled library.Suggested fix
In
include/al_lowlevel.h:351, change:to:
matching the
.cppdefinition and the siblingal_begin_timerange_actiondeclaration. This is a minimal, low-risk fix since it only affects a currently-unused declared symbol.Alternatively, since nothing currently uses this reentry path, maintainers may prefer to confirm whether it's still intended to be supported at all before fixing vs. removing it — raising as an issue rather than a PR so that decision can be made first.
Environment
develop@ad0f2e2910dac8dfd552e1f8ec67e8b4ac70162cnm/dlyibtoolchain), but this is a source-level signature mismatch, not platform-specific