Skip to content

Add JSON_DELETE_DEPRECATED_FUNCTIONS to delete the deprecated functions - #5755

Merged
nlohmann merged 4 commits into
claude/from-bon8-bjdata-ptr-len-5648from
claude/delete-deprecated-functions
Oct 4, 2026
Merged

nlohmann merged 4 commits into
claude/from-bon8-bjdata-ptr-len-5648from
claude/delete-deprecated-functions

Conversation

@nlohmann

@nlohmann nlohmann commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Adds an opt-in macro JSON_DELETE_DEPRECATED_FUNCTIONS, with the CMake option JSON_DeleteDeprecatedFunctions. When it is defined to 1, every deprecated function is declared = delete instead of JSON_HEDLEY_DEPRECATED_FOR. Code that is not ready for 4.0.0 then stops compiling, even if deprecation warnings are switched off. This follows the "try 4.0 today" approach from the roadmap (#5593).

This came out of @gregmarr's review of #5688 (comment). A deleted function, unlike a removed one, still takes part in overload resolution. So with the macro, from_cbor(ptr, len) is a hard error ("call to deleted function"). With the overload removed, the call would still compile: it would bind len to bool strict and read ptr as a NUL-terminated string. For the same reason, the roadmap now plans to keep the from_*(ptr, len) overloads deleted in 4.0.0 instead of removing them.

Changes

  • Library: the 30 deprecated functions in json.hpp and json_pointer.hpp each get an #if JSON_DELETE_DEPRECATED_FUNCTIONS branch that declares them deleted. The parameter list and default arguments stay the same, so the deleted overload is still selected wherever the deprecated one would have been. This covers:

    • from_cbor, from_msgpack, from_ubjson, from_bjdata, from_bon8, from_bson with (ptr, len), and the first four plus from_bson with an initializer list
    • parse, accept, sax_parse with an initializer list
    • iterator_wrapper
    • j << is and j >> os
    • json_pointer::operator string_t
    • comparing a json_pointer with a string
    • value, contains, operator[], at taking a json_pointer<basic_json>

    The legacy discarded-value comparison keeps its own macro (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON). The new macro gets no ABI tag: it changes no layout and no behavior, it only turns calls that compile into calls that don't (the same as JSON_USE_IMPLICIT_CONVERSIONS).

  • Tests:

    • New unit-delete_deprecated_functions.cpp defines the macro itself. Using SFINAE traits, it checks that each of the 32 deprecated calls is ill-formed and that the replacements still compile. I checked that all 32 checks flip when the macro is 0, so they are not trivially false.
    • The tests that call deprecated functions capture the macro before including json.hpp, the same way as for JSON_STRICT_NUL_HANDLING, and skip those calls when it is set.
    • Two tests now use the replacement instead of the deprecated function: unit-regression2's [json.exception.type_error.317] cannot serialize binary data to text JSON #2067 test and the "preserve state after parsing" stream test.
  • CI: new target ci_test_delete_deprecated_functions in ci_cmake_options. The CMake option is also added to the ci_cmake_flags checks.

  • Docs:

    • New macro page, plus entries in the macro index, the macro overview, the CMake page, the nav, and the docset.
    • Roadmap ("Version 4.0"):
    • Migration guide ("Replace deprecated functions"): a tip pointing to the macro, and the from_bjdata/from_bon8 overloads.
    • JSON_DISABLE_TUPLE_REFERENCE_CONVERSION's version history now says it is planned to become the default in 4.0.0.
    • Fixes findings of check_structure.py on develop: example titles on the JSON_STRICT_BINARY_UTF8 and JSON_USE_IMPLICIT_CONVERSIONS pages, and the missing docset entry for JSON_STRICT_BINARY_UTF8.

Stacked on #5688, because it also deletes the new from_bjdata/from_bon8 overloads.

Testing

  • Local full suite (Apple clang, Debug) with -DJSON_DeleteDeprecatedFunctions=ON: all tests pass except test-alt-string, which fails to build on develop itself (fixed in Fix CI on develop after merging the ready-to-merge PRs #5754).
  • The changed test files also pass without the macro.
  • unit-delete_deprecated_functions passes at C++11, 14, 17, and 20.
  • mkdocs build --strict and check_structure.py pass.

Public API

No breaking changes. The macro defaults to 0, which keeps everything as it is. With 1, calls to deprecated functions fail to compile, which is the purpose of the opt-in.


This PR was written by Claude Code.

🤖 Generated with Claude Code

Defining JSON_DELETE_DEPRECATED_FUNCTIONS to 1 (or the CMake option
JSON_DeleteDeprecatedFunctions) declares every deprecated function as
deleted instead of deprecated, so that code that is not ready for 4.0.0
no longer compiles. A deleted function still takes part in overload
resolution, so from_*(ptr, len) cannot silently bind len to the strict
parameter of from_*(InputType&&, bool); the roadmap now plans to keep
these overloads deleted in 4.0.0 instead of removing them.

The legacy discarded-value comparison is left to its own macro.

Also update the 4.0 roadmap: add JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
and JSON_DELETE_DEPRECATED_FUNCTIONS to the macro table, add the
from_bjdata/from_bon8 (ptr, len) overloads to the deprecated functions,
document the macro in the migration guide, and fix the docs style check
findings (example titles, missing docset entry for JSON_STRICT_BINARY_UTF8).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann added this pull request to stack #5756 October 4, 2026 10:54
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

🔴 Amalgamation check failed! 🔴

The source code has not been amalgamated and/or formatted correctly, or BUILD.bazel is out of date.

📎 A ready-to-apply patch is attached to the failed workflow run as the amalgamation-patch artifact. Download it, then apply it locally from the repository root with:

git apply amalgamation.patch

This does not require installing astyle yourself.

…5648' into HEAD

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string_t/
#if JSON_DELETE_DEPRECATED_FUNCTIONS
operator string_t() const = delete;
#else

@gregmarr gregmarr Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if this can avoid duplicating the declaration:

#if JSON_DELETE_DEPRECATED_FUNCTIONS
#define JSON_DELETE_DEPRECATED_FUNCTIONS_DEPRECATED(ver, func) 
#define JSON_DELETE_DEPRECATED_FUNCTIONS_DELETE = delete
#else
#define JSON_DELETE_DEPRECATED_FUNCTIONS_DEPRECATED(ver, func) JSON_HEDLEY_DEPRECATED_FOR(ver, func)
#define JSON_DELETE_DEPRECATED_FUNCTIONS_DELETE
#endif


    JSON_DELETE_DEPRECATED_FUNCTIONS_DEPRECATED(3.11.0, to_string())
    operator string_t() const
    JSON_DELETE_DEPRECATED_FUNCTIONS_DELETE()
    {
        return to_string();
    }

Still has the body, which would need guarding as well, but I have to run now.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thanks, good point. I went with a slightly simpler variant in fe5bc3d: no new macros, and each declaration (with its deprecation attribute) appears only once. Only the end of the function is switched:

JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
operator string_t() const
#if JSON_DELETE_DEPRECATED_FUNCTIONS
    = delete;
#else
{
    return to_string();
}
#endif

Passing the body as a macro argument would have broken bodies with // NOLINTNEXTLINE comments (e.g., sax_parse), and keeping the deprecation attribute on a deleted function is allowed. The unit-delete_deprecated_functions test passes with the macro set to 0 and 1 (clang, GCC 15).

(Written by Claude Code.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wasn't sure if you'd want to keep the JSON_HEDLEY_DEPRECATED_FOR around for things beyond 4.0.0. Otherwise I'm fine with this.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I thought about this as well, and kicked the can down the road. Let's see how much more will add up and decide then.

Instead of repeating every deprecated declaration in an
#if JSON_DELETE_DEPRECATED_FUNCTIONS branch, keep one declaration
(with its deprecation attribute) and switch only between "= delete;"
and the function body. Suggested by @gregmarr in the review.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann nlohmann added this to the Release 3.13.0 milestone Oct 4, 2026
@nlohmann nlohmann added the 🚀 ready to merge Ready to merge - just waiting for CI to complete. label Oct 4, 2026
…e-deprecated-functions

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann merged commit 23d3b37 into develop Oct 4, 2026
3 of 322 checks passed
@nlohmann
nlohmann deleted the claude/delete-deprecated-functions branch October 4, 2026 20:11
nlohmann added a commit that referenced this pull request Oct 5, 2026
MSVC 2015 does not treat selecting a deleted function in decltype as a
substitution failure, so the detection traits in
unit-delete_deprecated_functions (#5755) and the integral-key checks in
unit-element_access2 (#5657) report deleted overloads as callable there.
Calling them still fails to compile. Skip those checks for
_MSC_VER < 1910, and use the stream operators for real in the runtime
section, so MSVC 2015 still compiles them with the macro set.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 6, 2026
* Fix CI on develop after #5600, #5607, and #5755

- binary_reader: cast the result of -1 - number back to number_integer_t,
  because a number_integer_t narrower than int is promoted to int, which
  GCC's -Warith-conversion rejects (ci_test_gcc, ci_test_standards_gcc)
- JSON_DELETE_DEPRECATED_FUNCTIONS: declare the deleted stream operators
  as function templates at namespace scope, because GCC < 5 rejects deleted
  friend functions ("can't initialize friend function") and Clang 7-9 report
  a redefinition when a class template has a deleted friend function
- docs: give the examples of JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS "Example:"
  titles and add the page to the docset (style_check)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Ignore libstdc++'s <format> sign change in the sanitizer job

libstdc++ 14's <format> initializes a size_t parameter with -1 (GCC bug
119429), so every std::format call fails ci_test_clang_sanitizer under
-fsanitize=integer (test-std-format_cpp20). Exclude only the
implicit-integer-sign-change check and only that header via
-fsanitize-ignorelist.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the AppVeyor (MSVC 2015-2019) build

- binary_reader: emit_signed/emit_unsigned pass integers that do not fit
  the number types to emit_float as long double, because MSVC's <cmath>
  has no integer overloads of std::isfinite (C2668 'fpclassify'), from #5607
- scalar comparisons: the friend operators take the JSON type for their
  noexcept from their parameter, because MSVC 2015/2017 take basic_json as
  the class template there (C3203) and MSVC 2019 16.0 does not see member
  types or template parameters, from #5751
- unit-conversions2: skip the !is_nothrow_constructible static_assert for
  std::optional on MSVC 2017, which evaluates the conditional noexcept as
  true (C2607), from #5754

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Re-amalgamate

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Avoid MSVC 2015's C4800 for enums with underlying type bool

MSVC 2015 warns about any conversion to bool (C4800), even with an
explicit cast, so the enum conversions from #5754 (#5671) failed the
AppVeyor build with /WX. Convert to bool by comparing with zero via the
new detail::bool_aware_static_cast, and keep doctest from printing the
enum in the test.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Skip the #5650 allocator test on MSVC 2015 debug builds

MSVC 2015's debug STL constructs the containers' debug proxies through
the allocator in noexcept constructors, so countdown_allocator's failing
construction crashes test-allocator (SIGSEGV) instead of throwing
std::bad_alloc. Use the guard #5585 uses for the same reason.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Skip deleted-function detection checks on MSVC 2015

MSVC 2015 does not treat selecting a deleted function in decltype as a
substitution failure, so the detection traits in
unit-delete_deprecated_functions (#5755) and the integral-key checks in
unit-element_access2 (#5657) report deleted overloads as callable there.
Calling them still fails to compile. Skip those checks for
_MSC_VER < 1910, and use the stream operators for real in the runtime
section, so MSVC 2015 still compiles them with the macro set.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Split the Visual Studio 2017 AppVeyor jobs in two

The VS 2017 jobs hit AppVeyor's 60-minute limit per job while still
compiling the tests (77 of about 108 test targets after 58 minutes).
They pass /std:c++17 for everything anyway, so build only the C++17
variant of each test (JSON_TestStandards=17), and split the unit test
files across two jobs each with the new JSON_TestShard=<index>/<count>
option, which keeps every <count>-th test file starting at <index>.
The extra variants of single test files are built in shard 0 only.

CMAKE_OPTIONS is no longer quoted in appveyor.yml, so that it can hold
more than one option.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix std::terminate when converting std::optional with MSVC 2017

MSVC 2017 evaluates std::is_nothrow_assignable<json&, const T&> as true
even if T's to_json throws, so to_json(json&, const std::optional<T>&)
was noexcept there and the exception from #5642's test called
std::terminate instead of propagating. Make that conversion never
noexcept on MSVC 2017; all other compilers keep the exact condition.
The static_asserts on the condition are skipped for MSVC 2017; the
runtime check that the exception propagates still runs there.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 7, 2026
JSON_StrictBinaryUTF8 (#5741) and JSON_DeleteDeprecatedFunctions (#5755)
arrived with develop but were missing from the Meson build and the
pkg-config file, so check_build_options failed. Both Meson options
default to false and add JSON_STRICT_BINARY_UTF8=1 and
JSON_DELETE_DEPRECATED_FUNCTIONS=1 to the dependency, the pkg-config
file, and the generated CMake target; CMake's pkg-config file now
carries both definitions too. The ci_meson_install job sets and checks
them in its non-default install.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 7, 2026
JSON_StrictBinaryUTF8 (#5741) and JSON_DeleteDeprecatedFunctions (#5755)
arrived with develop but were missing from the Meson build and the
pkg-config file, so check_build_options failed. Both Meson options
default to false and add JSON_STRICT_BINARY_UTF8=1 and
JSON_DELETE_DEPRECATED_FUNCTIONS=1 to the dependency, the pkg-config
file, and the generated CMake target; CMake's pkg-config file now
carries both definitions too. The ci_meson_install job sets and checks
them in its non-default install.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann added a commit that referenced this pull request Oct 7, 2026
…5587)

* Install CMake package config files with Meson, and add Meson options

Squashed onto develop from:
- Install CMake package config files with Meson
- meson: Indent code inside an if block
- meson: set a minimum Meson version
- meson: use `override_dependency()` to set dependencies
- meson: use `install_subdir` for headers
- meson: set the C++ standard to C++11
- meson: handle single header and multiheader the same way CMake does
- meson: add support for the GlobalUDLs option
- meson: add support for the ImplictConversions option
- Add meson information to FILES.md
- Complete the Meson options and match CMake's compile definitions
- Add the JSON_* definitions to the CMake pkg-config file
- Document the Meson options and check them in CI
- Fix the Meson CMake target for includedir or datadir outside the prefix
- Avoid //include in Meson-generated CMake target for prefix /
- Add DisableTupleReferenceConversion to Meson and the CMake pkg-config file
- Check in CI that Meson and pkg-config offer the CMake options
- Remove accidentally committed Python bytecode

Co-authored-by: Dylan Baker <dylan@pnwbakers.com>
Signed-off-by: Dylan Baker <dylan@pnwbakers.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Add the StrictBinaryUTF8 and DeleteDeprecatedFunctions Meson options

JSON_StrictBinaryUTF8 (#5741) and JSON_DeleteDeprecatedFunctions (#5755)
arrived with develop but were missing from the Meson build and the
pkg-config file, so check_build_options failed. Both Meson options
default to false and add JSON_STRICT_BINARY_UTF8=1 and
JSON_DELETE_DEPRECATED_FUNCTIONS=1 to the dependency, the pkg-config
file, and the generated CMake target; CMake's pkg-config file now
carries both definitions too. The ci_meson_install job sets and checks
them in its non-default install.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Dylan Baker <dylan@pnwbakers.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Dylan Baker <dylan@pnwbakers.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aspect: binary formats BSON, CBOR, MessagePack, UBJSON CI CMake documentation L 🚀 ready to merge Ready to merge - just waiting for CI to complete. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants