Skip to content

feat(pj_base): centralize string→number parsing via fast_float private dep#101

Merged
facontidavide merged 2 commits into
mainfrom
feat/parse-number-fast-float
May 28, 2026
Merged

feat(pj_base): centralize string→number parsing via fast_float private dep#101
facontidavide merged 2 commits into
mainfrom
feat/parse-number-fast-float

Conversation

@facontidavide
Copy link
Copy Markdown
Contributor

Summary

  • Replace the floating-point branch of PJ::parseNumber<T> with a fast_float-backed implementation in a new pj_base/src/number_parse.cpp. fast_float is added as a private Conan dep (visible=False, transitive_*=False, mirrors the existing fmt pattern) and linked under $<BUILD_INTERFACE:FastFloat::fast_float> so it never appears in installed headers or the exported plotjuggler_coreTargets*.cmake. Integer branch keeps std::from_chars in the header.
  • Locale-independent parsing — fixes a latent correctness bug where std::strto* respected LC_NUMERIC and silently parsed "1.5" as 1 under a de_DE locale. Also drops the per-call std::string allocation that the previous null-terminated strto* path needed.
  • Public template surface unchanged. Patch bump 0.4.1 → 0.4.2.

Three in-tree call-sites migrated alongside the refactor: three std::stoi in widget_data_view.hpp, three std::strtod in the mock parsers (mock_json_parser.cpp + mock_schema_parser.cpp), and std::atoi in the parquet_import example (changed to error+exit on invalid input — the previous garbage→0 was a latent bug).

Test plan

  • RelWithDebInfo build clean with -Wall -Wextra -Werror
  • All 62 tests pass (ctest), including the new number_parse_test with 6 cases covering int success/failure, float success/failure, de_DE locale regression guard, and long double strtold fallback
  • Privacy verified end-to-end: cmake --install to a temp prefix, then grep — no fast_float #include in the installed include tree, no FastFloat reference in lib/cmake/plotjuggler_core/
  • Debug + ASAN build (./build.sh --debug) — pre-existing environment issue with ncurses/gcc-15 (transitive of cpython/arrow) blocked the local Debug run; CI should validate
  • LC_ALL=de_DE.UTF-8 end-to-end smoke test — locale isn't installed on the dev host, but the test attempts to set it and warns on skip; CI host with the locale installed will exercise it

Follow-ups

  • The pj_ported_plugins repo gets its own PR once that branch is unblocked (the local feat/parser-ros-frametransforms it stacks on has diverged from origin).
  • The upstream pj-official-plugins repo has 15 cleanly-replaceable call sites across 7 plugins; survey roadmap is ready and will land as per-plugin PRs once plotjuggler_core/0.4.2 is on the Cloudsmith remote.

🤖 Generated with Claude Code

facontidavide and others added 2 commits May 28, 2026 16:09
…e dep

Replace the floating-point branch of PJ::parseNumber<T> with a fast_float
implementation, kept in a new .cpp so the dependency is private and never
visible in installed headers or exported CMake targets. Integer branch
continues to use std::from_chars in the header.

Why fast_float matters:
- Locale-independent. std::strto* respects LC_NUMERIC, so a user under a
  de_DE locale silently parsed "1.5" as 1. This was a latent correctness
  bug affecting any English-formatted telemetry consumed under non-C
  locales.
- Zero allocation. No copy of the string_view to a null-terminated buffer.
- Drops the Apple Clang libc++ <charconv> floating-point workaround that
  the previous implementation needed.

Privacy guarantee verified:
- fast_float is declared in conanfile.py with visible=False and
  transitive_*=False (mirrors the existing fmt pattern).
- pj_base links FastFloat::fast_float under $<BUILD_INTERFACE:...> so it
  is stripped from the installed plotjuggler_coreTargets file.
- An install-tree grep confirms no fast_float #include and no FastFloat
  reference reaches downstream consumers.

Tests:
- New pj_base/tests/number_parse_test.cpp covering int success/failure,
  float success/failure, a de_DE-locale regression guard, and the
  long double strtold fallback path. 62/62 tests pass.

In-tree migrations (13 sites):
- pj_plugins/dialog_protocol/widget_data_view.hpp — three std::stoi
  parses of row/col indices.
- pj_plugins/examples/mock_{json,schema}_parser.cpp — three std::strtod
  calls. The new code returns a proper parse error instead of silently
  producing 0 on garbage input.
- pj_datastore/examples/parquet_import.cpp — std::atoi for chunk_rows;
  now prints an error and exits 1 on invalid input (the previous
  garbage→0 behavior was a latent bug).

Version bump:
- 0.4.1 → 0.4.2 (PATCH: public template signature unchanged).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@pabloinigoblasco pabloinigoblasco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unifying style: good!
Code looks correct.

I approve.

@facontidavide facontidavide merged commit b85757d into main May 28, 2026
4 checks passed
@facontidavide facontidavide deleted the feat/parse-number-fast-float branch May 28, 2026 15:12
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.

2 participants