Skip to content

Public headers require C++23, forcing the same on every consumer #928

Closed

Description

@mbasmanova

CMakeLists.txt:35 sets CMAKE_CXX_STANDARD 23, which is a fine choice for the
library's own sources. The requirement is not confined to them, though.
src/iceberg/result.h includes <expected> and <format>, and defines:

template <typename T, typename E = typename DefaultError<T>::type>
using Result = std::expected<T, E>;

using Status = Result<void>;

Result<T> is the return type of most public entry points —
Catalog::ListNamespaces, ListTables, LoadTable, CreateTable,
StageCreateTable and so on. Every consumer translation unit that calls them
must therefore compile as C++23 as well. std::expected needs libstdc++ 12 or
libc++ 16 and <format> needs libstdc++ 13, so a consumer on an older but still
widely deployed toolchain cannot include the headers at all.

This matters for the engines the library is meant to be embedded in: Velox
builds as C++20, Arrow as C++17, DuckDB as C++11. Integrating leaves two
options — move the whole engine to C++23, or add an isolation layer whose only
purpose is keeping iceberg-cpp headers out of the rest of the build. We are
looking at iceberg-cpp for an Iceberg connector in
Axiom, which builds on Velox at
C++20, and would rather do neither.

Would you consider making the public error type portable while keeping the API
shape unchanged?

#if defined(__cpp_lib_expected)
template <typename T, typename E = typename DefaultError<T>::type>
using Result = std::expected<T, E>;
#else
// vendored fallback with the same interface
#endif

arrow::Result, absl::StatusOr and tl::expected all exist for this reason.
The library's own sources could keep building as C++23; only the public headers
would need to hold a lower baseline.

Happy to send a patch if the direction is agreeable.

cc @PingLiuPing, who is proposing the Iceberg connector for Axiom.

Activity

  1. wgtmac commented on Sep 9, 2026

    @wgtmac
    Member

    IMHO, it would be more than welcome if such changes can broaden the adoption of iceberg-cpp. My concern is that fixing only <expected> and <format> is not enough. We have ported #40 but it got removed once we bump to C++23. Do you want to explore a PoC to see if it is possible? @mbasmanova @PingLiuPing

    cc @zhjwpku @manuzhang @HuaHuaY

  2. zhjwpku commented on Sep 9, 2026

    @zhjwpku
    Collaborator

    IMHO, it would be more than welcome if such changes can broaden the adoption of iceberg-cpp. My concern is that fixing only <expected> and <format> is not enough. We have ported #40 but it got removed once we bump to C++23. Do you want to explore a PoC to see if it is possible? @mbasmanova @PingLiuPing

    cc @zhjwpku @manuzhang @HuaHuaY

    I'm open to downgrading to C++20 if that would make it easier for other engines to adopt iceberg-cpp, especially at this early stage ( adoption is still limited :-)).

  3. HuaHuaY commented on Sep 9, 2026

    @HuaHuaY
    Contributor

    I am fine with wrapping an additional interface that compiles with an older C++ standard, but I do not want to downgrade the project's C++ version.

    #if defined(__cpp_lib_expected)
    template <typename T, typename E = typename DefaultError::type>
    using Result = std::expected<T, E>;
    #else
    // vendored fallback with the same interface
    #endif

    I am also fine with this approach if we can centrally manage these definitions.

  4. zhjwpku commented on Sep 10, 2026

    @zhjwpku
    Collaborator

    I am fine with wrapping an additional interface that compiles with an older C++ standard, but I do not want to downgrade the project's C++ version.

    That could be another option. We could expose a C API, like RocksDB does, to make it easier for more projects to adopt iceberg-cpp.

  5. PingLiuPing commented on Sep 14, 2026

    @PingLiuPing
    Contributor

    Thanks @mbasmanova for filing this issue.
    Result and Status in result.h are exactly where the C++23 requirement reaches consumers.
    And thanks @wgtmac, @zhjwpku and @HuaHuaY for the quick and thoughtful replies.

    To be explicit about what we are asking for: we only want the public headers to be portable. We are not proposing to lower the project's own C++ standard. iceberg-cpp would keep building as C++23; the .cc files and the *_internal.h headers keep using C++23. Only the headers a consumer actually include would hold a lower standard. This matches what @HuaHuaY asked for, a wrapper rather than a downgrade.

    Two options in this thread

    1. Portable C++ headers at a C++20 baseline — @mbasmanova's proposal: a vendored expected with the same interface behind Result and Status, with the library itself staying on C++23. As @wgtmac pointed out, <expected> and <format> alone are not enough for a consumer to actually include the headers, so this also means removing other C++23 features from them. The result is a C++20 baseline for the public headers.
    2. A C API, as @zhjwpku suggested, in the style of RocksDB.

    The two are not exclusive: a C API would also be the only thing that reaches the C++17 and C++11 consumers. Compiling the public headers at C++17 gives
    32 of 205 passing, and the blockers are:

    feature public headers using it
    std::span in signatures 31
    requires clauses 15
    std::format / <format> 11
    concept definitions 8
    std::remove_cvref_t 6
    operator<=> 2
    consteval 1

    Reaching C++17 would mean more work.

    I have a branch that go with option 1, and the complexity is modest: 48 files, +264 / −136 lines excluding the restored vendored file and its test:

    • Restore src/iceberg/expected.h from backport c++23 std::expected #40 (removed by chore: bump C++ standard to 23 #139) and alias Result
      to it unconditionally. The library's own errors become iceberg::unexpected.
    • Replace std::unreachable in 11 public headers, std::ranges::to in 2 and
      std::byteswap in 1.
    • Drop deducing this from TaskGroup (ref-qualified overloads) and from
      ErrorCollector::AddError (chaining on *this, which is already the derived
      type).
    • Build example/ as C++20 in CI, so a public header cannot silently pick up a
      C++23 feature again — it is already built on Linux, macOS and Windows against
      the installed package, which is exactly a consumer's view.

    Verified locally: all 205 public headers compile standalone as C++20, the library
    still builds as C++23, and the test suites pass (1878 tests across util_test,
    schema_test, table_test, expression_test, json_serde_test), including the
    26 tests in the restored expected_test.cc.

    Let me know if this is the right direction you want to go? Thanks.

  6. HuaHuaY commented on Sep 14, 2026

    @HuaHuaY
    Contributor

    Hi @PingLiuPing. Thanks for your work. It sounds like you've already finished most of the work. Can you open a PR or a draft PR? I think this will be helpful for a better review of the design proposal.

  7. PingLiuPing commented on Sep 17, 2026

    @PingLiuPing
    Contributor

    Hi @PingLiuPing. Thanks for your work. It sounds like you've already finished most of the work. Can you open a PR or a draft PR? I think this will be helpful for a better review of the design proposal.

    @HuaHuaY Thanks, submitted PR #936.

  8. added a commit that references this issue on Oct 9, 2026
    dab8664
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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