Skip to content

gen: cast enum return types to int64_t (fix ORB::ScoreType factory error)#89

Merged
ViralBShah merged 3 commits into
masterfrom
vs/fix-enum-return-cast
Jun 5, 2026
Merged

gen: cast enum return types to int64_t (fix ORB::ScoreType factory error)#89
ViralBShah merged 3 commits into
masterfrom
vs/fix-enum-return-cast

Conversation

@ViralBShah

Copy link
Copy Markdown
Member

Summary

Follow-up to #87. That PR stopped mod.add_type<EnumT> from being emitted, but OpenCV_jll v4.13.0+3 (built with that fix) still fails at using OpenCV:

C++ exception while wrapping module OpenCV: No appropriate factory for type N2cv3ORB9ScoreTypeE
ERROR: LoadError: No appropriate factory for type N2cv3ORB9ScoreTypeE

Why #87 was incomplete

The factory error fires not only on mod.add_type, but on any use of an unregistered type as a wrapped return type. getScoreType() was generated as return retval; (raw cv::ORB::ScoreType), which makes jlcxx instantiate stored_type<cv::ORB::ScoreType> from the return-type deduction path — same effect, same error.

Confirmed by symbol-inspection of libopencv_julia.dylib from OpenCV_jll v4.13.0+3:

000000000061c260 D __ZGVZN5jlcxx11stored_typeIN2cv3ORB9ScoreTypeE...m_dt

Root cause

hdr_parser's parse_arg normalizes types via ("::" -> "_") and strips cv:: (line 385). So an arg of type ORB::ScoreType arrives in the parsed decl as ORB_ScoreType. But parse_tree.py:227 reads the original return type (decl[4]) into self.rettype — and decl[4] is ORB::ScoreType (cv:: stripped, :: preserved).

The enums set in gen3_cpp.py only held the fully-qualified (cv::ORB::ScoreType) and fully-normalized (ORB_ScoreType) forms. The mid-form ORB::ScoreType was missing, so outlist[0].tp in enums at gen3_cpp.py:151 was False for every enum-returning getter, and the (int64_t) cast wasn't emitted.

Fix

Push the third form too:

enums.append(e2[0].replace("cv::", ""))   # ORB::ScoreType

so all three shapes a type can appear in (cv::ORB::ScoreType / ORB::ScoreType / ORB_ScoreType) match.

Verification

  • Regenerated autogen_cpp/cv_core.cpp locally: getScoreType now emits return (int64_t)retval;. Same for getDescriptorType, getDefaultName-style enum returners.
  • src/generated/ is unchanged — the bug was purely in the C++ glue's enum cast.

Pairs with

  • OpenCV_jll v4.13.0+4 rebuild on Yggdrasil (PR to be opened against JuliaPackaging/Yggdrasil).

Test plan

  • Regenerated autogen_cpp/cv_core.cpp has return (int64_t)retval; for getScoreType/getDescriptorType/getDiffusivity/etc.
  • julia gen/regenerate.jl is still deterministic; src/generated/ byte-identical.
  • After +4 lands: using OpenCV loads cleanly; OpenCV.getVersionString() == "4.13.0"; full test suite passes.

🤖 Generated with Claude Code

ViralBShah and others added 2 commits June 5, 2026 15:00
These eight .jl files (Mat, Vec, OpenCV, cv_cxx, cv_manual_wrap,
mat_conversion, types_conversion, typestructs) were inherited from
opencv_contrib/modules/julia/jl_cxx_files/, where they used to be
shipped inside OpenCV_jll. Since the move to per-repo Julia sources
under src/, the package loads only from src/ and Yggdrasil ships
only libopencv_julia.* (build_tarballs.jl drops the staged Julia
tree), so this directory's contents were never read at runtime and
had drifted from src/.

regenerate.jl only copies *wrap.jl from autogen_jl/ into
src/generated/, so removing it has no effect on that pipeline.
The Yggdrasil-side CMake copy line is dropped in the same change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ror)

Follow-up to #87. That PR stopped the generator from emitting
`mod.add_type<EnumT>("...")` for enum typedefs in register_types — but
the C++ glue still failed at @wrapmodule time with:

    C++ exception while wrapping module OpenCV:
    No appropriate factory for type N2cv3ORB9ScoreTypeE

Because `getScoreType` (and the other features2d enum getters) were
generated as `return retval;` instead of `return (int64_t)retval;`, so
jlcxx instantiated `stored_type<cv::ORB::ScoreType>` from the return
type's deduced jl-type, registering it as a wrapped type with no Julia
counterpart.

Root cause: hdr_parser normalizes argument types via `::` -> `_` and
strips `cv::` (parse_arg, line 385), so setScoreType sees its arg as
`ORB_ScoreType`. But parse_tree.py reads `decl[4]` (the *original*
return type) into self.rettype, so getScoreType's return reaches us as
`ORB::ScoreType` — cv:: stripped, `::` preserved. The previously
populated `enums` set had only `cv::ORB::ScoreType` and `ORB_ScoreType`,
so the `tp in enums` check at gen3_cpp.py:151 missed, and the int64_t
cast wasn't emitted.

Fix: when populating the `enums` set in gen3_cpp.py, also push the
cv::-stripped-but-`::`-preserving form (e.g. `ORB::ScoreType`) so all
three shapes a type can appear in are covered.

Verified locally: regenerated autogen_cpp/cv_core.cpp now emits
`return (int64_t)retval;` for getScoreType, getDescriptorType, etc.
src/generated/ is unchanged (the fix is C++-side only).

Pairs with a matching OpenCV_jll v4.13.0+4 rebuild on Yggdrasil.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ViralBShah ViralBShah force-pushed the vs/fix-enum-return-cast branch from 0245f70 to a8e1995 Compare June 5, 2026 15:04
…trip, test axes

The features2d test suite (added in #82, never run against a published
OpenCV_jll 4.13) failed in several ways once the JLL built:

- ORB_create()/SimpleBlobDetector_create() had no zero-argument methods;
  add convenience overloads supplying OpenCV's standard defaults.

- detect() -> compute() round-trip was broken. The generic StdVector
  conversion returned Vector{KeyPointDereferenced} whose elements are views
  into detect()'s C++ vector; once that return value is freed the elements
  dangle (use-after-free), corrupting keypoint.octave and tripping ORB's
  'inv_scale_x > 0' pyramid assertion non-deterministically. It is also the
  wrong, invariant container type for compute()'s Vector{KeyPoint} parameter.
  Fix by value-copying each KeyPoint into a fresh Julia-owned object while the
  source is alive, and rebuilding StdVector{KeyPoint} (the only registered
  element type) on the way back to C++.

- Test assertions assumed a (keypoints, descriptorSize) descriptor layout and
  exact crossCheck match counts. OpenCV.jl stores cv::Mat as (channels, cols,
  rows), so the keypoint axis is dim 3; and crossCheck collapses ORB's
  duplicate descriptors, so matches <= length(kps). Correct both.

Full suite: 208/208 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ViralBShah ViralBShah merged commit 7d86cca into master Jun 5, 2026
1 of 9 checks passed
@ViralBShah ViralBShah deleted the vs/fix-enum-return-cast branch June 5, 2026 16:22
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.

1 participant